FN-8487: render Oh My Pi logo in gradient
Render the shared Oh My Pi provider icon with its official colorful gradient.\n\n- Add a scoped SVG gradient with unique IDs for simultaneous OMP icons.\n- Define reusable OMP gradient tokens and retain the provider color fallback.\n- Cover gradient rendering and ID uniqueness in provider icon tests.\n\nFiles changed:\n packages/dashboard/app/components/ProviderIcon.tsx | 18 ++++++++++---\n .../app/components/__tests__/ProviderIcon.test.tsx | 31 ++++++++++++++++++++--\n packages/dashboard/app/styles.css | 5 +++-\n 3 files changed, 47 insertions(+), 7 deletions(-) Fusion-Task-Id: FN-8487 Fusion-Task-Lineage: 6b507791-934f-443b-9c93-f59e5ee99ddf Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { useId } from "react";
|
||||
import { Cpu } from "lucide-react";
|
||||
|
||||
import { inferProviderIconKey } from "../utils/providerIconKey";
|
||||
@@ -527,11 +528,13 @@ 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.
|
||||
FNXC:ProviderIcons 2026-07-22-00:00:
|
||||
FN-8487: The shared OMP mark must retain the official omp.sh favicon's pink-to-purple-to-cyan diagonal gradient instead of adapting to the theme as a monochrome glyph. Keep the favicon's stepped-T path but omit its dark rounded tile so the mark remains transparent on dashboard chrome.
|
||||
Source: https://omp.sh/favicon.svg
|
||||
*/
|
||||
function OmpIcon({ size, color, label = "Oh My Pi" }: { size: number; color: string; label?: string }) {
|
||||
function OmpIcon({ size, label = "Oh My Pi" }: { size: number; color: string; label?: string }) {
|
||||
const gradientId = `omp-gradient-${useId().replace(/:/g, "")}`;
|
||||
|
||||
return (
|
||||
<svg
|
||||
width={size}
|
||||
@@ -542,7 +545,14 @@ function OmpIcon({ size, color, label = "Oh My Pi" }: { size: number; color: str
|
||||
data-testid="omp-icon"
|
||||
aria-label={label}
|
||||
>
|
||||
<path d="M14 16h36v8H40v32h-8V24h-6v22h-8V24h-4z" fill={color} />
|
||||
<defs>
|
||||
<linearGradient id={gradientId} x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0" stopColor="var(--provider-omp-gradient-start)" />
|
||||
<stop offset="0.5" stopColor="var(--provider-omp-gradient-mid)" />
|
||||
<stop offset="1" stopColor="var(--provider-omp-gradient-end)" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path d="M14 16h36v8H40v32h-8V24h-6v22h-8V24h-4z" fill={`url(#${gradientId})`} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -74,14 +74,41 @@ 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", () => {
|
||||
it("renders the OMP brand icon, auth label, and official tokenized gradient for omp-cli", () => {
|
||||
render(<ProviderIcon provider="omp-cli" />);
|
||||
const svg = screen.getByTestId("omp-icon");
|
||||
const gradient = svg.querySelector("linearGradient");
|
||||
const path = svg.querySelector("path");
|
||||
|
||||
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)");
|
||||
expect(path).toHaveAttribute("fill", `url(#${gradient?.id})`);
|
||||
expect(gradient).toHaveAttribute("x1", "0");
|
||||
expect(gradient).toHaveAttribute("y1", "0");
|
||||
expect(gradient).toHaveAttribute("x2", "1");
|
||||
expect(gradient).toHaveAttribute("y2", "1");
|
||||
expect(Array.from(gradient?.querySelectorAll("stop") ?? []).map((stop) => stop.getAttribute("stop-color"))).toEqual([
|
||||
"var(--provider-omp-gradient-start)",
|
||||
"var(--provider-omp-gradient-mid)",
|
||||
"var(--provider-omp-gradient-end)",
|
||||
]);
|
||||
});
|
||||
|
||||
it("assigns unique gradient ids to simultaneous OMP icons", () => {
|
||||
const { container } = render(
|
||||
<>
|
||||
<ProviderIcon provider="omp-cli" />
|
||||
<ProviderIcon provider="omp" />
|
||||
</>,
|
||||
);
|
||||
const gradients = Array.from(container.querySelectorAll("[data-testid='omp-icon'] linearGradient"));
|
||||
const paths = Array.from(container.querySelectorAll("[data-testid='omp-icon'] path"));
|
||||
|
||||
expect(gradients).toHaveLength(2);
|
||||
expect(new Set(gradients.map((gradient) => gradient.id)).size).toBe(2);
|
||||
expect(paths.map((path) => path.getAttribute("fill"))).toEqual(gradients.map((gradient) => `url(#${gradient.id})`));
|
||||
});
|
||||
|
||||
it.each(["omp", "oh-my-pi"])("renders the OMP brand icon for the %s alias", (provider) => {
|
||||
|
||||
@@ -396,7 +396,10 @@ svg.spinning {
|
||||
--provider-cursor-cli: #7c3aed;
|
||||
--provider-ollama: #d4a27f;
|
||||
/* Runtime-plugin marks. */
|
||||
--provider-omp: var(--text);
|
||||
--provider-omp-gradient-start: #ed4abf;
|
||||
--provider-omp-gradient-mid: #9b4dff;
|
||||
--provider-omp-gradient-end: #5ad8e6;
|
||||
--provider-omp: var(--provider-omp-gradient-mid);
|
||||
--provider-hermes: #d4961c;
|
||||
--provider-openclaw: #ff4f40;
|
||||
--provider-paperclip: var(--text);
|
||||
|
||||
Reference in New Issue
Block a user