From d3558460d5a34f2f2e5bc24aeaf425ec0882fbe2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Thu, 6 Aug 2026 11:22:17 -0700 Subject: [PATCH] fix(ui): show Pi logo in session catalog (#119998) Show the official Pi provider mark beside Pi external-session catalog headings in the Control UI sidebar. Keep OpenCode sibling branding covered and preserve transcript navigation and paired-node view-only behavior. Proof: focused sidebar tests, browser-driven external-catalog E2E, production Control UI build/performance checks, UI typecheck, formatting/style checks, source-blind behavior validation, and clean Codex autoreview. GitHub Actions was in a confirmed service outage; maintainer explicitly directed landing without Actions. The repository full local gate reached unrelated failures across Codex, launchd, CLI, plugin naming, QA, and tooling suites after build and check passed. --- ui/public/provider-icons/ATTRIBUTION.md | 7 +++++++ ui/public/provider-icons/ProviderIcon-pi.svg | 4 ++++ ui/src/components/provider-icon.ts | 1 + .../e2e/external-session-catalogs.e2e.test.ts | 17 +++++++++++++++++ ui/src/styles/components.css | 3 ++- .../app-sidebar-cases/catalog-live.ts | 2 ++ 6 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 ui/public/provider-icons/ProviderIcon-pi.svg 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"]));