| 68 | let cache: ColorCache | null = null; |
| 69 | |
| 70 | function resolveColors(): ColorCache { |
| 71 | if (typeof document === 'undefined') { |
| 72 | // Non-browser (tests, SSR) — use fallbacks |
| 73 | return { |
| 74 | themeKey: '__fallback__', |
| 75 | known: new Map(Object.entries(FALLBACK_KNOWN)), |
| 76 | palette: FALLBACK_PALETTE, |
| 77 | }; |
| 78 | } |
| 79 | |
| 80 | const root = document.documentElement; |
| 81 | const themeKey = `${root.dataset.theme ?? ''}_${root.dataset.mode ?? ''}`; |
| 82 | if (cache && cache.themeKey === themeKey) return cache; |
| 83 | |
| 84 | const style = getComputedStyle(root); |
| 85 | |
| 86 | // Read known type colors |
| 87 | const known = new Map<string, string>(); |
| 88 | for (const [type, fallback] of Object.entries(FALLBACK_KNOWN)) { |
| 89 | const varName = `--graph-node-${type.toLowerCase()}`; |
| 90 | const val = style.getPropertyValue(varName).trim(); |
| 91 | known.set(type, val || fallback); |
| 92 | } |
| 93 | |
| 94 | // Read palette |
| 95 | const palette: string[] = []; |
| 96 | for (let i = 0; i < PALETTE_SIZE; i++) { |
| 97 | const val = style.getPropertyValue(`--graph-node-palette-${i}`).trim(); |
| 98 | palette.push(val || FALLBACK_PALETTE[i]); |
| 99 | } |
| 100 | |
| 101 | cache = { themeKey, known, palette }; |
| 102 | return cache; |
| 103 | } |
| 104 | |
| 105 | export function getNodeColor(type: string): string { |
| 106 | const { known, palette } = resolveColors(); |