From 3fdecd2b3917f60b8d36bcbfb99bd57476101efe Mon Sep 17 00:00:00 2001 From: bitkyc08-arch Date: Tue, 1 Sep 2026 01:45:40 +0900 Subject: [PATCH] fix(gui): wire the provider marks that were already committed minimax.svg landed for the MiniMax Code client in #3082. The MiniMax provider kept rendering a coloured initial tile, because CLIENT_MARKS is keyed by ExportClientId and PROVIDER_ICON_ALIASES by provider id: adding artwork on one side leaves no signal on the other. xiaomi-color.svg had the same problem, and more visibly -- mimo-free was already wired to it while xiaomi-mimo and mimo, the same brand, were not. Four alias rows. minimax and minimax-cn are one brand on two endpoints, the same shape as the three Alibaba ids that already share one asset. Both assets are multi-colour (xiaomi-color.svg carries #FF6900 and three more, minimax.svg a linearGradient), so neither is a masking candidate and this makes no painting decision. The guard is the part that matters. The fallback tile is a designed state that looks exactly like a mistake, so nothing could tell a maintainer that committed artwork was unwired. The new test probes, for every registry provider without an alias, whether a plausibly-named asset is sitting in the directory -- and fails with the filename when one is. It keeps closing that loop as new assets land. Providers without a resolved mark: 38 -> 34. Verification: 3 pass in the new file, each driven red (removing the minimax row, breaking a filename). tsc 0 both roots, oxlint clean, privacy:scan clean, full GUI suite 1157 pass / 0 fail across 187 files. --- gui/src/provider-icons.ts | 4 ++ gui/tests/provider-icons.test.ts | 76 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 gui/tests/provider-icons.test.ts diff --git a/gui/src/provider-icons.ts b/gui/src/provider-icons.ts index 2bfb727c71..043529cf67 100644 --- a/gui/src/provider-icons.ts +++ b/gui/src/provider-icons.ts @@ -28,6 +28,8 @@ const PROVIDER_ICON_ALIASES: Record = { kiro: "kiro-color.svg", "lm-studio": "lm-studio-color.svg", mistral: "mistral-color.svg", + minimax: "minimax.svg", + "minimax-cn": "minimax.svg", moonshot: "moonshot-color.svg", nvidia: "nvidia-color.svg", ollama: "ollama-color.svg", @@ -47,7 +49,9 @@ const PROVIDER_ICON_ALIASES: Record = { vllm: "vllm-color.svg", xai: "grok.svg", "mimo-free": "xiaomi-color.svg", + mimo: "xiaomi-color.svg", xiaomi: "xiaomi-color.svg", + "xiaomi-mimo": "xiaomi-color.svg", }; /** diff --git a/gui/tests/provider-icons.test.ts b/gui/tests/provider-icons.test.ts new file mode 100644 index 0000000000..5a0495c453 --- /dev/null +++ b/gui/tests/provider-icons.test.ts @@ -0,0 +1,76 @@ +import { expect, test } from "bun:test"; +import { existsSync, readdirSync } from "node:fs"; +import { join } from "node:path"; +import { PROVIDER_REGISTRY } from "../../src/providers/registry"; +import { providerIconSrc } from "../src/provider-icons"; + +const PUBLIC_DIR = join(import.meta.dir, "..", "public", "provider-icons"); + +/** + * Asset filenames that could plausibly belong to a provider id. + * + * A plan variant is a billing arrangement, not a brand -- `alibaba-token-plan` + * wears the Alibaba mark -- so the first dash-segment is probed too. + */ +function candidateAssets(providerId: string): string[] { + const stem = providerId.split("-")[0]!; + return [ + `${providerId}.svg`, + `${providerId}-color.svg`, + `${stem}.svg`, + `${stem}-color.svg`, + ]; +} + +/* + * The gap this exists for, stated plainly: `minimax.svg` was committed for the + * MiniMax Code CLIENT and the MiniMax PROVIDER kept rendering an initial tile for + * weeks. Nothing could have told anyone. `CLIENT_MARKS` is keyed by + * `ExportClientId` and `PROVIDER_ICON_ALIASES` by provider id, so adding artwork + * on one side leaves no signal on the other, and the fallback tile is a designed + * state that looks identical to a mistake. + * + * This is the only check that closes that loop, and it keeps closing it as new + * assets land: a mark added for any reason is immediately owed a provider row. + */ +test("a provider whose brand asset is already committed is actually wired to it", () => { + const files = new Set(readdirSync(PUBLIC_DIR).filter(name => name.endsWith(".svg"))); + const unwired: string[] = []; + for (const entry of PROVIDER_REGISTRY) { + if (providerIconSrc(entry.id)) continue; + const found = candidateAssets(entry.id).find(name => files.has(name)); + if (found) unwired.push(`${entry.id}: ${found} is committed but PROVIDER_ICON_ALIASES has no row`); + } + expect(unwired).toEqual([]); +}); + +/* + * A mistyped filename renders a broken image, which is strictly worse than the + * fallback tile it replaced: the tile is deliberate and legible, the broken image + * is a visual defect. The map is plain strings, so nothing else checks this. + */ +test("every wired provider icon names a file that exists", () => { + const broken = PROVIDER_REGISTRY + .map(entry => [entry.id, providerIconSrc(entry.id)] as const) + .filter((pair): pair is readonly [string, string] => pair[1] !== undefined) + .filter(([, src]) => !existsSync(join(PUBLIC_DIR, src.split("/").pop()!))) + .map(([id, src]) => `${id} -> ${src}`); + expect(broken).toEqual([]); +}); + +/* + * The four rows this phase added, pinned by intent rather than by count. + * + * MiniMax is one brand on two endpoints (`api.minimax.io` and `api.minimaxi.com`), + * the same shape as the three Alibaba ids that already share one asset. Xiaomi's + * MiMo ids are the same brand as `mimo-free`, which was already wired -- the + * inconsistency was the bug. + */ +test("the MiniMax and Xiaomi MiMo provider ids resolve to their brand's mark", () => { + expect(providerIconSrc("minimax")).toBe("/provider-icons/minimax.svg"); + expect(providerIconSrc("minimax-cn")).toBe("/provider-icons/minimax.svg"); + expect(providerIconSrc("xiaomi-mimo")).toBe("/provider-icons/xiaomi-color.svg"); + expect(providerIconSrc("mimo")).toBe("/provider-icons/xiaomi-color.svg"); + // The precedent that makes the two above consistent rather than novel. + expect(providerIconSrc("mimo-free")).toBe("/provider-icons/xiaomi-color.svg"); +});