fix(frontend): resolve Claude family colors before the generic claude key, on the TUI orange ramp#808
Merged
Merged
Conversation
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
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Contributor
There was a problem hiding this comment.
1 issue found across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
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
…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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Follow-up to #806, which added a
fableentry but didn't actually make Fable match the flagship tier on the live site. Checking production (tokscale.ai/u/junhoyeo, models tab) after deploy:claude-fable-5→ red (the fix(frontend): render Fable models in Opus red on profile overview #806 entry works)claude-opus-*id → amber#D97706(the genericclaudecolor)Root cause:
getModelColoris first-match over insertion order, and every real Opus id carries theclaude-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-5rendered amber instead of green).Additionally, the red
#DC2626from #806 was inferred from that dead"opus"entry, not an actual design decision — Claude family models should be orange.Fix
MODEL_COLORSso family keys (fable,opus,sonnet,haiku) precede the generic"claude"fallback.ANTHROPIC_SHADESincrates/tokscale-cli/src/tui/ui/widgets.rs), so brightness encodes the family hierarchy — matching fix(tui): rank model shades by Claude family/version hierarchy instead of spend #810's TUI shade ranking:#DA7756(darkest)#DF886B#E39980#E8AA95#ECB8A6(palest)getModelColorintomodelColors.tsso the ordering contract is unit-testable without pulling styled-components/next into vitest.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 passtsc --noEmit— no new errors (pre-existinggroupMemberRoleRoute.test.tserrors untouched)Companion PR: #810 (TUI shade ranking by family/version hierarchy).