mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-25 11:55:47 -06:00
fix(ui): make Control UI terminal readable in light mode (#123184)
The light terminal theme reused the dark-background ANSI palette (1.4-2.6:1 contrast on #f7f8fa; brightWhite was 1.06), and the intro banner hardcoded 256-color indices 223/216 that bypass the client theme entirely. Light mode now gets its own darkened ANSI-16 palette (>=4.5:1, bright variants darker for emphasis, brightWhite maps to strongest ink), and the banner emits themable ANSI-16 yellow/bright-red so each mode owns its rendering. Dark mode is visually unchanged.
This commit is contained in:
committed by
GitHub
parent
f04fdd5594
commit
dbea5a77f0
@@ -25,9 +25,9 @@ describe("composeTerminalIntroBanner", () => {
|
||||
const banner = composeTerminalIntroBanner();
|
||||
|
||||
expect(banner).toBe(
|
||||
`\r\n\x1b[38;5;223mWelcome to the Claw.\x1b[0m\r\n\r\n\x1b[38;5;216m${EXPECTED_ART.join("\r\n")}\r\n\r\n\x1b[0m`,
|
||||
`\r\n\x1b[33mWelcome to the Claw.\x1b[0m\r\n\r\n\x1b[91m${EXPECTED_ART.join("\r\n")}\r\n\r\n\x1b[0m`,
|
||||
);
|
||||
expect(banner.startsWith("\r\n\x1b[38;5;223mWelcome to the Claw.\x1b[0m")).toBe(true);
|
||||
expect(banner.startsWith("\r\n\x1b[33mWelcome to the Claw.\x1b[0m")).toBe(true);
|
||||
expect(banner.endsWith("\r\n\r\n\x1b[0m")).toBe(true);
|
||||
expect(banner.replaceAll("\r\n", "")).not.toContain("\n");
|
||||
});
|
||||
|
||||
@@ -22,8 +22,11 @@ const TERMINAL_INTRO_ART = [
|
||||
// Always full art: open-time request.cols is the pre-fit boot grid (the client
|
||||
// resizes immediately after open), so width gating keyed on it suppressed the
|
||||
// art on real, wide terminals.
|
||||
// ANSI-16 colors only: the server can't know the client's light/dark mode, and
|
||||
// fixed 256-color indices (223/216) bypass the client theme and vanish on light
|
||||
// backgrounds. Yellow/bright-red stay warm on dark and darken on light.
|
||||
export function composeTerminalIntroBanner(): string {
|
||||
const headline = `\x1b[38;5;223mWelcome to the Claw.${RESET}`;
|
||||
const art = `\x1b[38;5;216m${TERMINAL_INTRO_ART.join("\r\n")}\r\n\r\n`;
|
||||
const headline = `\x1b[33mWelcome to the Claw.${RESET}`;
|
||||
const art = `\x1b[91m${TERMINAL_INTRO_ART.join("\r\n")}\r\n\r\n`;
|
||||
return `\r\n${headline}\r\n\r\n${art}${RESET}`;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,10 @@ type TerminalTheme = NonNullable<
|
||||
NonNullable<CreateGhosttyTerminalOptions["terminalOptions"]>["theme"]
|
||||
>;
|
||||
|
||||
// ANSI palette tuned to sit on the Control UI's near-black / near-white surfaces.
|
||||
// Shared 8 + bright 8; foreground/background/cursor are overridden per mode below.
|
||||
const ANSI = {
|
||||
// ANSI palettes tuned per mode: colors that read on the near-black surface sit
|
||||
// at 1.4-2.6:1 on the light surface, so light gets its own darkened set
|
||||
// (>=4.5:1 on #f7f8fa) instead of sharing the dark palette.
|
||||
const DARK_ANSI = {
|
||||
black: "#1b1e26",
|
||||
red: "#ff6b6b",
|
||||
green: "#4ec9a8",
|
||||
@@ -29,6 +30,29 @@ const ANSI = {
|
||||
brightWhite: "#ffffff",
|
||||
} as const;
|
||||
|
||||
// Same hue identities as DARK_ANSI, darkened for the light surface. Bright
|
||||
// variants go darker than normal ones so bold/bright text stays emphatic
|
||||
// instead of washing out; brightWhite maps to the strongest ink, matching
|
||||
// the light-theme convention in Ghostty/iTerm paired themes.
|
||||
const LIGHT_ANSI = {
|
||||
black: "#3a3f4b",
|
||||
red: "#c62f3d",
|
||||
green: "#177a5e",
|
||||
yellow: "#8f6400",
|
||||
blue: "#1e66d0",
|
||||
magenta: "#94439c",
|
||||
cyan: "#0f7487",
|
||||
white: "#1b1e26",
|
||||
brightBlack: "#5c6370",
|
||||
brightRed: "#a3242f",
|
||||
brightGreen: "#0f664e",
|
||||
brightYellow: "#755200",
|
||||
brightBlue: "#1a55ab",
|
||||
brightMagenta: "#7c3382",
|
||||
brightCyan: "#0c6070",
|
||||
brightWhite: "#0a0c10",
|
||||
} as const;
|
||||
|
||||
// Dark mirrors the claw tokens in styles/base.css (`--bg` #0e1015,
|
||||
// `--accent` #ff5c5c) — keep them in sync when the tokens change. Light is a
|
||||
// deliberate neutral cool white: the light theme families diverge (ivory,
|
||||
@@ -47,16 +71,14 @@ export function terminalTheme(mode: "dark" | "light"): TerminalTheme {
|
||||
const colors = terminalDynamicColors(mode);
|
||||
if (mode === "light") {
|
||||
return {
|
||||
...ANSI,
|
||||
...LIGHT_ANSI,
|
||||
...colors,
|
||||
cursorAccent: "#f7f8fa",
|
||||
selectionBackground: "rgba(90, 162, 255, 0.30)",
|
||||
black: "#3a3f4b",
|
||||
white: "#1b1e26",
|
||||
selectionBackground: "rgba(30, 102, 208, 0.30)",
|
||||
};
|
||||
}
|
||||
return {
|
||||
...ANSI,
|
||||
...DARK_ANSI,
|
||||
...colors,
|
||||
cursorAccent: "#0e1015",
|
||||
selectionBackground: "rgba(90, 162, 255, 0.32)",
|
||||
|
||||
Reference in New Issue
Block a user