81 lines
2.4 KiB
TypeScript
81 lines
2.4 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { readFileSync } from "node:fs";
|
|
import { resolve } from "node:path";
|
|
import { test } from "vitest";
|
|
|
|
const { getXtermTheme } = await import("../../web/lib/xterm-theme.ts");
|
|
|
|
function hexToRgb(hex: string): [number, number, number] {
|
|
const normalized = hex.replace("#", "");
|
|
const value =
|
|
normalized.length === 3
|
|
? normalized
|
|
.split("")
|
|
.map((char) => char + char)
|
|
.join("")
|
|
: normalized;
|
|
const int = Number.parseInt(value, 16);
|
|
return [(int >> 16) & 255, (int >> 8) & 255, int & 255];
|
|
}
|
|
|
|
function srgbToLinear(channel: number): number {
|
|
const normalized = channel / 255;
|
|
return normalized <= 0.04045
|
|
? normalized / 12.92
|
|
: ((normalized + 0.055) / 1.055) ** 2.4;
|
|
}
|
|
|
|
function contrastRatio(foreground: string, background: string): number {
|
|
const luminance = (hex: string) => {
|
|
const [r, g, b] = hexToRgb(hex).map(srgbToLinear);
|
|
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
};
|
|
const [lighter, darker] = [luminance(foreground), luminance(background)].sort(
|
|
(a, b) => b - a,
|
|
);
|
|
return (lighter + 0.05) / (darker + 0.05);
|
|
}
|
|
|
|
test("light xterm palette keeps warning and ANSI white entries readable", () => {
|
|
const theme = getXtermTheme(false);
|
|
|
|
assert.ok(
|
|
contrastRatio(theme.foreground, theme.background) >= 14,
|
|
"foreground should remain highly legible",
|
|
);
|
|
assert.ok(
|
|
contrastRatio(theme.yellow, theme.background) >= 4.5,
|
|
"yellow should meet readable contrast",
|
|
);
|
|
assert.ok(
|
|
contrastRatio(theme.brightYellow, theme.background) >= 4.5,
|
|
"bright yellow should meet readable contrast",
|
|
);
|
|
assert.ok(
|
|
contrastRatio(theme.white, theme.background) >= 4.5,
|
|
"white should stay readable on light background",
|
|
);
|
|
assert.ok(
|
|
contrastRatio(theme.brightWhite, theme.background) >= 4.5,
|
|
"bright white should stay readable on light background",
|
|
);
|
|
});
|
|
|
|
test("terminal components share the central xterm theme helper", () => {
|
|
const shellSource = readFileSync(
|
|
resolve(import.meta.dirname, "../../web/components/sf/shell-terminal.tsx"),
|
|
"utf8",
|
|
);
|
|
const mainSource = readFileSync(
|
|
resolve(
|
|
import.meta.dirname,
|
|
"../../web/components/sf/main-session-terminal.tsx",
|
|
),
|
|
"utf8",
|
|
);
|
|
|
|
assert.match(shellSource, /from "@\/lib\/xterm-theme"/);
|
|
assert.match(mainSource, /from "@\/lib\/xterm-theme"/);
|
|
assert.doesNotMatch(shellSource, /const XTERM_LIGHT_THEME =/);
|
|
assert.doesNotMatch(mainSource, /const XTERM_LIGHT_THEME =/);
|
|
});
|