FN-8354: add Oh My Pi provider logo

Render the official Oh My Pi mark throughout provider selection and analytics surfaces.

- Add a tokenized OMP SVG icon and provider aliases.
- Infer OMP-tagged model identifiers without misattributing bare models.
- Cover OMP icon rendering and inference with dashboard tests.

Files changed:
 packages/dashboard/app/components/ProviderIcon.tsx | 26 +++++++++++++++++++++-
 .../app/components/__tests__/ProviderIcon.test.tsx | 23 +++++++++++++++++++
 packages/dashboard/app/styles.css                  |  1 +
 .../app/utils/__tests__/providerIconKey.test.ts    |  8 +++++++
 packages/dashboard/app/utils/providerIconKey.ts    |  9 ++++++++
 5 files changed, 66 insertions(+), 1 deletion(-)

Fusion-Task-Id: FN-8354

Fusion-Task-Lineage: 71d5638c-b41e-4960-9391-c7a441d04f51

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-07-18 18:58:44 -07:00
parent 0cba6fa56a
commit 927f2baf7d
5 changed files with 66 additions and 1 deletions

View File

@@ -526,6 +526,27 @@ function GitHubIcon({ size, color, label = "GitHub" }: { size: number; color: st
);
}
/*
FNXC:ProviderIcons 2026-07-18-18:24:
FN-8354: OMP ACP is a first-class provider, so its shared dashboard icon must use the official omp.sh favicon's stepped T mark rather than the generic Cpu fallback. Render its official single-color silhouette with the provider token so it remains legible in either theme.
Source: https://omp.sh/favicon.svg
*/
function OmpIcon({ size, color, label = "Oh My Pi" }: { size: number; color: string; label?: string }) {
return (
<svg
width={size}
height={size}
viewBox="0 0 64 64"
fill="none"
xmlns="http://www.w3.org/2000/svg"
data-testid="omp-icon"
aria-label={label}
>
<path d="M14 16h36v8H40v32h-8V24h-6v22h-8V24h-4z" fill={color} />
</svg>
);
}
// Hermes — caduceus (winged staff) mark, single-color so it adapts to theme.
function HermesIcon({ size, color, label = "Hermes" }: { size: number; color: string; label?: string }) {
return (
@@ -817,7 +838,10 @@ const providerConfig: Record<
vercel: { component: VercelIcon, color: "var(--provider-vercel)" },
"vercel-ai-gateway": { component: VercelIcon, color: "var(--provider-vercel)", label: "Vercel AI Gateway" },
// Runtime-plugin marks (Hermes / OpenClaw / Paperclip).
// Runtime-plugin marks (OMP / Hermes / OpenClaw / Paperclip).
"omp-cli": { component: OmpIcon, color: "var(--provider-omp)", label: "Oh My Pi — via omp ACP" },
omp: { component: OmpIcon, color: "var(--provider-omp)", label: "Oh My Pi" },
"oh-my-pi": { component: OmpIcon, color: "var(--provider-omp)", label: "Oh My Pi" },
hermes: { component: HermesIcon, color: "var(--provider-hermes)", label: "Hermes" },
"hermes-agent": { component: HermesIcon, color: "var(--provider-hermes)", label: "Hermes" },
hermesagent: { component: HermesIcon, color: "var(--provider-hermes)", label: "Hermes" },

View File

@@ -74,6 +74,29 @@ describe("ProviderIcon", () => {
expect(document.querySelector('[data-provider="cursor"] svg:not([data-testid])')).not.toBeInTheDocument();
});
it("renders the OMP brand icon, auth label, and tokenized color for omp-cli", () => {
render(<ProviderIcon provider="omp-cli" />);
const svg = screen.getByTestId("omp-icon");
expect(svg).toBeInTheDocument();
expect(screen.getByLabelText("Oh My Pi — via omp ACP")).toBeInTheDocument();
expect(svg.parentElement).toHaveAttribute("data-provider", "omp-cli");
expect(svg.parentElement).toHaveStyle({ color: "var(--provider-omp)" });
expect(svg.querySelector("path")).toHaveAttribute("fill", "var(--provider-omp)");
});
it.each(["omp", "oh-my-pi"])("renders the OMP brand icon for the %s alias", (provider) => {
render(<ProviderIcon provider={provider} />);
const svg = screen.getByTestId("omp-icon");
expect(svg.parentElement).toHaveAttribute("data-provider", provider);
expect(svg.parentElement).toHaveStyle({ color: "var(--provider-omp)" });
});
it("infers OMP model-provider strings to the OMP brand icon", () => {
render(<ProviderIcon provider="omp/claude-sonnet-4" />);
expect(screen.getByTestId("omp-icon")).toBeInTheDocument();
expect(document.querySelector('[data-provider="omp/claude-sonnet-4"] svg:not([data-testid])')).not.toBeInTheDocument();
});
it("renders grok-cli icon reusing the xAI brand mark", () => {
render(<ProviderIcon provider="grok-cli" />);
const svg = screen.getByTestId("xai-icon");

View File

@@ -385,6 +385,7 @@ svg.spinning {
--provider-cursor-cli: #7c3aed;
--provider-ollama: #d4a27f;
/* Runtime-plugin marks. */
--provider-omp: var(--text);
--provider-hermes: #d4961c;
--provider-openclaw: #ff4f40;
--provider-paperclip: var(--text);

View File

@@ -13,6 +13,9 @@ describe("inferProviderIconKey", () => {
["Cursor", "cursor-cli"],
["cursor-agent", "cursor-cli"],
["cursor/gpt-5", "cursor-cli"],
["omp-cli/MiniMax-M2.5", "omp-cli"],
["omp/claude-sonnet-4", "omp-cli"],
["Oh My Pi", "omp-cli"],
["ollama/llama3", "ollama"],
["minimax-text-01", "minimax"],
["zhipu-glm-4", "zai"],
@@ -32,6 +35,11 @@ describe("inferProviderIconKey", () => {
expect(inferProviderIconKey(input)).toBe(expected);
});
it("does not attribute bare model ids to OMP", () => {
expect(inferProviderIconKey("MiniMax-M2.5")).toBe("minimax");
expect(inferProviderIconKey("claude-sonnet-4")).not.toBe("omp-cli");
});
it("returns unknown ids unchanged so ProviderIcon can render its fallback", () => {
expect(inferProviderIconKey("custom-model-v1")).toBe("custom-model-v1");
expect(inferProviderIconKey("not-a-glmish-model")).toBe("not-a-glmish-model");

View File

@@ -6,6 +6,8 @@ FNXC:ProviderIcons 2026-06-28-20:46:
Standalone GLM model ids such as glm-5.1, glm-4.5-air, and glm-5v-turbo are Z.ai/Zhipu-family models even when the analytics label omits zai/zhipu. Match GLM only at model-id segment boundaries so unrelated words do not hijack the Z.ai provider icon.
*/
const GLM_MODEL_SEGMENT_PATTERN = /(?:^|[/:_-])glm(?:$|[\d._:-]|-[\da-z])/;
const OMP_PROVIDER_SEGMENT_PATTERN = /(?:^|[/:_\s-])omp(?:$|[/:_\s-])/;
const OMP_PROVIDER_NAME_PATTERN = /oh[-\s]my[-\s]pi/;
export function inferProviderIconKey(modelOrProviderName: string): string {
const normalized = modelOrProviderName.toLowerCase();
@@ -19,6 +21,13 @@ export function inferProviderIconKey(modelOrProviderName: string): string {
if (normalized.includes("cursor")) {
return "cursor-cli";
}
/*
FNXC:ProviderIcons 2026-07-18-18:24:
FN-8354: omp ACP model ids can contain another vendor model name (for example omp/claude-sonnet-4), but must retain the OMP brand mark on selection and analytics surfaces. Match only an OMP segment or the Oh My Pi name so unrelated strings and untagged model ids keep existing inference/fallback behavior.
*/
if (OMP_PROVIDER_SEGMENT_PATTERN.test(normalized) || OMP_PROVIDER_NAME_PATTERN.test(normalized)) {
return "omp-cli";
}
if (normalized.includes("claude") || normalized.includes("anthropic")) {
return "anthropic";
}