Files
HellionChat/scripts/verify-colour-channels.sh
T
JonKazama-Hellion 3e46600fc3 fix(style): the popup text fed RGBA into the contrast helper -- and a guard so this stops recurring
Fifth and sixth occurrences of the same defect in one day, this time in PopupRow
and three spots in the lab. The menu entries and the channel picker were
unreadable at rest on the green theme and only became legible on hover, because
the hover lerp pulls toward a correctly converted accent -- which is exactly the
symptom the tester reported.

The shape of the mistake is always identical: a raw ThemeColors member (RGBA)
handed to EnsureContrast (ABGR). The compiler cannot see it, both layouts are
uint, and a stray extra RgbaToAbgr around the call makes the result look
plausible while measuring a contrast between two colours that are never on
screen.

So this commit is mostly the guard. preflight Block G runs
scripts/verify-colour-channels.sh, which flags any raw theme member in either
argument of EnsureContrast across the UI tree. Falsified before trusting: broken
deliberately, it goes red; and on its very first real run it caught three
offenders in the lab that a hand-rolled grep had missed minutes earlier.
2026-08-19 19:09:22 +02:00

61 lines
2.1 KiB
Bash
Executable File

#!/usr/bin/env bash
# verify-colour-channels.sh — Block G of preflight.
#
# EnsureContrast works in ABGR. ThemeColors fields are RGBA. Feeding one into
# the other swaps red and blue on both arguments, measures a contrast between
# two colours that are never on screen, and returns a value in the wrong order
# on top — and because RgbaToAbgr is an involution, a stray extra conversion
# around the call makes it LOOK plausible while being wrong.
#
# This happened four times on 2026-08-19 alone (channel header, ghost buttons,
# pill text, popup rows), each found only by a human staring at an unreadable
# window. The compiler cannot catch it — both layouts are uint. This script
# catches the one shape every occurrence shared: a raw ThemeColors member as a
# direct argument to EnsureContrast.
#
# Legitimate calls convert first (RgbaToAbgr(...)) or pass values that are
# already ABGR (fields/locals named *Abgr, palette.Abgr(...)).
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
fail() { echo "verify-colour-channels: FAIL — $1" >&2; exit 1; }
ok() { echo "verify-colour-channels: OK — $1"; }
HITS="$(python3 - "$ROOT/HellionChat" <<'PY'
import re
import sys
from pathlib import Path
root = Path(sys.argv[1])
# A ThemeColors member (via any local alias) fed straight into EnsureContrast,
# in either argument position, spanning line breaks. Members already in ABGR
# end in "Abgr" and are exempt.
pattern = re.compile(
r"EnsureContrast\(\s*"
r"(?:[A-Za-z_][\w.]*\.)?(?:Colors|c|colors)\.(?!\w*Abgr\b)\w+\s*,"
r"|EnsureContrast\([^;]{0,200}?,\s*"
r"(?:[A-Za-z_][\w.]*\.)?(?:Colors|c|colors)\.(?!\w*Abgr\b)\w+\s*[,)]",
re.S,
)
hits = []
for f in root.rglob("*.cs"):
if "obj" in f.parts or "bin" in f.parts:
continue
text = f.read_text(encoding="utf-8")
for m in pattern.finditer(text):
line = text.count("\n", 0, m.start()) + 1
hits.append(f"{f.relative_to(root.parent)}:{line}")
print("\n".join(hits))
PY
)"
if [ -n "$HITS" ]; then
fail "raw ThemeColors member fed into EnsureContrast (expects ABGR — wrap in RgbaToAbgr):
$HITS"
fi
ok "no raw RGBA theme members reach EnsureContrast"