Browse Source

fix(tui): revert theme to main-branch colors to fix Terminal.app crash

Remove all Terminal.app color/theme workarounds (16-color palette,
execSync theme detection, light/dark palettes). Use the same static
hex colors as the main branch. Only keep isAppleTerminal for the
markdown rendering fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fanghanjun 7 months ago
parent
commit
1936580f75
2 changed files with 13 additions and 180 deletions
  1. 0 54
      packages/cli/src/tui/__tests__/theme.test.ts
  2. 13 126
      packages/cli/src/tui/theme.ts

+ 0 - 54
packages/cli/src/tui/__tests__/theme.test.ts

@@ -1,54 +0,0 @@
-import { describe, expect, it, vi, afterEach } from "vitest";
-import type { TerminalTheme } from "../theme.js";
-
-describe("detectTerminalTheme", () => {
-  const originalEnv = process.env.COLORFGBG;
-  const originalPlatform = process.platform;
-
-  afterEach(() => {
-    if (originalEnv === undefined) {
-      delete process.env.COLORFGBG;
-    } else {
-      process.env.COLORFGBG = originalEnv;
-    }
-    Object.defineProperty(process, "platform", { value: originalPlatform });
-    vi.resetModules();
-  });
-
-  async function detect(opts?: { colorfgbg?: string; platform?: string }): Promise<TerminalTheme> {
-    if (opts?.colorfgbg !== undefined) {
-      process.env.COLORFGBG = opts.colorfgbg;
-    } else {
-      delete process.env.COLORFGBG;
-    }
-    if (opts?.platform) {
-      Object.defineProperty(process, "platform", { value: opts.platform });
-    }
-    const mod = await import("../theme.js");
-    return mod.detectTerminalTheme();
-  }
-
-  it("detects dark background from COLORFGBG bg index 0", async () => {
-    expect(await detect({ colorfgbg: "15;0" })).toBe("dark");
-  });
-
-  it("detects dark from three-part COLORFGBG", async () => {
-    expect(await detect({ colorfgbg: "15;0;0" })).toBe("dark");
-  });
-
-  it("detects light background from COLORFGBG bg index 15", async () => {
-    expect(await detect({ colorfgbg: "0;15" })).toBe("light");
-  });
-
-  it("detects light from bg index 7", async () => {
-    expect(await detect({ colorfgbg: "0;7" })).toBe("light");
-  });
-
-  it("treats index 8 as dark", async () => {
-    expect(await detect({ colorfgbg: "15;8" })).toBe("dark");
-  });
-
-  it("defaults to dark on non-darwin without COLORFGBG", async () => {
-    expect(await detect({ platform: "linux" })).toBe("dark");
-  });
-});

+ 13 - 126
packages/cli/src/tui/theme.ts

@@ -1,130 +1,17 @@
-import { execSync } from "node:child_process";
-
-export type TerminalTheme = "dark" | "light";
-
-/** Whether the terminal is macOS Terminal.app (limited ANSI support). */
+/** Whether the terminal is macOS Terminal.app. */
 export const isAppleTerminal = process.env.TERM_PROGRAM === "Apple_Terminal";
 
-/**
- * Detect terminal background color.
- *
- * 1. $COLORFGBG (iTerm2, rxvt, etc.) — format: "fg;bg" or "fg;other;bg"
- * 2. macOS system appearance via `defaults read` (for Terminal.app which lacks COLORFGBG)
- * 3. Default: "dark"
- */
-export function detectTerminalTheme(): TerminalTheme {
-  // Method 1: COLORFGBG
-  const raw = process.env.COLORFGBG;
-  if (raw) {
-    const parts = raw.split(";");
-    const bg = Number(parts[parts.length - 1]);
-    if (!Number.isNaN(bg)) {
-      return bg <= 6 || bg === 8 ? "dark" : "light";
-    }
-  }
-
-  // Method 2: macOS system appearance (AppleInterfaceStyle = "Dark" in dark mode)
-  if (process.platform === "darwin") {
-    try {
-      const result = execSync("defaults read -g AppleInterfaceStyle", {
-        encoding: "utf8",
-        timeout: 500,
-        stdio: ["pipe", "pipe", "pipe"],
-      }).trim();
-      return result === "Dark" ? "dark" : "light";
-    } catch {
-      // Command fails when light mode is active (key doesn't exist)
-      return "light";
-    }
-  }
-
-  return "dark";
-}
-
-interface ThemePalette {
-  readonly warmAccent: string;
-  readonly warmMuted: string;
-  readonly warmReply: string;
-  readonly warmBorder: string;
-  readonly statusSuccess: string;
-  readonly statusError: string;
-  readonly statusActive: string;
-  readonly statusIdle: string;
-  readonly roleUser: string;
-  readonly roleSystem: string;
-}
-
-const darkPalette: ThemePalette = {
-  warmAccent: "#d4a070",
-  warmMuted: "#8f8374",
-  warmReply: "#a8c4d4",
-  warmBorder: "#6b6156",
-  statusSuccess: "#7ec87e",
-  statusError: "#e06060",
-  statusActive: "#d4a76a",
-  statusIdle: "#7a7268",
-  roleUser: "#c0a480",
-  roleSystem: "#b8a8d0",
-};
-
-const lightPalette: ThemePalette = {
-  warmAccent: "#8b5e3c",
-  warmMuted: "#7a6e62",
-  warmReply: "#2a5a7a",
-  warmBorder: "#b0a898",
-  statusSuccess: "#2e7d32",
-  statusError: "#c62828",
-  statusActive: "#a06020",
-  statusIdle: "#908478",
-  roleUser: "#6b4c30",
-  roleSystem: "#5c4a80",
-};
-
-// Terminal.app: use basic 16-color ANSI names to avoid 24-bit escape sequences
-// that can trigger CoreGraphics crashes with CJK text.
-const appleTerminalDarkPalette: ThemePalette = {
-  warmAccent: "yellow",
-  warmMuted: "gray",
-  warmReply: "cyan",
-  warmBorder: "gray",
-  statusSuccess: "green",
-  statusError: "red",
-  statusActive: "yellow",
-  statusIdle: "gray",
-  roleUser: "white",
-  roleSystem: "magenta",
-};
-
-const appleTerminalLightPalette: ThemePalette = {
-  warmAccent: "yellow",
-  warmMuted: "gray",
-  warmReply: "blue",
-  warmBorder: "gray",
-  statusSuccess: "green",
-  statusError: "red",
-  statusActive: "yellow",
-  statusIdle: "gray",
-  roleUser: "black",
-  roleSystem: "magenta",
-};
-
-function resolvePalette(): ThemePalette {
-  const theme = detectTerminalTheme();
-  if (isAppleTerminal) {
-    return theme === "light" ? appleTerminalLightPalette : appleTerminalDarkPalette;
-  }
-  return theme === "light" ? lightPalette : darkPalette;
-}
+export const WARM_ACCENT = "#c88a56";
+export const WARM_MUTED = "#8f8374";
+export const WARM_REPLY = "#f0e6d8";
+export const WARM_BORDER = "#6b6156";
 
-const palette = resolvePalette();
+// Semantic status colors
+export const STATUS_SUCCESS = "#7ec87e";
+export const STATUS_ERROR = "#e06060";
+export const STATUS_ACTIVE = "#d4a76a";
+export const STATUS_IDLE = "#7a7268";
 
-export const WARM_ACCENT = palette.warmAccent;
-export const WARM_MUTED = palette.warmMuted;
-export const WARM_REPLY = palette.warmReply;
-export const WARM_BORDER = palette.warmBorder;
-export const STATUS_SUCCESS = palette.statusSuccess;
-export const STATUS_ERROR = palette.statusError;
-export const STATUS_ACTIVE = palette.statusActive;
-export const STATUS_IDLE = palette.statusIdle;
-export const ROLE_USER = palette.roleUser;
-export const ROLE_SYSTEM = palette.roleSystem;
+// Role colors
+export const ROLE_USER = "#a8c4d4";
+export const ROLE_SYSTEM = "#b8a8d0";