colors.ts (3031B)
1 /** 2 * Shared model color palette for all charts. 3 * 10 perceptually distinct colors, colorblind-friendly, designed for dark backgrounds. 4 * Hues spaced ~36° apart on the color wheel, lightness 60-75% for readability. 5 */ 6 7 // Anthropic models (cool tones) 8 // GLM models (warm/earth tones) 9 // Future models get auto-assigned from the overflow palette 10 11 export const MODEL_COLORS: Record<string, string> = { 12 // Anthropic (versioned + legacy aliases) 13 "haiku-4.5": "hsl(193 44% 67%)", // cyan 14 "sonnet-4.6": "hsl(40 71% 73%)", // gold 15 "opus-4.6": "hsl(311 24% 63%)", // mauve 16 haiku: "hsl(193 44% 67%)", // legacy alias 17 sonnet: "hsl(40 71% 73%)", // legacy alias 18 opus: "hsl(311 24% 63%)", // legacy alias 19 20 // GLM (Z.AI) 21 "glm-4.5-air": "hsl(145 35% 58%)", // sage green 22 "glm-4.7": "hsl(220 50% 65%)", // steel blue 23 "glm-5.1": "hsl(15 65% 65%)", // coral 24 25 // Qwen (OpenRouter) 26 "qwen-3.6-plus": "hsl(270 40% 68%)", // lavender 27 28 // Google 29 "gemma-4-26b": "hsl(60 50% 62%)", // olive 30 31 // MiniMax 32 "minimax-m2.7": "hsl(340 50% 65%)", // rose 33 // Moonshot 34 "kimi-k2.5": "hsl(170 40% 55%)", // teal 35 36 // Future slots 37 "slot-7": "hsl(270 40% 68%)", // lavender 38 "slot-8": "hsl(60 50% 62%)", // olive 39 "slot-9": "hsl(340 50% 65%)", // rose 40 "slot-10": "hsl(170 40% 55%)", // teal 41 }; 42 43 // Same colors in hsla format (without closing opacity) for fill regions 44 export const MODEL_FILL_COLORS: Record<string, string> = { 45 haiku: "hsla(193, 44%, 67%,", 46 sonnet: "hsla(40, 71%, 73%,", 47 opus: "hsla(311, 24%, 63%,", 48 "glm-4.5-air": "hsla(145, 35%, 58%,", 49 "glm-4.7": "hsla(220, 50%, 65%,", 50 "glm-5.1": "hsla(15, 65%, 65%,", 51 "slot-7": "hsla(270, 40%, 68%,", 52 "slot-8": "hsla(60, 50%, 62%,", 53 "slot-9": "hsla(340, 50%, 65%,", 54 "slot-10": "hsla(170, 40%, 55%,", 55 }; 56 57 // Ordered list for auto-assigning to unknown models 58 const OVERFLOW_HUES = [270, 60, 340, 170, 95, 250, 30, 0, 200, 130]; 59 let _nextOverflow = 0; 60 61 export function getModelColor(model: string): string { 62 if (MODEL_COLORS[model]) return MODEL_COLORS[model]; 63 // Auto-assign from overflow palette 64 const hue = OVERFLOW_HUES[_nextOverflow % OVERFLOW_HUES.length]; 65 _nextOverflow++; 66 const color = `hsl(${hue} 40% 65%)`; 67 MODEL_COLORS[model] = color; 68 MODEL_FILL_COLORS[model] = `hsla(${hue}, 40%, 65%,`; 69 return color; 70 } 71 72 export function getModelFillColor(model: string): string { 73 if (MODEL_FILL_COLORS[model]) return MODEL_FILL_COLORS[model]; 74 getModelColor(model); // ensure it's assigned 75 return MODEL_FILL_COLORS[model]; 76 } 77 78 // Sort order: Anthropic by tier, then GLM by version, then alphabetical 79 const MODEL_ORDER: Record<string, number> = { 80 "haiku-4.5": 1, "sonnet-4.6": 2, "opus-4.6": 3, 81 haiku: 1, sonnet: 2, opus: 3, // legacy aliases 82 "glm-4.5-air": 10, "glm-4.7": 11, "glm-5.1": 12, 83 }; 84 85 export function modelSortOrder(model: string): number { 86 return MODEL_ORDER[model] ?? 99; 87 }