feat(dashboard): add CheaperInference sponsor banner and route banner links through the shortener (#11196)

- New CheaperInferenceSponsorBanner on the dashboard home, same size/shape as
  KimiSponsorBanner, no version gate (durable partnership). Uses the
  cheaperinference ProviderIcon and the brand green (#31f889) with the dark
  ink CTA (contrast, per colors.ts token).
- CTA points at https://link.omniroute.online/cheaper — the branded short
  link — so clicks land in our Kutt metrics.
- VscodeCopilotBanner CTA now points at https://link.omniroute.online/vsx
  instead of the raw Marketplace URL, for the same reason.
- i18n strings in en + pt (en is the namespace-level fallback for the other
  41 locales).
- Tests: new cheaperInferenceSponsorBanner.test.tsx (render, CTA href, dismiss
  persistence); vscodeCopilotBanner.test.tsx updated to the new CTA URL.

Co-authored-by: Xiangzhe <bakryun0718@proton.me>
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-08-22 22:46:34 -03:00
committed by GitHub
parent 3ef54fc55b
commit eb5797370a
7 changed files with 220 additions and 2 deletions

View File

@@ -0,0 +1,108 @@
"use client";
import { useSyncExternalStore } from "react";
import { useTranslations } from "next-intl";
import ProviderIcon from "@/shared/components/ProviderIcon";
// Branded short link through our own link.omniroute.online shortener, so the
// click lands in our Kutt metrics. Points at cheaperinference.com?utm_source=omniroute
// (the URL in README.md's Open Source Friends section). Keep in sync with the
// `cheaper` slug on the shortener.
const CHEAPER_INFERENCE_URL = "https://link.omniroute.online/cheaper";
// Cheaper Inference brand green (#31f889). White text on it fails contrast, so
// the CTA pairs it with the dark ink from the provider's color token (colors.ts:
// cheaperinference.text = #04170d). Hex values stay in sync with that token.
const DISMISS_STORAGE_KEY = "omniroute-cheaperinference-sponsor-banner-dismissed-v1";
// Same-tab signal for the dismiss button, since writing localStorage doesn't
// fire a "storage" event in the tab that wrote it.
const DISMISS_EVENT = "omniroute:cheaperinference-sponsor-banner-dismissed";
function isNotDismissed(): boolean {
try {
return !localStorage.getItem(DISMISS_STORAGE_KEY);
} catch {
return true;
}
}
function subscribe(callback: () => void) {
window.addEventListener(DISMISS_EVENT, callback);
return () => window.removeEventListener(DISMISS_EVENT, callback);
}
// SSR has no localStorage, so the server always renders the banner visible;
// useSyncExternalStore reconciles that against the real client-side value
// right after hydration, mirroring KimiSponsorBanner's pattern.
function getServerSnapshot() {
return true;
}
/**
* Dismissable banner announcing the Cheaper Inference OmniRoute partnership on
* the dashboard home page — same size/shape as KimiSponsorBanner, no version
* gate (durable partnership, not a time-boxed offer). The logomark reuses
* <ProviderIcon providerId="cheaperinference" .../>.
*/
export default function CheaperInferenceSponsorBanner() {
const t = useTranslations("cheaperInferenceSponsorBanner");
const visible = useSyncExternalStore(subscribe, isNotDismissed, getServerSnapshot);
if (!visible) {
return null;
}
const dismiss = () => {
try {
localStorage.setItem(DISMISS_STORAGE_KEY, "true");
} catch {
// ignore — worst case the banner reappears next visit
}
window.dispatchEvent(new Event(DISMISS_EVENT));
};
return (
<div
role="complementary"
aria-label={t("title")}
className="mb-4 flex flex-col gap-3 rounded-lg border border-[#31f889]/30 bg-[#31f889]/5 px-4 py-3 dark:bg-[#31f889]/10 sm:flex-row sm:items-center sm:justify-between"
>
<div className="flex min-w-0 items-start gap-3">
<div className="flex size-9 shrink-0 items-center justify-center rounded-lg bg-[#31f889]/10">
<ProviderIcon providerId="cheaperinference" size={24} type="color" />
</div>
<div className="min-w-0">
<p className="text-sm font-semibold text-text-main">{t("title")}</p>
<p className="mt-0.5 text-xs text-text-muted">{t("description")}</p>
</div>
</div>
<div className="flex shrink-0 items-center gap-3 self-end sm:self-auto">
<div className="flex flex-col items-end gap-0.5">
<a
href={CHEAPER_INFERENCE_URL}
target="_blank"
rel="noopener noreferrer"
title={t("partnerLinkNote")}
className="inline-flex items-center gap-1.5 whitespace-nowrap rounded-lg bg-[#31f889] px-3 py-1.5 text-xs font-semibold text-[#04170d] transition-colors hover:brightness-110"
>
{t("cta")}
<span className="material-symbols-outlined text-[14px]" aria-hidden="true">
open_in_new
</span>
</a>
<span className="text-[9px] text-text-muted/70">{t("partnerLinkNote")}</span>
</div>
<button
type="button"
onClick={dismiss}
aria-label={t("dismissAriaLabel")}
className="text-text-muted transition-colors hover:text-text-main"
>
<span className="material-symbols-outlined text-[18px]">close</span>
</button>
</div>
</div>
);
}

View File

@@ -6,7 +6,9 @@ import { useTranslations } from "next-intl";
// Marketplace listing is the primary CTA; Open VSX (Cursor/Windsurf/VSCodium/etc.)
// is called out via secondaryNote instead of a second button, to keep this banner
// the same size as KimiSponsorBanner.
const MARKETPLACE_URL = "https://marketplace.visualstudio.com/items?itemName=diegosouzapw.omnicopilot";
// Branded short link through our own link.omniroute.online shortener (the `vsx`
// slug), so the click lands in our Kutt metrics.
const MARKETPLACE_URL = "https://link.omniroute.online/vsx";
const DISMISS_STORAGE_KEY = "omniroute-vscode-copilot-banner-dismissed-v1";
// Same-tab signal for the dismiss button, since writing localStorage doesn't

View File

@@ -4,6 +4,7 @@ import { getSettings } from "@/lib/localDb";
import HomePageClient from "../dashboard/HomePageClient";
import BootstrapBanner from "../dashboard/BootstrapBanner";
import KimiSponsorBanner from "../dashboard/KimiSponsorBanner";
import CheaperInferenceSponsorBanner from "../dashboard/CheaperInferenceSponsorBanner";
import VscodeCopilotBanner from "../dashboard/VscodeCopilotBanner";
import NewsBanner from "../dashboard/NewsBanner";
@@ -20,6 +21,7 @@ export default async function HomePage() {
<>
{isBootstrapped && <BootstrapBanner />}
<KimiSponsorBanner />
<CheaperInferenceSponsorBanner />
<VscodeCopilotBanner />
<NewsBanner />
<HomePageClient machineId={machineId} />

View File

@@ -13869,5 +13869,12 @@
"toolsMismatch": "Provider does not support tool calling",
"structuredOutputMismatch": "Provider does not support structured output",
"contextWindowMismatch": "Request exceeds provider context window"
},
"cheaperInferenceSponsorBanner": {
"title": "Cheaper Inference is an OmniRoute Open Source Friend",
"description": "A cost-ranked gateway reselling dozens of frontier models behind one OpenAI-compatible endpoint — routing each request to the cheapest eligible provider, never above list price.",
"cta": "Get an API Key",
"partnerLinkNote": "Partner link",
"dismissAriaLabel": "Dismiss"
}
}

View File

@@ -13845,5 +13845,12 @@
"toolsMismatch": "Provider does not support tool calling",
"structuredOutputMismatch": "Provider does not support structured output",
"contextWindowMismatch": "Request exceeds provider context window"
},
"cheaperInferenceSponsorBanner": {
"title": "A Cheaper Inference é uma Amiga do Código Aberto do OmniRoute",
"description": "Um gateway com custo ordenado que revende dezenas de modelos de fronteira num único endpoint compatível com OpenAI — roteando cada requisição ao provedor elegível mais barato, nunca acima do preço de tabela.",
"cta": "Obter uma Chave de API",
"partnerLinkNote": "Link de parceiro",
"dismissAriaLabel": "Dispensar"
}
}

