Files
rengine/shared/utils/color.py
T
Yogesh OjhaandClaude Opus 5 2cb8d73280 style: strip explanatory comments across the codebase
The comment rule in CLAUDE.md is one line maximum, never narration: no
multi-line blocks, no module-header explainers, no sentences that explain or
justify the code to the reader. The tree had drifted a long way from it, and
most of the drift is mine.

Removed:
  232  lines of multi-line comment blocks
  203  single-line comments that argued instead of stating
  154  comments joined by so/because/otherwise/rather-than
  143  narrating comments in the frontend
  112  test docstrings (the test name already says what it asserts)
   25  test module docstrings and 25 decorative section dividers

Collapsed:
  324  multi-line docstrings to one factual line
  271  one-line docstrings that had a justifying clause bolted on

Kept: functional directives, section dividers, short field labels, alembic
revision headers, and the eight FastAPI route docstrings that render in /docs.
Where a fact was load-bearing it is in CLAUDE.md, which is where it belongs.

284 python tests and 34 frontend tests pass, ruff and ruff-format clean across
ten packages, svelte-check 0 errors, prettier and eslint clean, app.main imports.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dKUV96WkQQETZBMoAG98C
2026-09-11 14:11:23 +05:30

117 lines
3.6 KiB
Python

"""sRGB ↔ OKLCH."""
from __future__ import annotations
import math
_M1 = (
(0.4122214708, 0.5363325363, 0.0514459929),
(0.2119034982, 0.6806995451, 0.1073969566),
(0.0883024619, 0.2817188376, 0.6299787005),
)
_M2 = (
(0.2104542553, 0.7936177850, -0.0040720468),
(1.9779984951, -2.4285922050, 0.4505937099),
(0.0259040371, 0.7827717662, -0.8086757660),
)
_M2_INV = (
(1.0, 0.3963377774, 0.2158037573),
(1.0, -0.1055613458, -0.0638541728),
(1.0, -0.0894841775, -1.2914855480),
)
_M1_INV = (
(4.0767416621, -3.3077115913, 0.2309699292),
(-1.2684380046, 2.6097574011, -0.3413193965),
(-0.0041960863, -0.7034186147, 1.7076147010),
)
_LINEAR_CUT = 0.04045
_SRGB_CUT = 0.0031308
_SHORT_HEX = 3
_FULL_HEX = 6
_DARK_BELOW = 0.5
def _to_linear(value: float) -> float:
return value / 12.92 if value <= _LINEAR_CUT else ((value + 0.055) / 1.055) ** 2.4
def _to_srgb(value: float) -> float:
return value * 12.92 if value <= _SRGB_CUT else 1.055 * value ** (1 / 2.4) - 0.055
def parse_hex(value: str) -> tuple[float, float, float] | None:
text = (value or "").strip().lstrip("#")
if len(text) == _SHORT_HEX:
text = "".join(c * 2 for c in text)
if len(text) != _FULL_HEX:
return None
try:
number = int(text, 16)
except ValueError:
return None
return (
(number >> 16 & 255) / 255,
(number >> 8 & 255) / 255,
(number & 255) / 255,
)
def to_oklch(value: str) -> tuple[float, float, float] | None:
rgb = parse_hex(value)
if rgb is None:
return None
lin = [_to_linear(c) for c in rgb]
lms = [sum(row[i] * lin[i] for i in range(3)) ** (1 / 3) for row in _M1]
lab = [sum(row[i] * lms[i] for i in range(3)) for row in _M2]
chroma = math.hypot(lab[1], lab[2])
hue = math.degrees(math.atan2(lab[2], lab[1])) % 360
return lab[0], chroma, hue
def from_oklch(lightness: float, chroma: float, hue: float) -> str:
radians = math.radians(hue)
lab = (lightness, chroma * math.cos(radians), chroma * math.sin(radians))
lms = [sum(row[i] * lab[i] for i in range(3)) ** 3 for row in _M2_INV]
lin = [sum(row[i] * lms[i] for i in range(3)) for row in _M1_INV]
parts = [max(0, min(255, round(_to_srgb(c) * 255))) for c in lin]
return "#{:02x}{:02x}{:02x}".format(*parts)
def restate(value: str, lightness: float, chroma: float, fallback: str = "") -> str:
"""The same hue at a stated lightness and chroma."""
parsed = to_oklch(value)
if parsed is None:
return fallback or value
_, source_chroma, hue = parsed
return from_oklch(lightness, min(source_chroma * chroma, 0.37), hue)
def tint(value: str, lightness: float = 0.968) -> str:
"""A wash of the colour, for a badge or a callout ground."""
return restate(value, lightness, 0.20, fallback=value)
def ink(value: str, lightness: float = 0.44) -> str:
"""The colour darkened until it can carry text on its own tint."""
return restate(value, lightness, 0.85, fallback=value)
def mix(value: str, other: str, amount: float) -> str:
"""Blend two colours in linear light."""
first, second = parse_hex(value), parse_hex(other)
if first is None or second is None:
return value
blended = [
_to_srgb(_to_linear(a) * (1 - amount) + _to_linear(b) * amount)
for a, b in zip(first, second, strict=True)
]
parts = [max(0, min(255, round(c * 255))) for c in blended]
return "#{:02x}{:02x}{:02x}".format(*parts)
def is_dark(value: str) -> bool:
parsed = to_oklch(value)
return parsed is not None and parsed[0] < _DARK_BELOW