diff --git a/ui/public/provider-icons/ATTRIBUTION.md b/ui/public/provider-icons/ATTRIBUTION.md index 77c681cad533..dd30b166c2b7 100644 --- a/ui/public/provider-icons/ATTRIBUTION.md +++ b/ui/public/provider-icons/ATTRIBUTION.md @@ -17,6 +17,13 @@ content otherwise match the cited upstream files. Provider names and marks remain the property of their respective owners. +## Pi icon + +`ProviderIcon-pi.svg` is a metadata-cleaned copy of the official Pi logo: + +- Source: https://pi.dev/logo.svg +- Press kit: https://pi.dev/press-kit + ## LM Studio icon `ProviderIcon-lmstudio.svg` is the official monochrome outline icon published diff --git a/ui/public/provider-icons/ProviderIcon-pi.svg b/ui/public/provider-icons/ProviderIcon-pi.svg new file mode 100644 index 000000000000..c48d2e86a4ee --- /dev/null +++ b/ui/public/provider-icons/ProviderIcon-pi.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/src/components/provider-icon.ts b/ui/src/components/provider-icon.ts index 61cd76486458..0f176f571108 100644 --- a/ui/src/components/provider-icon.ts +++ b/ui/src/components/provider-icon.ts @@ -49,6 +49,7 @@ const PROVIDER_ICON_NAMES = new Set([ "opencodego", "openrouter", "perplexity", + "pi", "poe", "qoder", "sakana", diff --git a/ui/src/e2e/external-session-catalogs.e2e.test.ts b/ui/src/e2e/external-session-catalogs.e2e.test.ts index 41122cd11a29..c3ce48db5dea 100644 --- a/ui/src/e2e/external-session-catalogs.e2e.test.ts +++ b/ui/src/e2e/external-session-catalogs.e2e.test.ts @@ -116,6 +116,23 @@ suite("OpenCode and Pi external session catalogs", () => { }); await page.goto(`${server.baseUrl}chat`); + await expect + .poll(() => + page + .locator('[data-session-section="catalog:opencode"] [data-provider-icon="opencode"]') + .count(), + ) + .toBe(1); + await expect + .poll(() => + page.locator('[data-session-section="catalog:pi"] [data-provider-icon="pi"]').count(), + ) + .toBe(1); + const piIconResponse = await page.request.get( + new URL("provider-icons/ProviderIcon-pi.svg", server.baseUrl).toString(), + ); + expect(piIconResponse.ok()).toBe(true); + await page.getByText("OpenCode release review", { exact: true }).click(); await expect.poll(() => page.getByText("OpenCode transcript loaded").count()).toBe(1); await page.getByText("Pi architecture notes", { exact: true }).click(); diff --git a/ui/src/styles/components.css b/ui/src/styles/components.css index 72c71376b860..7a7101bfc54d 100644 --- a/ui/src/styles/components.css +++ b/ui/src/styles/components.css @@ -5931,7 +5931,8 @@ td.data-table-key-col { } .provider-brand-icon[data-provider-icon="opencode"], -.provider-brand-icon[data-provider-icon="opencodego"] { +.provider-brand-icon[data-provider-icon="opencodego"], +.provider-brand-icon[data-provider-icon="pi"] { color: var(--text-strong); } /* Shared working-claw motion: chat and settings use the same body flex and diff --git a/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts b/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts index de326a8eb986..e1aee4123b89 100644 --- a/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts +++ b/ui/src/test-helpers/app-sidebar-cases/catalog-live.ts @@ -166,6 +166,8 @@ describe("AppSidebar session catalog pagination", () => { it.each([ { id: "claude", label: "Claude Code" }, { id: "codex", label: "Codex" }, + { id: "opencode", label: "OpenCode" }, + { id: "pi", label: "Pi" }, ])("groups $label catalog rows by their owning host", async ({ id, label }) => { const gateway = createGateway({} as GatewayBrowserClient); const { sidebar } = await mountSidebar(gateway, createSessions("main", ["agent:main:main"]));