Skip to content

feat(log-viewer): give each namespace its own stable colour - #943

Merged
lcottercertinia merged 3 commits into
certinia:mainfrom
lukecotter:feat-namespace-palette
Aug 19, 2026
Merged

feat(log-viewer): give each namespace its own stable colour#943
lcottercertinia merged 3 commits into
certinia:mainfrom
lukecotter:feat-namespace-palette

Conversation

@lukecotter

@lukecotter lukecotter commented Aug 18, 2026

Copy link
Copy Markdown
Collaborator

Namespace colours came from the log's namespace order and wrapped at eight, so a ninth namespace re-used the first colour and a package changed colour between logs.

What changes

  • A namespace's name picks its colour, so a package keeps the same colour between logs, and default always holds the first.
  • Five of Wong's colour-blind-safe eight stay as literals. The other three are near-duplicates of them — two blues 7.8° apart, two magentas 11.7°, a green and a mint 14.9° — so any log naming six namespaces showed such a pair. The five that are left sit at least 29° apart.
  • Past the five, colours are generated: hue is settled first, at least 24° from every hue in play while the wheel allows it, since a shared hue reads as the same colour lighter or darker however far apart OKLab says the two are. Chroma comes from what sRGB holds at that hue and lightness, capped at the literals' own widest, so a generated colour is as vivid as a literal rather than a washed-out one. Five lightness bands carry what a crowded wheel can no longer separate by hue.
  • The palette moves out of namespaceTime.ts, which walks the tree, into components/namespacePalette.ts.
  • MAX_SEGMENTS on the namespace bar becomes its own concern at 12, no longer tied to how many colours exist. The tail (N others) is unchanged.

Colours stay literal, as the timeline categories do: they show meaning, not chrome, so they do not follow the host theme.

Measured

Over the whole palette, literals included, at a range of namespace counts:

namespaces closest hue gap closest distance lowest generated chroma
9 24.0° 0.140 0.111
12 24.0° 0.118 0.114
14 17.0° 0.100 0.110
20 13.8° 0.075 0.093
40 6.8° 0.054 0.092

The literals themselves span 0.118–0.170 chroma, so no generated colour reads as a dull one. Building the palette takes 9–41 ms for 12 to 40 namespaces, and the candidate table is built only on the first log holding more namespaces than there are literals.

Known limits

  • A fixed set with no repeats cannot also be perfectly stable. When two names hash to one slot the second takes the next free one, so its colour can differ between logs. Stability is the common case, not a guarantee — noted in the module.
  • Past about twelve namespaces the hue gap has to fall: twelve chips already need 30° each on a 360° wheel. There lightness takes over and same-hue pairs return.
  • The three colours that were dropped are replaced by generated ones, which are not verified colour-blind-safe. That was already true of every namespace past the eighth.

Alternatives considered

  • Gate all eight literals on the same hue clearance. Measured: the gate rejects all three crowders at every namespace count, so the set collapses to the five that ship. It only perturbs the hash, and measures slightly worse at twelve namespaces (22.5° against 24.0°).
  • Drop the literals: a continuous name-hash hue, separated per log by minimum-movement isotonic regression. This reaches the theoretical optimum of 360/n at every count and lifts cross-log recognisability from 44% to 66% at eight namespaces. Rejected on colour-blind safety: simulated deuteranopia separation falls from 0.156 to 0.005, and cannot be fixed, since an evenly-spread wheel collapses red against green.
  • Pin the five literals and relax the rest into the arcs between them. Worse than both — Wong's hues carve very uneven arcs, one only 29° wide, so it gives 9.8° at twelve namespaces against the shipped 18.5°.

Checks

  • 131 suites / 1771 tests pass; tsc -b, eslint, prettier --check clean; production build clean.
  • Checked in the dev host on a log naming thirteen namespaces, light and dark: a colour per namespace, no pair reading as one, and a namespace carrying the same colour on the Timeline and Database bars.

No CHANGELOG entry: the namespace bar and the inspector section are still unreleased (#939, #373), and neither entry mentions colours.

Colours came from the log's namespace order and wrapped at eight, so a
ninth namespace re-used the first colour and a package changed colour
between logs.

A namespace's name now picks its colour, and once the colour-blind-safe
eight are taken the rest are generated across the hues in play, in bands
of eight so lightness separates them as well. The namespace bar's cap
becomes its own concern at 12 segments, no longer tied to how many
colours exist.
Wong's eight hold three near-duplicate pairs: two blues 7.8 degrees
apart, two magentas 11.7, and a green and a mint 14.9. Any log naming
six namespaces showed such a pair, so two packages read as one colour.

Keep the five that sit at least 29 degrees apart and generate the rest,
which the palette holds 24 degrees apart while the wheel allows it.
The gap holds to twelve namespaces and narrows past it. Spreading hues
evenly would hold it further but cannot be colour-blind safe, so the
literals are not replaceable by a generated spread.
@lcottercertinia
lcottercertinia merged commit 48003b6 into certinia:main Aug 19, 2026
7 checks passed
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.

2 participants