From 4015f0af5840b4cbe79335f2ff97b4accc02793e Mon Sep 17 00:00:00 2001 From: Aayam Bansal Date: Fri, 7 Aug 2026 22:16:11 +0800 Subject: [PATCH 1/2] feat: add colorful credential branding --- .../src/components/dialog-settings.tsx | 27 +++ .../src/components/settings/Compute.tsx | 6 +- .../settings/CredentialServices.tsx | 8 +- .../src/components/settings/Credentials.tsx | 4 +- .../src/components/settings/Models.tsx | 11 +- .../components/settings/ProviderLogo.test.ts | 7 +- .../src/components/settings/ProviderLogo.tsx | 211 +++++++++++++++++- 7 files changed, 256 insertions(+), 18 deletions(-) diff --git a/frontend/workspace/src/components/dialog-settings.tsx b/frontend/workspace/src/components/dialog-settings.tsx index ca5f5adb..cdf97d47 100644 --- a/frontend/workspace/src/components/dialog-settings.tsx +++ b/frontend/workspace/src/components/dialog-settings.tsx @@ -278,6 +278,33 @@ const SETTINGS_STYLES = ` gap: 11px; padding: 10px 12px 10px 14px; } +.provider-logo { + color: var(--provider-logo-ink); + border-color: var(--provider-logo-ring); + background: var(--provider-logo-wash); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.42), + 0 3px 10px var(--provider-logo-shadow); + transition: + transform 160ms cubic-bezier(0.2, 0.8, 0.2, 1), + box-shadow 160ms ease; +} +.provider-logo__status { + border: 1px solid color-mix(in srgb, var(--surface-raised-base) 82%, transparent); + box-shadow: 0 0 0 1px color-mix(in srgb, var(--icon-success-base) 20%, transparent); +} +.settings-list-row:hover .provider-logo, +.settings-dialog label:hover .provider-logo { + transform: translateY(-1px) scale(1.03); + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.5), + 0 5px 14px var(--provider-logo-shadow); +} +@media (prefers-reduced-motion: reduce) { + .provider-logo { + transition: none; + } +} .settings-list-copy { min-width: 0; display: flex; diff --git a/frontend/workspace/src/components/settings/Compute.tsx b/frontend/workspace/src/components/settings/Compute.tsx index 33d171c1..2111b6f3 100644 --- a/frontend/workspace/src/components/settings/Compute.tsx +++ b/frontend/workspace/src/components/settings/Compute.tsx @@ -419,7 +419,9 @@ const Compute: Component = () => {

Compute

-

Choose where kernels and research jobs can run safely.

+

+ Run close to home or head to the cloud when the work gets bigger. +

@@ -435,7 +437,7 @@ const Compute: Component = () => {
diff --git a/frontend/workspace/src/components/settings/CredentialServices.tsx b/frontend/workspace/src/components/settings/CredentialServices.tsx index a6cb92e4..40db92ee 100644 --- a/frontend/workspace/src/components/settings/CredentialServices.tsx +++ b/frontend/workspace/src/components/settings/CredentialServices.tsx @@ -160,7 +160,7 @@ export const CredentialServices: Component<{
{service.label} - {service.connected ? "Connected" : service.description} + {service.connected ? "Connected and ready" : service.description}
@@ -226,7 +226,7 @@ export const CredentialServices: Component<{
@@ -145,8 +145,7 @@ export default function Models() { ChatGPT / Codex

- Use supported OpenAI models through your existing ChatGPT plan. OAuth tokens stay in owner-only local - storage. + Already have ChatGPT? Connect it once and use supported OpenAI models here. OAuth tokens stay local.

@@ -158,8 +157,8 @@ export default function Models() { Provider keys

- Bring your own model-provider key. Keys stay in an owner-only local auth file, not the system keychain, - and the provider bills you directly. + Prefer your own account? Add a provider key here. It stays in your owner-only local auth file, and the + provider bills you directly.

diff --git a/frontend/workspace/src/components/settings/ProviderLogo.test.ts b/frontend/workspace/src/components/settings/ProviderLogo.test.ts index f25d1873..c07fd665 100644 --- a/frontend/workspace/src/components/settings/ProviderLogo.test.ts +++ b/frontend/workspace/src/components/settings/ProviderLogo.test.ts @@ -1,6 +1,6 @@ import { describe, expect, test } from "bun:test" import { MODEL_PROVIDERS } from "./model-providers" -import { providerLogoSource } from "./ProviderLogo" +import { PROVIDER_LOGO_IDS, providerLogoSource, providerLogoTheme } from "./ProviderLogo" const CREDENTIALS = [ "aws", @@ -27,6 +27,11 @@ describe("provider logos", () => { for (const id of CREDENTIALS) expect(providerLogoSource(id).kind).not.toBe("fallback") }) + test("gives every built-in provider its own colorful treatment", () => { + const fallback = providerLogoTheme("custom:lab-service") + for (const id of PROVIDER_LOGO_IDS) expect(providerLogoTheme(id)).not.toEqual(fallback) + }) + test("uses a monogram only for user-defined services", () => { expect(providerLogoSource("custom:lab-service").kind).toBe("fallback") }) diff --git a/frontend/workspace/src/components/settings/ProviderLogo.tsx b/frontend/workspace/src/components/settings/ProviderLogo.tsx index 2639b75c..cc933106 100644 --- a/frontend/workspace/src/components/settings/ProviderLogo.tsx +++ b/frontend/workspace/src/components/settings/ProviderLogo.tsx @@ -13,6 +13,13 @@ type Source = | { kind: "image"; src: string } | { kind: "fallback" } +type Theme = { + ink: string + wash: string + ring: string + shadow: string +} + const OPENALEX = "data:image/png;base64,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" const TINKER = @@ -53,6 +60,196 @@ const VECTORS = { }, } satisfies Record +const THEMES: Record = { + anthropic: { + ink: "#c15f3c", + wash: "linear-gradient(145deg, #fff4ee, #f5d8c9)", + ring: "rgba(193, 95, 60, 0.24)", + shadow: "rgba(193, 95, 60, 0.16)", + }, + openai: { + ink: "#087f6d", + wash: "linear-gradient(145deg, #effcf8, #ccefe5)", + ring: "rgba(8, 127, 109, 0.22)", + shadow: "rgba(8, 127, 109, 0.14)", + }, + "openai-codex": { + ink: "#087f6d", + wash: "linear-gradient(145deg, #effcf8, #ccefe5)", + ring: "rgba(8, 127, 109, 0.22)", + shadow: "rgba(8, 127, 109, 0.14)", + }, + google: { + ink: "#ffffff", + wash: "linear-gradient(145deg, #4285f4 0%, #6f83e8 34%, #a66dcc 58%, #ea6e85 78%, #fbbc04 100%)", + ring: "rgba(66, 133, 244, 0.28)", + shadow: "rgba(112, 94, 214, 0.2)", + }, + gemini: { + ink: "#ffffff", + wash: "linear-gradient(145deg, #4285f4 0%, #8f6ed5 50%, #d96570 100%)", + ring: "rgba(113, 93, 216, 0.28)", + shadow: "rgba(113, 93, 216, 0.2)", + }, + xai: { + ink: "#5e53aa", + wash: "linear-gradient(145deg, #f6f3ff, #dfdcf8)", + ring: "rgba(94, 83, 170, 0.22)", + shadow: "rgba(94, 83, 170, 0.14)", + }, + meta: { + ink: "#0866ff", + wash: "linear-gradient(145deg, #f1f7ff, #d5e6ff)", + ring: "rgba(8, 102, 255, 0.22)", + shadow: "rgba(8, 102, 255, 0.14)", + }, + openrouter: { + ink: "#6956d9", + wash: "linear-gradient(145deg, #f7f4ff, #e1dcff)", + ring: "rgba(105, 86, 217, 0.22)", + shadow: "rgba(105, 86, 217, 0.15)", + }, + togetherai: { + ink: "#d94f2b", + wash: "linear-gradient(145deg, #fff6ef, #ffdccc)", + ring: "rgba(217, 79, 43, 0.22)", + shadow: "rgba(217, 79, 43, 0.14)", + }, + groq: { + ink: "#f55036", + wash: "linear-gradient(145deg, #fff5f1, #ffd9d1)", + ring: "rgba(245, 80, 54, 0.22)", + shadow: "rgba(245, 80, 54, 0.14)", + }, + "fireworks-ai": { + ink: "#cf496f", + wash: "linear-gradient(145deg, #fff5f8, #ffd7e3)", + ring: "rgba(207, 73, 111, 0.22)", + shadow: "rgba(207, 73, 111, 0.14)", + }, + mistral: { + ink: "#e95024", + wash: "linear-gradient(145deg, #fff6d9, #ffbc4b 58%, #f47421)", + ring: "rgba(233, 80, 36, 0.24)", + shadow: "rgba(244, 116, 33, 0.18)", + }, + deepseek: { + ink: "#4d6bfe", + wash: "linear-gradient(145deg, #f2f5ff, #d6defe)", + ring: "rgba(77, 107, 254, 0.22)", + shadow: "rgba(77, 107, 254, 0.14)", + }, + moonshotai: { + ink: "#565bd8", + wash: "linear-gradient(145deg, #f4f4ff, #dcdcff)", + ring: "rgba(86, 91, 216, 0.22)", + shadow: "rgba(86, 91, 216, 0.14)", + }, + zai: { + ink: "#5a4ee0", + wash: "linear-gradient(145deg, #f7f4ff, #ddd8ff)", + ring: "rgba(90, 78, 224, 0.22)", + shadow: "rgba(90, 78, 224, 0.14)", + }, + cerebras: { + ink: "#e85828", + wash: "linear-gradient(145deg, #fff4ec, #ffd8c4)", + ring: "rgba(232, 88, 40, 0.22)", + shadow: "rgba(232, 88, 40, 0.14)", + }, + perplexity: { + ink: "#167c80", + wash: "linear-gradient(145deg, #effafa, #cdebed)", + ring: "rgba(22, 124, 128, 0.22)", + shadow: "rgba(22, 124, 128, 0.14)", + }, + aws: { + ink: "#ff9900", + wash: "linear-gradient(145deg, #27384f, #17263a)", + ring: "rgba(255, 153, 0, 0.3)", + shadow: "rgba(19, 41, 69, 0.24)", + }, + gcp: { + ink: "#ffffff", + wash: "linear-gradient(145deg, #4285f4 0%, #34a853 42%, #fbbc05 70%, #ea4335 100%)", + ring: "rgba(66, 133, 244, 0.28)", + shadow: "rgba(66, 133, 244, 0.18)", + }, + azure: { + ink: "#0078d4", + wash: "linear-gradient(145deg, #eef9ff, #cfeeff)", + ring: "rgba(0, 120, 212, 0.22)", + shadow: "rgba(0, 120, 212, 0.14)", + }, + nvidia: { + ink: "#76b900", + wash: "linear-gradient(145deg, #f6ffe8, #dff1bd)", + ring: "rgba(118, 185, 0, 0.24)", + shadow: "rgba(84, 132, 0, 0.14)", + }, + modal: { + ink: "#7057d7", + wash: "linear-gradient(145deg, #f8f4ff, #e5dbff)", + ring: "rgba(112, 87, 215, 0.22)", + shadow: "rgba(112, 87, 215, 0.14)", + }, + github: { + ink: "#7656c9", + wash: "linear-gradient(145deg, #f7f3ff, #e3d9fb)", + ring: "rgba(118, 86, 201, 0.22)", + shadow: "rgba(118, 86, 201, 0.14)", + }, + literature: { + ink: "#3f7ecb", + wash: "linear-gradient(145deg, #f1f8ff, #d3e9ff)", + ring: "rgba(63, 126, 203, 0.22)", + shadow: "rgba(63, 126, 203, 0.14)", + }, + openalex: { + ink: "#8457d6", + wash: "linear-gradient(145deg, #faf5ff, #e5d6fa)", + ring: "rgba(132, 87, 214, 0.22)", + shadow: "rgba(132, 87, 214, 0.14)", + }, + huggingface: { + ink: "#f3a900", + wash: "linear-gradient(145deg, #fff9df, #ffe791)", + ring: "rgba(243, 169, 0, 0.25)", + shadow: "rgba(243, 169, 0, 0.16)", + }, + tinker: { + ink: "#d76b52", + wash: "linear-gradient(145deg, #fff5ef, #f5d9ce)", + ring: "rgba(215, 107, 82, 0.22)", + shadow: "rgba(215, 107, 82, 0.14)", + }, + wandb: { + ink: "#d79600", + wash: "linear-gradient(145deg, #fff9dc, #ffe27a)", + ring: "rgba(215, 150, 0, 0.25)", + shadow: "rgba(215, 150, 0, 0.16)", + }, + pinecone: { + ink: "#635bdb", + wash: "linear-gradient(145deg, #f5f4ff, #dddafe)", + ring: "rgba(99, 91, 219, 0.22)", + shadow: "rgba(99, 91, 219, 0.14)", + }, + langsmith: { + ink: "#7fc8ff", + wash: "linear-gradient(145deg, #e7f5ff, #b9ddff)", + ring: "rgba(91, 168, 226, 0.24)", + shadow: "rgba(91, 168, 226, 0.16)", + }, +} + +const FALLBACK: Theme = { + ink: "#6c63b8", + wash: "linear-gradient(145deg, #f7f5ff, #e4e0f6)", + ring: "rgba(108, 99, 184, 0.2)", + shadow: "rgba(108, 99, 184, 0.12)", +} + const SOURCES: Record = { anthropic: { kind: "provider", id: "anthropic" }, openai: { kind: "provider", id: "openai" }, @@ -92,6 +289,10 @@ export function providerLogoSource(id: string) { return SOURCES[id] ?? ({ kind: "fallback" } as const) } +export function providerLogoTheme(id: string) { + return THEMES[id] ?? FALLBACK +} + const Mark: Component<{ source: Source; label: string; small?: boolean }> = (props) => { if (props.source.kind === "provider") { return