fix(tui): body text and the codex mark read on a light terminal (#2499)

`rig tui` body text now reads on a light terminal as well as a dark one, with nothing to configure. The `bright` token, used for a slice's intent, requirements and proof text and for most plain text, and `markInk`, the codex mark's `_`, now use the terminal's own default foreground (SGR 39) in every colour mode wherever the TUI doesn't paint its own background. On a background the TUI paints itself (selected rows, the terminal mark, accent bars) they keep their fixed light ink, so those stay legible, and inverse flash rows now swap the terminal's own colours. Dark terminals look the same to the eye. Limit: in 16-colour mode the selected-row wash isn't painted, an existing issue handled separately. Reported, with the fix, by @adam-nectar.
This commit is contained in:
Mike Schwarz authored and GitHub committed 2026-11-04 14:47:48 -08:00
1 parent e10b8014f6
commit 7b02076427
4 files changed
+32 -3

No files matched your search

+6 -1
View File
@@ -69,6 +69,10 @@ const PALETTE: Record<Token, [[number, number, number], number, number]> = {
codexBlue: [[104, 103, 170], 61, 34],
};
// #2498: these light inks vanish on a light terminal. On the terminal's own background they use its default
// foreground (SGR 39), which reads on either; on a background the TUI paints itself, they keep the ink chosen for it.
const TERMINAL_INK: ReadonlySet<Token> = new Set(["bright", "markInk"]);
export interface Style {
/** wrap text in the token's SGR (plus bold/inverse); identity in "none" mode */
paint(token: Token, text: string, opts?: { bold?: boolean; inverse?: boolean; bg?: Token; blink?: boolean }): string;
@@ -83,7 +87,8 @@ export function createStyle(mode: ColorMode = detectColorMode()): Style {
if (opts?.blink) parts.push("5");
if (opts?.inverse) parts.push("7");
const [rgb, x256, basic] = PALETTE[token];
if (mode === "truecolor") parts.push(`38;2;${rgb[0]};${rgb[1]};${rgb[2]}`);
if (!opts?.bg && TERMINAL_INK.has(token)) parts.push("39");
else if (mode === "truecolor") parts.push(`38;2;${rgb[0]};${rgb[1]};${rgb[2]}`);
else if (mode === "256") parts.push(`38;5;${x256}`);
else parts.push(String(basic));
if (opts?.bg) {
+1 -1
View File
@@ -39,7 +39,7 @@ const MARK_SGR = {
clawdBodyFg: "38;2;173;103;85", // clawd body ink (downsample forms)
clawdEyeFg: "38;2;24;24;24", // clawd eyes #181818
terminalCellBg: "48;2;12;10;9", // terminal mark dark cell
markInkFg: "38;2;250;250;249", // codex `>_` light ink
markInkFg: "\x1b[1;39m_", // codex `>_` ink: the terminal's default foreground (#2498)
codexBlueFg: "38;2;104;103;170", // OFFICIAL sampled #6867aa
} as const;
const MARK_GLYPHS = /[▘▝▖▗▚▞▐▌█▀▄╹]|>_|></; // incl. the picks-v4 eyes pair
@@ -24,7 +24,7 @@ describe("S06 installed presentation contracts", () => {
const line = lines.find(l => l.includes("application unverified"))!;
expect(line).toBeTruthy();
expect(line).not.toContain("38;2;152;195;121");
expect(line).toContain("38;2;232;234;240");
expect(line).toContain("\x1b[39m");
});
it.each([[140, 42], [80, 24]])("keeps every selected Help command and its registry example visible at %ix%i", (cols, rows) => {
const snap = emptySnapshot();
+24
View File
@@ -0,0 +1,24 @@
import { describe, expect, it } from "vitest";
import { createStyle } from "../src/theme.js";
// #2498: light ink on the terminal's own background vanishes on a light theme. Body text (`bright`) and the codex
// mark's `_` (`markInk`) use the terminal's default foreground instead, which reads on either background. On a
// background the TUI paints itself (the selected-row wash, the terminal mark's dark cell), the light ink stays.
describe("light ink reads on light and dark terminals", () => {
it.each(["truecolor", "256", "16"] as const)("bright and markInk use the terminal's default foreground in %s mode", (mode) => {
const style = createStyle(mode);
expect(style.paint("bright", "intent")).toBe("\x1b[39mintent\x1b[0m");
expect(style.paint("bright", "INTENT", { bold: true })).toBe("\x1b[1;39mINTENT\x1b[0m");
expect(style.paint("markInk", "_", { bold: true })).toBe("\x1b[1;39m_\x1b[0m");
});
it("keeps the light ink on a background the TUI paints itself", () => {
expect(createStyle("truecolor").paint("bright", "row", { bg: "selection" })).toBe("\x1b[38;2;232;234;240;48;2;34;52;82mrow\x1b[0m");
expect(createStyle("256").paint("bright", ">", { bg: "markBg", bold: true })).toBe("\x1b[1;38;5;254;48;5;233m>\x1b[0m");
expect(createStyle("16").paint("bright", "_", { bg: "markBg" })).toBe("\x1b[97;40m_\x1b[0m");
});
it("leaves text plain with colour off", () => {
expect(createStyle("none").paint("bright", "intent")).toBe("intent");
});
});