diff --git a/surfaces/gui/e2e/gallery.spec.ts b/surfaces/gui/e2e/gallery.spec.ts index cf9d91c9..5aa46fbe 100644 --- a/surfaces/gui/e2e/gallery.spec.ts +++ b/surfaces/gui/e2e/gallery.spec.ts @@ -1,7 +1,6 @@ -// Settings ▸ Personas ▸ Gallery: the catalog lives in a screen-sized modal opened -// from the Personas page (link → featured carousel + list → in-modal solo page → -// informed install → Done lands back on Personas). Plus the page-level delete -// affordance for non-builtin personas. +// The Gallery entry point was removed from Settings ▸ Coworkers (owner 2026-08-21) — +// coworkers install from GitHub / folder / zip. This file keeps the page-level +// delete flow (now on the coworker detail page, UX-035). import { expect } from "@playwright/test"; import { test } from "./fixtures"; @@ -10,91 +9,12 @@ async function openPersonas(page) { await page.getByTestId("account-row").click(); await page.getByRole("button", { name: "Settings", exact: true }).click(); await page.getByRole("button", { name: "Coworkers", exact: true }).click(); - await expect(page.getByTestId("gallery-link")).toBeVisible(); } -async function openGallery(page) { +test("the Gallery entry point is gone from the Coworkers page", async ({ page }) => { await openPersonas(page); - await page.getByTestId("gallery-link").click(); - await expect(page.getByTestId("gallery-modal")).toBeVisible(); -} - -test("slow cloud: skeleton shows while the gallery loads, never a blank body", async ({ - page, -}) => { - // The real gallery is a cloud round-trip (Lambda + Dynamo) that can take seconds; - // delay the mocked endpoints to assert the skeleton bridges the gap. - await page.route("**/v1/cloud/status", async (route) => { - await new Promise((r) => setTimeout(r, 1200)); - await route.fulfill({ json: { ok: true, signed_in: false } }); - }); - await openGallery(page); - await expect(page.getByTestId("gallery-loading")).toBeVisible(); - await expect(page.getByTestId("gallery-loading")).toContainText("Loading the gallery"); - // Resolves into the real body (signed-out prompt here) once the cloud answers. - await expect(page.getByTestId("gallery-signin")).toBeVisible({ timeout: 10_000 }); - await expect(page.getByTestId("gallery-loading")).toHaveCount(0); -}); - -test("signed out: modal prompts for sign-in, manual install path unaffected", async ({ page }) => { - await openGallery(page); - const prompt = page.getByTestId("gallery-signin"); - await expect(prompt).toContainText("needs a (free) cloud sign-in"); - await expect(prompt).toContainText("always works without an account"); - await expect(prompt.getByRole("button", { name: "Sign in" })).toBeVisible(); - // Esc closes; the Personas page (with its dir/Git importer) is still there. - await page.keyboard.press("Escape"); - await expect(page.getByTestId("gallery-modal")).not.toBeVisible(); - // The manual installer (collapsed disclosure, UX-035) is still there. - await page.getByTestId("install-disclosure").click(); - await expect(page.getByRole("button", { name: "Install", exact: true })).toBeVisible(); -}); - -test("signed in: featured carousel + list; solo page installs informed; Done returns", async ({ - page, -}) => { - await openGallery(page); - await page.getByTestId("gallery-signin").getByRole("button", { name: "Sign in" }).click(); - - // Featured carousel holds the flagged persona; the list holds both. - const featured = page.getByTestId("gallery-featured"); - await expect(featured).toBeVisible({ timeout: 10_000 }); - await expect(featured).toContainText("Sales Coworker"); - await expect(featured).not.toContainText("Recruiter"); - await expect(page.getByTestId("gallery-recruiter")).toContainText("View & install"); - await expect(page.getByTestId("gallery-team-teaser")).toContainText("coming soon"); - - // Search narrows the list. - await page.getByPlaceholder("Search coworkers").fill("recruit"); - await expect(page.getByTestId("gallery-sales")).not.toBeVisible(); - await page.getByPlaceholder("Search coworkers").fill(""); - - // Solo page: pitch + manifest-derived capabilities BEFORE install. - await page.getByTestId("gallery-sales").click(); - const detail = page.getByTestId("gallery-detail"); - await expect(detail).toContainText("Walk into every call already knowing the account"); - const caps = page.getByTestId("gallery-capabilities"); - await expect(caps).toContainText("verified from its manifest"); - await expect(caps).toContainText("files, search, todo"); - await expect(caps).toContainText("hubspot · core"); - await expect(caps).toContainText("read deals and contacts"); - - await detail.getByRole("button", { name: "Install" }).click(); - await expect(detail).toContainText("disabled until you approve and enable it"); - - // Done closes the modal, landing back on the Personas page. - await detail.getByRole("button", { name: "Done" }).click(); - await expect(page.getByTestId("gallery-modal")).not.toBeVisible(); - await expect(page.getByTestId("gallery-link")).toBeVisible(); -}); - -test("back link returns from the solo page to the catalog", async ({ page }) => { - await openGallery(page); - await page.getByTestId("gallery-signin").getByRole("button", { name: "Sign in" }).click(); - await page.getByTestId("gallery-sales").click({ timeout: 10_000 }); - await expect(page.getByTestId("gallery-detail")).toBeVisible(); - await page.getByRole("button", { name: "← Gallery" }).click(); - await expect(page.getByTestId("gallery-cards")).toBeVisible(); + await expect(page.getByTestId("install-disclosure")).toBeVisible(); + await expect(page.getByTestId("gallery-link")).toHaveCount(0); }); test("delete: non-builtin personas removable after confirm; built-ins are not", async ({ diff --git a/surfaces/gui/src/App.tsx b/surfaces/gui/src/App.tsx index 43fe271d..06e84090 100644 --- a/surfaces/gui/src/App.tsx +++ b/surfaces/gui/src/App.tsx @@ -2084,7 +2084,13 @@ export function App() { setBoardDetailId(id); setBoardOpen(true); }} - isLead={teamMembers.length > 0 || (!!curSession?.team && curSession.team.role !== "worker")} + /* team serializes as {} for plain sessions — lead-ness needs an actual + role, else every solo session loses its Progress panel (owner-hit + 2026-08-21: the rail showed nothing but "More"). */ + isLead={ + teamMembers.length > 0 || + (curSession?.team?.role != null && curSession.team.role !== "worker") + } teamMembers={teamMembers} teamChatEnabled={!!curSession?.team?.chat_enabled} teamChatUnread={curSession?.team?.chat_unread || 0} diff --git a/surfaces/gui/src/components/PersonasTab.tsx b/surfaces/gui/src/components/PersonasTab.tsx index 608b364b..dd03af99 100644 --- a/surfaces/gui/src/components/PersonasTab.tsx +++ b/surfaces/gui/src/components/PersonasTab.tsx @@ -27,14 +27,7 @@ const BTN_BORDERED = const QUIET_ROW = "w-full flex items-center gap-2 px-4 pt-2 mt-6 text-[12.5px] text-muted select-none"; -export function PersonasTab({ - onOpenPersona, - onMeta, -}: { - onOpenPersona?: (id: string) => void; - // Lets the section gate internal-build affordances (the Gallery entry point). - onMeta?: (meta: { internal: boolean }) => void; -}) { +export function PersonasTab({ onOpenPersona }: { onOpenPersona?: (id: string) => void }) { const [personas, setPersonas] = useState([]); const [internal, setInternal] = useState(false); const [mode, setMode] = useState<"git" | "dir" | "zip">("git"); @@ -68,7 +61,6 @@ export function PersonasTab({ .then((r) => { setPersonas(r.personas); setInternal(r.internal); - onMeta?.({ internal: r.internal }); }) .catch(() => {}); const reloadSessions = () => getSessions().then(setSessions).catch(() => {}); diff --git a/surfaces/gui/src/components/SettingsView.tsx b/surfaces/gui/src/components/SettingsView.tsx index 96ba748d..a9f525a6 100644 --- a/surfaces/gui/src/components/SettingsView.tsx +++ b/surfaces/gui/src/components/SettingsView.tsx @@ -40,7 +40,6 @@ import { Icon } from "./Icon"; import { PanelHead } from "./IntegrationsView"; import { ModelsTab } from "./ManageTabs"; import { MemorySection } from "./MemorySection"; -import { GalleryModal } from "./GalleryModal"; import { PersonasTab } from "./PersonasTab"; import { SkillsTab } from "./SkillsTab"; import { showPersonas } from "../flags"; @@ -375,13 +374,10 @@ function VoiceInputSection() { // -- Personas: installed/enabled/delete management, the dir/Git importer, and the // entry point to the Persona Gallery (a screen-sized modal — installs finish back // here, disabled pending consent; a gallery install re-mounts the list in place). +// The Gallery entry point is GONE (owner 2026-08-21) — coworkers install from +// GitHub / folder / zip only. GalleryModal stays in the tree for the gallery's +// possible return as a first-class distribution surface, but nothing mounts it. function PersonasSection({ onOpenPersona }: { onOpenPersona?: (id: string) => void }) { - const [galleryBump, setGalleryBump] = useState(0); - const [galleryOpen, setGalleryOpen] = useState(false); - // The Gallery is OUR distribution channel during development — internal builds only - // (owner 2026-08-21). Users install from GitHub / folder / zip. - const [internal, setInternal] = useState(false); - return (
@@ -390,33 +386,7 @@ function PersonasSection({ onOpenPersona }: { onOpenPersona?: (id: string) => vo with the tools and skills to be successful in that role. Enabling a coworker lets you pick it when starting a conversation.

- setInternal(m.internal)} - /> - {internal && ( - - )} - {galleryOpen && ( - setGalleryOpen(false)} - onInstalled={() => setGalleryBump((b) => b + 1)} - /> - )} +
); }