From 4bd5fa134994529371db1262dc7c90b4ff747836 Mon Sep 17 00:00:00 2001
From: Rhys Sullivan <39114868+RhysSullivan@users.noreply.github.com>
Date: Sat, 29 Aug 2026 22:32:50 -0700
Subject: [PATCH 1/2] Unify integration icons on one component
---
e2e/local/codex-plugins.test.ts | 24 ++++-
.../plugins/mcp/src/react/CodexPluginAdd.tsx | 10 +-
packages/plugins/mcp/src/sdk/presets.ts | 2 +-
.../react/src/components/command-palette.tsx | 15 +--
.../components/integration-favicon.test.tsx | 20 ++++
.../src/components/integration-favicon.tsx | 70 +++++++++++--
packages/react/src/components/preset-icon.tsx | 84 ----------------
.../react/src/pages/integration-browse.tsx | 97 +++++++------------
8 files changed, 151 insertions(+), 171 deletions(-)
delete mode 100644 packages/react/src/components/preset-icon.tsx
diff --git a/e2e/local/codex-plugins.test.ts b/e2e/local/codex-plugins.test.ts
index 423e9a13ed..89597666f6 100644
--- a/e2e/local/codex-plugins.test.ts
+++ b/e2e/local/codex-plugins.test.ts
@@ -28,7 +28,7 @@ import { composePluginApi } from "@executor-js/api/server";
import { mcpHttpPlugin } from "@executor-js/plugin-mcp/api";
import { scenario } from "../src/scenario";
-import { Cli, RunDir } from "../src/services";
+import { Browser, Cli, RunDir, Target } from "../src/services";
import { withLocalServer } from "./local-server";
const api = composePluginApi([mcpHttpPlugin()] as const);
@@ -109,7 +109,10 @@ scenario(
{ timeout: 300_000 },
Effect.gen(function* () {
const cli = yield* Cli;
+ const browser = yield* Browser;
const runDir = yield* RunDir;
+ const target = yield* Target;
+ const identity = yield* target.newIdentity();
const codexHome = makeCodexHome();
yield* withLocalServer(
@@ -142,6 +145,25 @@ scenario(
CODEX_HOME: codexHome,
});
}
+
+ yield* browser.session(identity, async ({ page, step }) => {
+ await step("Find Computer Use on the integration browse page", async () => {
+ await page.goto(server.url, { waitUntil: "domcontentloaded" });
+ await page.getByRole("link", { name: "Add integration" }).click();
+ await page
+ .getByRole("textbox", { name: "Search integrations, or paste a URL" })
+ .fill("Computer Use");
+
+ const card = page.getByTestId("preset-mcp-codex-computer-use");
+ await card.getByText("Computer Use MCP").waitFor({ timeout: 30_000 });
+ await card
+ .locator(
+ 'img[src="https://learn.chatgpt.com/images/codex/icons/computer-use-plugin-icon.png"]',
+ )
+ .waitFor({ timeout: 30_000 });
+ });
+ });
+
// Curated entries carry the app-server bridge recipe: `codex
// app-server`, the server name the bridge calls tools on, and the
// preset it came from — that last one is what lets a macOS refusal
diff --git a/packages/plugins/mcp/src/react/CodexPluginAdd.tsx b/packages/plugins/mcp/src/react/CodexPluginAdd.tsx
index 957e1d00f0..dbef98116a 100644
--- a/packages/plugins/mcp/src/react/CodexPluginAdd.tsx
+++ b/packages/plugins/mcp/src/react/CodexPluginAdd.tsx
@@ -5,6 +5,7 @@ import * as AsyncResult from "effect/unstable/reactivity/AsyncResult";
import { Button } from "@executor-js/react/components/button";
import { FloatActions } from "@executor-js/react/components/float-actions";
+import { IntegrationFavicon } from "@executor-js/react/components/integration-favicon";
import { integrationsOptimisticAtom } from "@executor-js/react/api/atoms";
import { integrationWriteKeys } from "@executor-js/react/api/reactivity-keys";
import { addIntegrationErrorMessage } from "@executor-js/react/lib/integration-add";
@@ -150,10 +151,11 @@ export default function CodexPluginAdd(props: {
{/* The plugin's own icon comes from the local Codex install; without
one the card still identifies its provider rather than showing a
gap, which matters most on the machines that have no install. */}
-