Skip to content

fix(frontend): resolve Claude family colors before the generic claude key, on the TUI orange ramp#808

Merged
junhoyeo merged 3 commits into
mainfrom
fix/opus-claude-prefix-color
Jul 3, 2026
Merged

fix(frontend): resolve Claude family colors before the generic claude key, on the TUI orange ramp#808
junhoyeo merged 3 commits into
mainfrom
fix/opus-claude-prefix-color

Conversation

@junhoyeo

@junhoyeo junhoyeo commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Problem

Follow-up to #806, which added a fable entry but didn't actually make Fable match the flagship tier on the live site. Checking production (tokscale.ai/u/junhoyeo, models tab) after deploy:

Root cause: getModelColor is first-match over insertion order, and every real Opus id carries the claude- prefix (claude-opus-4-6, claude-opus-4-5-thinking-high, …), so the generic "claude" key matched before "opus" ever could. The "opus" entry was dead code — same bug hit Haiku (claude-haiku-4-5 rendered amber instead of green).

Additionally, the red #DC2626 from #806 was inferred from that dead "opus" entry, not an actual design decision — Claude family models should be orange.

Fix

Family Color
fable #DA7756 (darkest)
opus #DF886B
sonnet #E39980
haiku #E8AA95
generic claude #ECB8A6 (palest)
  • Extract the map + getModelColor into modelColors.ts so the ordering contract is unit-testable without pulling styled-components/next into vitest.
  • Regression tests lock in: family colors resolve despite the claude- prefix, Fable one step darker than Opus, and the ramp's luminance is strictly increasing down the tiers.

Verification

  • vitest run __tests__/components/modelColors.test.ts — 4/4 pass
  • tsc --noEmit — no new errors (pre-existing groupMemberRoleRoute.test.ts errors untouched)
  • Production dot colors above were read directly off the deployed DOM via browser inspection

Companion PR: #810 (TUI shade ranking by family/version hierarchy).

The MODEL_COLORS lookup is first-match, and every real Opus/Haiku id
carries the "claude-" prefix (claude-opus-4-6, claude-haiku-4-5), so the
generic "claude" key swallowed them all: Opus rendered amber #D97706
instead of red #DC2626, and Haiku amber instead of green. This is why
Fable (fixed to red in #806) still didn't match Opus on the live site —
Opus itself was never red. Verified against production: every
claude-opus-* dot rendered rgb(217,119,6).

Reorder so family keys (fable/opus/sonnet/haiku) precede the "claude"
fallback, and extract the map into modelColors.ts so the ordering
contract is unit-testable without pulling styled-components into vitest.

Constraint: getModelColor iterates Object.entries insertion order, first substring match wins
Rejected: longest-match-wins lookup | more code for the same result; ordering + regression test is enough
Confidence: high
Scope-risk: narrow
Directive: keep the generic "claude" key last among Anthropic keys — the test file locks this in
@vercel

vercel Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
tokscale Ready Ready Preview, Comment Jul 3, 2026 5:05am

Request Review

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread packages/frontend/src/components/profile/modelColors.ts Outdated
junhoyeo added a commit that referenced this pull request Jul 3, 2026
Found while auditing every model-color path for the Fable/Opus color
fixes (#806, #808). Each item has zero references outside its own
definition (verified by grep across all packages, eslint, and knip) and
has been unused for 6+ months:

- ProviderLogo.tsx: superseded by SourceLogo.tsx, never imported
- filterByModel (utils.ts): relic of a per-model graph filter that no
  UI calls; its shared helpers stay alive via filterByClient
- hexToRgb (utils.ts): unused; embedShared.ts has its own private copy
- SOURCE_TEXT_COLORS (constants.ts): added with the droid palette but
  never consumed

Rejected: removing ProfileModels' tag-list fallback | reachable for legacy profiles whose contributions lack per-model data (API returns empty modelUsage)
Rejected: pruning knip's 40+ other unused exports | unrelated to the model-color surface; separate concern
Confidence: high
Scope-risk: narrow
Not-tested: profiles with empty modelUsage (fallback path kept, not exercised in tests)
Fable/Opus red (#DC2626) was inferred from the dead "opus" map entry,
not a real design decision. Align the Anthropic family colors with the
TUI's ANTHROPIC_SHADES ramp instead: brightness encodes the family
hierarchy — fable #DA7756 (darkest), opus #DF886B, sonnet #E39980,
haiku #E8AA95, generic claude #ECB8A6 (palest).

Constraint: fable sits one step darker than opus, per hierarchy fable > opus > sonnet > haiku
Rejected: keeping #DC2626 red for fable/opus | flagship Claude models are brand-orange, red belonged to no design
Rejected: keeping haiku green (#059669) | full ramp alignment chosen so web matches TUI semantics
Confidence: high
Scope-risk: narrow
Directive: keep this ramp in sync with ANTHROPIC_SHADES in crates/tokscale-cli/src/tui/ui/widgets.rs
@junhoyeo junhoyeo changed the title fix(frontend): match model family colors before the generic claude key fix(frontend): resolve Claude family colors before the generic claude key, on the TUI orange ramp Jul 3, 2026
…ings

Review finding (P2): includes() matching painted unrelated ids containing
"fable"/"opus" fragments (e.g. "unfabled-x") in Anthropic colors. Keys now
match whole alphanumeric tokens, or a token's alpha prefix when followed
directly by a version digit ("gpt4", "qwen2.5") — mirroring the TUI's
delimited matching in get_provider_from_model.

Constraint: "chatgpt-4o-latest" no longer contains a bare "gpt" token, so a "chatgpt" key was added
Rejected: regex word-boundary per key | token split once vs building 14 regexes per call
Confidence: high
Scope-risk: narrow
@junhoyeo junhoyeo merged commit 67f4c53 into main Jul 3, 2026
7 checks passed
@junhoyeo junhoyeo deleted the fix/opus-claude-prefix-color branch July 3, 2026 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant