tiletopia/src/lib/theme.test.ts
megaproxy 7e624a3f96 Add customizable terminal colors (global theme + per-pane overrides)
Four editable colors (background/foreground/cursor/selection) via a new
ColorPanel modal with built-in presets and live preview. Global default
persists to localStorage and syncs across windows; per-pane overrides ride
on LeafNode.colorOverride in the workspace tree. Titlebar 🎨 button edits
the global theme; per-pane 🎨 chip overrides a single pane. Subsumes the
prior uncommitted softened-foreground tweak into lib/theme.ts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 23:41:19 +01:00

79 lines
2.7 KiB
TypeScript

import { describe, it, expect } from "vitest";
import {
resolvePaneColors,
toXtermTheme,
DEFAULT_PANE_COLORS,
COLOR_PRESETS,
type PaneColors,
} from "./theme";
describe("resolvePaneColors", () => {
it("falls back to defaults when nothing is set", () => {
expect(resolvePaneColors(undefined, undefined)).toEqual(DEFAULT_PANE_COLORS);
});
it("uses global values over defaults", () => {
const global: PaneColors = { background: "#111111", cursor: "#abcdef" };
const r = resolvePaneColors(global, undefined);
expect(r.background).toBe("#111111");
expect(r.cursor).toBe("#abcdef");
// Unset fields still come from defaults.
expect(r.foreground).toBe(DEFAULT_PANE_COLORS.foreground);
expect(r.selection).toBe(DEFAULT_PANE_COLORS.selection);
});
it("per-pane override wins over global, field by field", () => {
const global: PaneColors = { background: "#111111", foreground: "#222222" };
const override: PaneColors = { background: "#999999" };
const r = resolvePaneColors(global, override);
expect(r.background).toBe("#999999"); // override wins
expect(r.foreground).toBe("#222222"); // inherits global
expect(r.cursor).toBe(DEFAULT_PANE_COLORS.cursor); // inherits default
});
it("always returns all four fields defined", () => {
const r = resolvePaneColors({}, {});
expect(Object.keys(r).sort()).toEqual([
"background",
"cursor",
"foreground",
"selection",
]);
});
});
describe("toXtermTheme", () => {
it("maps resolved colours onto the xterm ITheme shape", () => {
const theme = toXtermTheme({
background: "#0c0c0c",
foreground: "#c5c8c6",
cursor: "#ffffff",
selection: "#3a3a3a",
});
expect(theme.background).toBe("#0c0c0c");
expect(theme.foreground).toBe("#c5c8c6");
expect(theme.cursor).toBe("#ffffff");
// selection maps to xterm 5.x's renamed property.
expect(theme.selectionBackground).toBe("#3a3a3a");
// cursorAccent is pinned to the background for block-cursor legibility.
expect(theme.cursorAccent).toBe("#0c0c0c");
});
it("keeps the fixed softened white/brightWhite slice", () => {
const theme = toXtermTheme(DEFAULT_PANE_COLORS);
expect(theme.white).toBe("#c5c8c6");
expect(theme.brightWhite).toBe("#e0e0e0");
});
});
describe("COLOR_PRESETS", () => {
it("starts with the tiletopia default and every preset is fully specified", () => {
expect(COLOR_PRESETS[0].name).toBe("Tiletopia Dark");
expect(COLOR_PRESETS[0].colors).toEqual(DEFAULT_PANE_COLORS);
for (const p of COLOR_PRESETS) {
for (const key of ["background", "foreground", "cursor", "selection"] as const) {
expect(p.colors[key]).toMatch(/^#[0-9a-fA-F]{6}$/);
}
}
});
});