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