diff --git a/src/ui/assets/provider-logos/copilot-dark.svg b/src/ui/assets/provider-logos/copilot-dark.svg index d09df805..275b8393 100644 --- a/src/ui/assets/provider-logos/copilot-dark.svg +++ b/src/ui/assets/provider-logos/copilot-dark.svg @@ -1 +1 @@ - + diff --git a/src/ui/assets/provider-logos/copilot-light.svg b/src/ui/assets/provider-logos/copilot-light.svg new file mode 100644 index 00000000..b52d96cf --- /dev/null +++ b/src/ui/assets/provider-logos/copilot-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/cursor-dark.svg b/src/ui/assets/provider-logos/cursor-dark.svg index d50421b5..6849fbc3 100644 --- a/src/ui/assets/provider-logos/cursor-dark.svg +++ b/src/ui/assets/provider-logos/cursor-dark.svg @@ -1 +1,12 @@ - + + + + + + + + diff --git a/src/ui/assets/provider-logos/cursor-light.svg b/src/ui/assets/provider-logos/cursor-light.svg new file mode 100644 index 00000000..b054b189 --- /dev/null +++ b/src/ui/assets/provider-logos/cursor-light.svg @@ -0,0 +1,12 @@ + + + + + + + + diff --git a/src/ui/assets/provider-logos/openai-dark.svg b/src/ui/assets/provider-logos/openai-dark.svg index 7e19c92d..cd86afb5 100644 --- a/src/ui/assets/provider-logos/openai-dark.svg +++ b/src/ui/assets/provider-logos/openai-dark.svg @@ -1 +1,11 @@ - + + + + + + + + + + + diff --git a/src/ui/assets/provider-logos/openai-light.svg b/src/ui/assets/provider-logos/openai-light.svg new file mode 100644 index 00000000..a57ca0da --- /dev/null +++ b/src/ui/assets/provider-logos/openai-light.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/ui/assets/provider-logos/opencode-dark.svg b/src/ui/assets/provider-logos/opencode-dark.svg index 62e10df4..0bdfe089 100644 --- a/src/ui/assets/provider-logos/opencode-dark.svg +++ b/src/ui/assets/provider-logos/opencode-dark.svg @@ -1 +1 @@ - + diff --git a/src/ui/assets/provider-logos/opencode-light.svg b/src/ui/assets/provider-logos/opencode-light.svg new file mode 100644 index 00000000..8a274375 --- /dev/null +++ b/src/ui/assets/provider-logos/opencode-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/assets/provider-logos/pi-on-light.svg b/src/ui/assets/provider-logos/pi-on-light.svg new file mode 100644 index 00000000..5472a94e --- /dev/null +++ b/src/ui/assets/provider-logos/pi-on-light.svg @@ -0,0 +1 @@ + diff --git a/src/ui/icons.ts b/src/ui/icons.ts index 8bada413..8fcb11c4 100644 --- a/src/ui/icons.ts +++ b/src/ui/icons.ts @@ -34,17 +34,40 @@ export const toolIcons = { export type ToolIcon = IconNode; const providerLogos = { - claude: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, - codex: new URL("./assets/provider-logos/openai-dark.svg", import.meta.url).href, - copilot: new URL("./assets/provider-logos/copilot-dark.svg", import.meta.url).href, - cursor: new URL("./assets/provider-logos/cursor-dark.svg", import.meta.url).href, - opencode: new URL("./assets/provider-logos/opencode-dark.svg", import.meta.url).href, - pi: new URL("./assets/provider-logos/pi-on-dark.svg", import.meta.url).href, + claude: { + light: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/claude.svg", import.meta.url).href, + }, + codex: { + light: new URL("./assets/provider-logos/openai-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/openai-dark.svg", import.meta.url).href, + }, + copilot: { + light: new URL("./assets/provider-logos/copilot-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/copilot-dark.svg", import.meta.url).href, + }, + cursor: { + light: new URL("./assets/provider-logos/cursor-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/cursor-dark.svg", import.meta.url).href, + }, + opencode: { + light: new URL("./assets/provider-logos/opencode-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/opencode-dark.svg", import.meta.url).href, + }, + pi: { + light: new URL("./assets/provider-logos/pi-on-light.svg", import.meta.url).href, + dark: new URL("./assets/provider-logos/pi-on-dark.svg", import.meta.url).href, + }, } as const; -export function getProviderLogo(name: string): string | undefined { +export type ProviderLogoTheme = "light" | "dark"; + +export function getProviderLogo( + name: string, + theme: ProviderLogoTheme = "dark", +): string | undefined { const normalizedName = name.trim().toLowerCase() as keyof typeof providerLogos; - return providerLogos[normalizedName]; + return providerLogos[normalizedName]?.[theme]; } export function renderIcon(icon: ToolIcon, className = "icon-svg"): SVGElement { diff --git a/src/ui/workspace-app.tsx b/src/ui/workspace-app.tsx index bc101cb3..27d4f5b8 100644 --- a/src/ui/workspace-app.tsx +++ b/src/ui/workspace-app.tsx @@ -12,7 +12,13 @@ import { type HostContext, type ToolResultCard, } from "./card-types.js"; -import { getProviderLogo, renderIcon, toolIcons, type ToolIcon } from "./icons.js"; +import { + getProviderLogo, + renderIcon, + toolIcons, + type ProviderLogoTheme, + type ToolIcon, +} from "./icons.js"; import { getFileChangePathDisplay, getPatchDisplayParts, @@ -83,6 +89,7 @@ async function boot(): Promise { }; app.onhostcontextchanged = (ctx) => { + const previousTheme = hostContext?.theme; hostContext = { ...hostContext, ...ctx, @@ -90,7 +97,13 @@ async function boot(): Promise { applyHostContext(); // Workspace details inherit host variables directly. Rebuilding their DOM on // iframe resize would reset an in-progress instruction preview interaction. - if (card?.tool !== "open_workspace") renderPayloadIfNeeded(); + if (card?.tool === "open_workspace") { + if (ctx.theme && ctx.theme !== previousTheme) { + syncWorkspaceProviderLogos(ctx.theme === "light" ? "light" : "dark"); + } + } else { + renderPayloadIfNeeded(); + } }; app.onteardown = async () => { @@ -583,6 +596,9 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v const providers = card.agentProviders ?? []; const agents = card.agents ?? []; + const providerLogoTheme: ProviderLogoTheme = hostContext?.theme === "light" + ? "light" + : "dark"; const agentChips: WorkspaceChip[] = agents.map((agent) => { const name = agent.name ?? "Unnamed agent"; const providerName = agent.provider?.trim(); @@ -594,14 +610,17 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v ].filter((value): value is string => Boolean(value)).join("\n"); return { label: name, - logo: providerName ? getProviderLogo(providerName) : undefined, + logo: providerName + ? getProviderLogo(providerName, providerLogoTheme) + : undefined, + logoProvider: providerName, profile: true, title: title || undefined, }; }); const providerChips: WorkspaceChip[] = providers.map((provider) => { const name = provider.id?.trim() || "Unknown provider"; - const logo = getProviderLogo(name); + const logo = getProviderLogo(name, providerLogoTheme); const title = [ provider.model ? `Model: ${provider.model}` : undefined, provider.effort ? `Effort: ${provider.effort}` : undefined, @@ -610,6 +629,7 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v return { label: name, logo, + logoProvider: logo ? name : undefined, bareLogo: Boolean(logo), ariaLabel: name, title: title || name, @@ -636,6 +656,7 @@ function renderWorkspacePayload(container: HTMLElement, card: ToolResultCard): v interface WorkspaceChip { label: string; logo?: string; + logoProvider?: string; profile?: boolean; bareLogo?: boolean; ariaLabel?: string; @@ -913,6 +934,7 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { ? "workspace-agent-profile-logo" : "workspace-chip-logo"; logo.src = chip.logo; + if (chip.logoProvider) logo.dataset.provider = chip.logoProvider; logo.alt = ""; logo.setAttribute("aria-hidden", "true"); item.append(logo); @@ -925,6 +947,15 @@ function renderWorkspaceChips(chips: WorkspaceChip[]): HTMLElement { return list; } +function syncWorkspaceProviderLogos(theme: ProviderLogoTheme): void { + for (const logo of document.querySelectorAll("img[data-provider]")) { + const providerName = logo.dataset.provider; + if (!providerName) continue; + const src = getProviderLogo(providerName, theme); + if (src && logo.src !== src) logo.src = src; + } +} + function element( tag: K, options: {