View File

@@ -0,0 +1,92 @@
// @vitest-environment jsdom
/**
* CheaperInferenceSponsorBanner — render gate (localStorage dismissal), CTA
* pointing at our link.omniroute.online branded short link, and discreet
* partner-link note. Mirrors kimiSponsorBanner.test.tsx, minus the version gate
* (this banner is a durable partnership, not a time-boxed offer).
*/
import React from "react";
import { act } from "react";
import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const STORAGE_KEY = "omniroute-cheaperinference-sponsor-banner-dismissed-v1";
const DISMISS_EVENT = "omniroute:cheaperinference-sponsor-banner-dismissed";
const SHORT_URL = "https://link.omniroute.online/cheaper";
vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k }));
vi.mock("@/shared/components/ProviderIcon", () => ({ default: () => null }));
async function renderBanner(): Promise<HTMLDivElement> {
vi.resetModules();
const { default: CheaperInferenceSponsorBanner } =
await import("../../../src/app/(dashboard)/dashboard/CheaperInferenceSponsorBanner");
const container = document.createElement("div");
document.body.appendChild(container);
const root = createRoot(container);
act(() => {
root.render(<CheaperInferenceSponsorBanner />);
});
return container;
}
describe("CheaperInferenceSponsorBanner", () => {
beforeEach(() => {
(
globalThis as typeof globalThis & { IS_REACT_ACT_ENVIRONMENT?: boolean }
).IS_REACT_ACT_ENVIRONMENT = true;
localStorage.removeItem(STORAGE_KEY);
});
afterEach(() => {
document.body.innerHTML = "";
localStorage.removeItem(STORAGE_KEY);
});
it("renders with the CTA pointing at the branded short link", async () => {
const container = await renderBanner();
expect(container.textContent).toContain("title");
expect(container.textContent).toContain("cta");
const link = container.querySelector("a[href]");
expect(link).not.toBeNull();
expect(link?.getAttribute("href")).toBe(SHORT_URL);
expect(link?.getAttribute("target")).toBe("_blank");
expect(link?.getAttribute("rel")).toContain("noopener");
});
it("shows the discreet partner-link note near the CTA", async () => {
const container = await renderBanner();
expect(container.textContent).toContain("partnerLinkNote");
const link = container.querySelector("a[href]");
expect(link?.getAttribute("title")).toBe("partnerLinkNote");
});
it("hides after dismissal and stays hidden on re-render", async () => {
const first = await renderBanner();
const button = first.querySelector("button");
expect(button).not.toBeNull();
act(() => {
button?.click();
});
expect(localStorage.getItem(STORAGE_KEY)).toBe("true");
expect(first.textContent).not.toContain("title");
// a fresh render (simulating a later visit) stays hidden
const second = await renderBanner();
expect(second.textContent).not.toContain("title");
});
it("re-renders visible again only after the key is cleared", async () => {
const first = await renderBanner();
const button = first.querySelector("button");
act(() => {
button?.click();
});
expect(localStorage.getItem(STORAGE_KEY)).toBe("true");
localStorage.removeItem(STORAGE_KEY);
const second = await renderBanner();
expect(second.textContent).toContain("title");
});
});

View File

@@ -11,7 +11,7 @@ import { createRoot } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
const STORAGE_KEY = "omniroute-vscode-copilot-banner-dismissed-v1";
const MARKETPLACE_URL = "https://marketplace.visualstudio.com/items?itemName=diegosouzapw.omnicopilot";
const MARKETPLACE_URL = "https://link.omniroute.online/vsx";
vi.mock("next-intl", () => ({ useTranslations: () => (k: string) => k }));