Show catalog provider icons (#1098)

This commit is contained in:
Mohamed Boudra
2026-05-19 20:54:33 +08:00
committed by GitHub
parent 2c561536e4
commit 352e8fb6eb
2 changed files with 82 additions and 8 deletions
@@ -0,0 +1,30 @@
import { describe, expect, it, vi } from "vitest";
vi.mock("lucide-react-native", () => ({
Bot: function Bot() {
return null;
},
PackagePlus: function PackagePlus() {
return null;
},
}));
import { Bot, PackagePlus } from "lucide-react-native";
import { getProviderIcon } from "./provider-icons";
describe("getProviderIcon", () => {
it("keeps built-in provider icons", () => {
expect(getProviderIcon("kiro")).toBe(PackagePlus);
});
it("uses vendored ACP catalog icons for catalog provider ids", () => {
const icon = getProviderIcon("amp-acp");
expect(icon).not.toBe(Bot);
expect(getProviderIcon("amp-acp")).toBe(icon);
});
it("falls back to the robot icon for unknown custom providers", () => {
expect(getProviderIcon("custom-claude-profile")).toBe(Bot);
});
});
+52 -8
View File
@@ -1,19 +1,63 @@
import { Bot, PackagePlus } from "lucide-react-native";
import { createElement, type ComponentType } from "react";
import { SvgXml } from "react-native-svg";
import { ClaudeIcon } from "@/components/icons/claude-icon";
import { CodexIcon } from "@/components/icons/codex-icon";
import { CopilotIcon } from "@/components/icons/copilot-icon";
import { OpenCodeIcon } from "@/components/icons/opencode-icon";
import { PiIcon } from "@/components/icons/pi-icon";
import { ACP_PROVIDER_CATALOG } from "@/data/acp-provider-catalog";
const PROVIDER_ICONS: Record<string, typeof Bot> = {
claude: ClaudeIcon as unknown as typeof Bot,
codex: CodexIcon as unknown as typeof Bot,
copilot: CopilotIcon as unknown as typeof Bot,
export interface ProviderIconProps {
size: number;
color: string;
}
export type ProviderIconComponent = ComponentType<ProviderIconProps>;
const PROVIDER_ICONS: Record<string, ProviderIconComponent> = {
claude: ClaudeIcon as unknown as ProviderIconComponent,
codex: CodexIcon as unknown as ProviderIconComponent,
copilot: CopilotIcon as unknown as ProviderIconComponent,
kiro: PackagePlus,
opencode: OpenCodeIcon as unknown as typeof Bot,
pi: PiIcon as unknown as typeof Bot,
opencode: OpenCodeIcon as unknown as ProviderIconComponent,
pi: PiIcon as unknown as ProviderIconComponent,
};
export function getProviderIcon(provider: string): typeof Bot {
return PROVIDER_ICONS[provider] ?? Bot;
const CATALOG_ICON_SVGS = new Map(
ACP_PROVIDER_CATALOG.flatMap((entry) => (entry.iconSvg ? [[entry.id, entry.iconSvg]] : [])),
);
const catalogIconComponents = new Map<string, ProviderIconComponent>();
function createCatalogIcon(provider: string, iconSvg: string): ProviderIconComponent {
const CatalogProviderIcon: ProviderIconComponent = ({ size, color }) =>
createElement(SvgXml, {
xml: iconSvg,
width: size,
height: size,
color,
});
CatalogProviderIcon.displayName = `CatalogProviderIcon(${provider})`;
return CatalogProviderIcon;
}
function getCatalogProviderIcon(provider: string): ProviderIconComponent | undefined {
const cached = catalogIconComponents.get(provider);
if (cached) {
return cached;
}
const iconSvg = CATALOG_ICON_SVGS.get(provider);
if (!iconSvg) {
return undefined;
}
const icon = createCatalogIcon(provider, iconSvg);
catalogIconComponents.set(provider, icon);
return icon;
}
export function getProviderIcon(provider: string): ProviderIconComponent {
return PROVIDER_ICONS[provider] ?? getCatalogProviderIcon(provider) ?? Bot;
}