diff --git a/gui/public/provider-icons/README.md b/gui/public/provider-icons/README.md index 39be97558e..cf17e7230e 100644 --- a/gui/public/provider-icons/README.md +++ b/gui/public/provider-icons/README.md @@ -37,6 +37,14 @@ Export-client marks (used by the API tab's connect rows, not the provider list): carries its authoring tool's generator comment). - `kimi-color.svg` — already in the baseline as a provider icon; the API tab reuses it for the Kimi Code client, which is the same Moonshot AI brand. +- `cline-color.svg` — the Cline mark introduced by + [`ec81f7fef9bb`](https://github.com/lidge-jun/opencodex/blob/ec81f7fef9bb0086d3d527cac234d3845ce4d559/gui/public/provider-icons/cline-color.svg), + whose commit identifies it as the official brand mark but records no download + URL. That Git object preserves the original 92x96 SVG. `b4820478fcea` removed + its fixed dimensions and normalized it to `viewBox="0 0 24 24"` with + `translate(0.5000 0.0000) scale(0.25000)`. The path, circle, rectangles, and + purple `#9F58FA` fill/strokes are unchanged. The Cline client reuses this + provider icon as an image, preserving its brand color. - `aside.svg` — extracted 2026-08-31 from the installed Aside application, module `Contents/Frameworks/Aside Framework.framework/Versions/1.0.825.1/Libraries/AsideAgentManager/assets/official-brand-symbol-*.js`. It is Aside's own brand symbol, named as such by the vendor and rendered by diff --git a/gui/tests/fr-localization.test.ts b/gui/tests/fr-localization.test.ts index 04a3e03d9e..178795bda5 100644 --- a/gui/tests/fr-localization.test.ts +++ b/gui/tests/fr-localization.test.ts @@ -123,6 +123,9 @@ const INTENTIONAL_ENGLISH = new Set([ "api.clientConfig.clientRaycast", "integrations.tab.omo", "api.clientConfig.clientOmo", + // Cline CLI is the product name, identical in French and English. + "integrations.tab.cline", + "api.clientConfig.clientCline", "models.reasoningEffort.minimal", "models.reasoningEffort.max", "pws.pacingRpmUnit", diff --git a/gui/tests/integrations-api.test.ts b/gui/tests/integrations-api.test.ts index 7b540be78f..873be11b05 100644 --- a/gui/tests/integrations-api.test.ts +++ b/gui/tests/integrations-api.test.ts @@ -16,9 +16,9 @@ import { const originalFetch = globalThis.fetch; -test("DSH, Aside, Raycast and omo are file integration clients", () => { +test("the complete file integration client registry includes Cline", () => { expect(FILE_INTEGRATION_CLIENTS).toEqual([ - "opencode", "pi", "omp", "hermes", "openclaw", "kimi", "gajae", "dsh", "mcode", "zcode", "prime", "aside", "raycast", "omo", + "opencode", "pi", "omp", "hermes", "openclaw", "kimi", "gajae", "dsh", "mcode", "zcode", "prime", "aside", "raycast", "omo", "cline", ]); }); diff --git a/gui/tests/locale-parity.test.ts b/gui/tests/locale-parity.test.ts index 883827baa7..dd8aad9cd8 100644 --- a/gui/tests/locale-parity.test.ts +++ b/gui/tests/locale-parity.test.ts @@ -135,6 +135,9 @@ const ZH_TW_KEEP_ENGLISH: ReadonlySet = new Set([ // "omo" is the product's own lowercase spelling, identical in every locale. "integrations.tab.omo", "api.clientConfig.clientOmo", + // Cline CLI is the product name, kept in English like the other client brands. + "integrations.tab.cline", + "api.clientConfig.clientCline", "integrations.codex.title", // Provider proper nouns kept in English "provider.name.commandCodeAuth", diff --git a/scripts/test-layout/layout.json b/scripts/test-layout/layout.json index d6f5853c55..4345ef3619 100644 --- a/scripts/test-layout/layout.json +++ b/scripts/test-layout/layout.json @@ -11,7 +11,7 @@ "domains": { "providers": { "match": [ - "^(?:aside(?!-profile)|auto|azure|baseten|chutes|cline|command|commandcode|context|cyber|deepinfra|deepseek|digitalocean|exa|featherless|forward|hyperbolic|kimi|meta|mimo|minimax|moonshot|muse|new|nous|novita|nscale|nvidia|opencode|openrouter|qwen38|sambanova|umans|vercel|zcode|zhipu)-" + "^(?:aside(?!-profile)|auto|azure|baseten|chutes|cline(?!-(?:client|writer)(?:[.-]|$))|command|commandcode|context|cyber|deepinfra|deepseek|digitalocean|exa|featherless|forward|hyperbolic|kimi|meta|mimo|minimax|moonshot|muse|new|nous|novita|nscale|nvidia|opencode|openrouter|qwen38|sambanova|umans|vercel|zcode|zhipu)-" ], "children": { "cursor": [ @@ -109,6 +109,7 @@ "clients": { "match": [ "^aside-profile(?!s-routes)", + "^cline-(?:client|writer)(?:[.-]|$)", "^(?:desktop|omp|pi|prime|remote|sync)-" ] }, @@ -1389,4 +1390,4 @@ "web-search", "windows" ] -} \ No newline at end of file +} diff --git a/src/cli/help.ts b/src/cli/help.ts index 70d53f9eb2..8608e5789d 100644 --- a/src/cli/help.ts +++ b/src/cli/help.ts @@ -81,7 +81,7 @@ Usage: ocx memory [--json] Alias of ocx observe memory ocx api-key Alias of ocx access key ocx access External API keys and endpoint information - ocx export --client Print a client config wired to the running proxy (14 clients) + ocx export --client Print a client config wired to the running proxy (15 clients) ocx integration client Enable, disable, inspect or roll back a client integration ocx grok Grok Build model selection and apply ocx system Runtime settings, startup, sync, OpenCodex updates, and Codex CLI inspection diff --git a/tests/gui/integrations-invariants.test.ts b/tests/gui/integrations-invariants.test.ts index 2c89054a3b..6b4339bab1 100644 --- a/tests/gui/integrations-invariants.test.ts +++ b/tests/gui/integrations-invariants.test.ts @@ -80,9 +80,9 @@ afterEach(() => { }); describe("the client registries cannot drift apart", () => { - test("every list of clients holds exactly the same thirteen ids", async () => { + test("every list of clients holds exactly the same fifteen ids", async () => { /* - * Five lists name the same thirteen clients, and two of them are maintained by + * Five lists name the same fifteen clients, and two of them are maintained by * hand: the GUI cannot import the backend registry, because that would * pull node:os and node:path into the browser bundle. A client added * server-side renders no row until someone remembers the tuple, and the @@ -93,7 +93,7 @@ describe("the client registries cannot drift apart", () => { const guiRouting = await import("../../gui/src/app-routing"); const expected = [...EXPORT_CLIENT_IDS].sort(); - expect(expected).toHaveLength(14); + expect(expected).toHaveLength(15); expect([...INTEGRATION_CLIENT_IDS].sort()).toEqual(expected); expect([...gui.CLIENTS].sort()).toEqual(expected);