From 988ef6148855072352c59c4a11af32aa06fb6d97 Mon Sep 17 00:00:00 2001 From: Juan Francisco Lebrero <101231690+frizynn@users.noreply.github.com> Date: Thu, 19 Mar 2026 18:35:56 -0300 Subject: [PATCH] refactor: consolidate theme files and remove manual schema (#1478) - Delete theme-schema.json (335 lines): redundant with the TypeBox schema already defined in theme.ts, only referenced via $schema URLs in the JSON files for editor autocomplete. - Delete dark.json (85 lines) and light.json (84 lines): move built-in theme definitions into themes.ts as TypeScript objects, eliminating runtime filesystem reads and the getThemesDir() dependency. - Export ThemeJson type from theme.ts so themes.ts can reference it. - Net reduction: ~319 lines removed. --- .../src/modes/interactive/theme/dark.json | 85 ----- .../src/modes/interactive/theme/light.json | 84 ----- .../modes/interactive/theme/theme-schema.json | 335 ------------------ .../src/modes/interactive/theme/theme.ts | 25 +- .../src/modes/interactive/theme/themes.ts | 196 ++++++++++ 5 files changed, 203 insertions(+), 522 deletions(-) delete mode 100644 packages/pi-coding-agent/src/modes/interactive/theme/dark.json delete mode 100644 packages/pi-coding-agent/src/modes/interactive/theme/light.json delete mode 100644 packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json create mode 100644 packages/pi-coding-agent/src/modes/interactive/theme/themes.ts diff --git a/packages/pi-coding-agent/src/modes/interactive/theme/dark.json b/packages/pi-coding-agent/src/modes/interactive/theme/dark.json deleted file mode 100644 index 0ca2af510..000000000 --- a/packages/pi-coding-agent/src/modes/interactive/theme/dark.json +++ /dev/null @@ -1,85 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json", - "name": "dark", - "vars": { - "cyan": "#00d7ff", - "blue": "#5f87ff", - "green": "#b5bd68", - "red": "#cc6666", - "yellow": "#ffff00", - "gray": "#808080", - "dimGray": "#666666", - "darkGray": "#505050", - "accent": "#8abeb7", - "selectedBg": "#3a3a4a", - "userMsgBg": "#343541", - "toolPendingBg": "#282832", - "toolSuccessBg": "#283228", - "toolErrorBg": "#3c2828", - "customMsgBg": "#2d2838" - }, - "colors": { - "accent": "accent", - "border": "blue", - "borderAccent": "cyan", - "borderMuted": "darkGray", - "success": "green", - "error": "red", - "warning": "yellow", - "muted": "gray", - "dim": "dimGray", - "text": "", - "thinkingText": "gray", - - "selectedBg": "selectedBg", - "userMessageBg": "userMsgBg", - "userMessageText": "", - "customMessageBg": "customMsgBg", - "customMessageText": "", - "customMessageLabel": "#9575cd", - "toolPendingBg": "toolPendingBg", - "toolSuccessBg": "toolSuccessBg", - "toolErrorBg": "toolErrorBg", - "toolTitle": "", - "toolOutput": "gray", - - "mdHeading": "#f0c674", - "mdLink": "#81a2be", - "mdLinkUrl": "dimGray", - "mdCode": "accent", - "mdCodeBlock": "green", - "mdCodeBlockBorder": "gray", - "mdQuote": "gray", - "mdQuoteBorder": "gray", - "mdHr": "gray", - "mdListBullet": "accent", - - "toolDiffAdded": "green", - "toolDiffRemoved": "red", - "toolDiffContext": "gray", - - "syntaxComment": "#6A9955", - "syntaxKeyword": "#569CD6", - "syntaxFunction": "#DCDCAA", - "syntaxVariable": "#9CDCFE", - "syntaxString": "#CE9178", - "syntaxNumber": "#B5CEA8", - "syntaxType": "#4EC9B0", - "syntaxOperator": "#D4D4D4", - "syntaxPunctuation": "#D4D4D4", - - "thinkingOff": "darkGray", - "thinkingMinimal": "#6e6e6e", - "thinkingLow": "#5f87af", - "thinkingMedium": "#81a2be", - "thinkingHigh": "#b294bb", - "thinkingXhigh": "#d183e8", - - "bashMode": "green" - }, - "export": { - "pageBg": "#18181e", - "cardBg": "#1e1e24", - "infoBg": "#3c3728" - } -} diff --git a/packages/pi-coding-agent/src/modes/interactive/theme/light.json b/packages/pi-coding-agent/src/modes/interactive/theme/light.json deleted file mode 100644 index 58ab93e98..000000000 --- a/packages/pi-coding-agent/src/modes/interactive/theme/light.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/badlogic/pi-mono/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json", - "name": "light", - "vars": { - "teal": "#5a8080", - "blue": "#547da7", - "green": "#588458", - "red": "#aa5555", - "yellow": "#9a7326", - "mediumGray": "#6c6c6c", - "dimGray": "#767676", - "lightGray": "#b0b0b0", - "selectedBg": "#d0d0e0", - "userMsgBg": "#e8e8e8", - "toolPendingBg": "#e8e8f0", - "toolSuccessBg": "#e8f0e8", - "toolErrorBg": "#f0e8e8", - "customMsgBg": "#ede7f6" - }, - "colors": { - "accent": "teal", - "border": "blue", - "borderAccent": "teal", - "borderMuted": "lightGray", - "success": "green", - "error": "red", - "warning": "yellow", - "muted": "mediumGray", - "dim": "dimGray", - "text": "", - "thinkingText": "mediumGray", - - "selectedBg": "selectedBg", - "userMessageBg": "userMsgBg", - "userMessageText": "", - "customMessageBg": "customMsgBg", - "customMessageText": "", - "customMessageLabel": "#7e57c2", - "toolPendingBg": "toolPendingBg", - "toolSuccessBg": "toolSuccessBg", - "toolErrorBg": "toolErrorBg", - "toolTitle": "", - "toolOutput": "mediumGray", - - "mdHeading": "yellow", - "mdLink": "blue", - "mdLinkUrl": "dimGray", - "mdCode": "teal", - "mdCodeBlock": "green", - "mdCodeBlockBorder": "mediumGray", - "mdQuote": "mediumGray", - "mdQuoteBorder": "mediumGray", - "mdHr": "mediumGray", - "mdListBullet": "green", - - "toolDiffAdded": "green", - "toolDiffRemoved": "red", - "toolDiffContext": "mediumGray", - - "syntaxComment": "#008000", - "syntaxKeyword": "#0000FF", - "syntaxFunction": "#795E26", - "syntaxVariable": "#001080", - "syntaxString": "#A31515", - "syntaxNumber": "#098658", - "syntaxType": "#267F99", - "syntaxOperator": "#000000", - "syntaxPunctuation": "#000000", - - "thinkingOff": "lightGray", - "thinkingMinimal": "#767676", - "thinkingLow": "blue", - "thinkingMedium": "teal", - "thinkingHigh": "#875f87", - "thinkingXhigh": "#8b008b", - - "bashMode": "green" - }, - "export": { - "pageBg": "#f8f8f8", - "cardBg": "#ffffff", - "infoBg": "#fffae6" - } -} diff --git a/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json b/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json deleted file mode 100644 index 7bc495da0..000000000 --- a/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.json +++ /dev/null @@ -1,335 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "Pi Coding Agent Theme", - "description": "Theme schema for Pi coding agent", - "type": "object", - "required": ["name", "colors"], - "properties": { - "$schema": { - "type": "string", - "description": "JSON schema reference" - }, - "name": { - "type": "string", - "description": "Theme name" - }, - "vars": { - "type": "object", - "description": "Reusable color variables", - "additionalProperties": { - "oneOf": [ - { - "type": "string", - "description": "Hex color (#RRGGBB), variable reference, or empty string for terminal default" - }, - { - "type": "integer", - "minimum": 0, - "maximum": 255, - "description": "256-color palette index (0-255)" - } - ] - } - }, - "colors": { - "type": "object", - "description": "Theme color definitions (all required)", - "required": [ - "accent", - "border", - "borderAccent", - "borderMuted", - "success", - "error", - "warning", - "muted", - "dim", - "text", - "thinkingText", - "selectedBg", - "userMessageBg", - "userMessageText", - "customMessageBg", - "customMessageText", - "customMessageLabel", - "toolPendingBg", - "toolSuccessBg", - "toolErrorBg", - "toolTitle", - "toolOutput", - "mdHeading", - "mdLink", - "mdLinkUrl", - "mdCode", - "mdCodeBlock", - "mdCodeBlockBorder", - "mdQuote", - "mdQuoteBorder", - "mdHr", - "mdListBullet", - "toolDiffAdded", - "toolDiffRemoved", - "toolDiffContext", - "syntaxComment", - "syntaxKeyword", - "syntaxFunction", - "syntaxVariable", - "syntaxString", - "syntaxNumber", - "syntaxType", - "syntaxOperator", - "syntaxPunctuation", - "thinkingOff", - "thinkingMinimal", - "thinkingLow", - "thinkingMedium", - "thinkingHigh", - "thinkingXhigh", - "bashMode" - ], - "properties": { - "accent": { - "$ref": "#/$defs/colorValue", - "description": "Primary accent color (logo, selected items, cursor)" - }, - "border": { - "$ref": "#/$defs/colorValue", - "description": "Normal borders" - }, - "borderAccent": { - "$ref": "#/$defs/colorValue", - "description": "Highlighted borders" - }, - "borderMuted": { - "$ref": "#/$defs/colorValue", - "description": "Subtle borders" - }, - "success": { - "$ref": "#/$defs/colorValue", - "description": "Success states" - }, - "error": { - "$ref": "#/$defs/colorValue", - "description": "Error states" - }, - "warning": { - "$ref": "#/$defs/colorValue", - "description": "Warning states" - }, - "muted": { - "$ref": "#/$defs/colorValue", - "description": "Secondary/dimmed text" - }, - "dim": { - "$ref": "#/$defs/colorValue", - "description": "Very dimmed text (more subtle than muted)" - }, - "text": { - "$ref": "#/$defs/colorValue", - "description": "Default text color (usually empty string)" - }, - "thinkingText": { - "$ref": "#/$defs/colorValue", - "description": "Thinking block text color" - }, - "selectedBg": { - "$ref": "#/$defs/colorValue", - "description": "Selected item background" - }, - "userMessageBg": { - "$ref": "#/$defs/colorValue", - "description": "User message background" - }, - "userMessageText": { - "$ref": "#/$defs/colorValue", - "description": "User message text color" - }, - "customMessageBg": { - "$ref": "#/$defs/colorValue", - "description": "Custom message background (hook-injected messages)" - }, - "customMessageText": { - "$ref": "#/$defs/colorValue", - "description": "Custom message text color" - }, - "customMessageLabel": { - "$ref": "#/$defs/colorValue", - "description": "Custom message type label color" - }, - "toolPendingBg": { - "$ref": "#/$defs/colorValue", - "description": "Tool execution box (pending state)" - }, - "toolSuccessBg": { - "$ref": "#/$defs/colorValue", - "description": "Tool execution box (success state)" - }, - "toolErrorBg": { - "$ref": "#/$defs/colorValue", - "description": "Tool execution box (error state)" - }, - "toolTitle": { - "$ref": "#/$defs/colorValue", - "description": "Tool execution box title color" - }, - "toolOutput": { - "$ref": "#/$defs/colorValue", - "description": "Tool execution box output text color" - }, - "mdHeading": { - "$ref": "#/$defs/colorValue", - "description": "Markdown heading text" - }, - "mdLink": { - "$ref": "#/$defs/colorValue", - "description": "Markdown link text" - }, - "mdLinkUrl": { - "$ref": "#/$defs/colorValue", - "description": "Markdown link URL" - }, - "mdCode": { - "$ref": "#/$defs/colorValue", - "description": "Markdown inline code" - }, - "mdCodeBlock": { - "$ref": "#/$defs/colorValue", - "description": "Markdown code block content" - }, - "mdCodeBlockBorder": { - "$ref": "#/$defs/colorValue", - "description": "Markdown code block fences" - }, - "mdQuote": { - "$ref": "#/$defs/colorValue", - "description": "Markdown blockquote text" - }, - "mdQuoteBorder": { - "$ref": "#/$defs/colorValue", - "description": "Markdown blockquote border" - }, - "mdHr": { - "$ref": "#/$defs/colorValue", - "description": "Markdown horizontal rule" - }, - "mdListBullet": { - "$ref": "#/$defs/colorValue", - "description": "Markdown list bullets/numbers" - }, - "toolDiffAdded": { - "$ref": "#/$defs/colorValue", - "description": "Added lines in tool diffs" - }, - "toolDiffRemoved": { - "$ref": "#/$defs/colorValue", - "description": "Removed lines in tool diffs" - }, - "toolDiffContext": { - "$ref": "#/$defs/colorValue", - "description": "Context lines in tool diffs" - }, - "syntaxComment": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: comments" - }, - "syntaxKeyword": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: keywords" - }, - "syntaxFunction": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: function names" - }, - "syntaxVariable": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: variable names" - }, - "syntaxString": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: string literals" - }, - "syntaxNumber": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: number literals" - }, - "syntaxType": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: type names" - }, - "syntaxOperator": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: operators" - }, - "syntaxPunctuation": { - "$ref": "#/$defs/colorValue", - "description": "Syntax highlighting: punctuation" - }, - "thinkingOff": { - "$ref": "#/$defs/colorValue", - "description": "Thinking level border: off" - }, - "thinkingMinimal": { - "$ref": "#/$defs/colorValue", - "description": "Thinking level border: minimal" - }, - "thinkingLow": { - "$ref": "#/$defs/colorValue", - "description": "Thinking level border: low" - }, - "thinkingMedium": { - "$ref": "#/$defs/colorValue", - "description": "Thinking level border: medium" - }, - "thinkingHigh": { - "$ref": "#/$defs/colorValue", - "description": "Thinking level border: high" - }, - "thinkingXhigh": { - "$ref": "#/$defs/colorValue", - "description": "Thinking level border: xhigh (OpenAI codex-max only)" - }, - "bashMode": { - "$ref": "#/$defs/colorValue", - "description": "Editor border color in bash mode" - } - }, - "additionalProperties": false - }, - "export": { - "type": "object", - "description": "Optional colors for HTML export (defaults derived from userMessageBg if not specified)", - "properties": { - "pageBg": { - "$ref": "#/$defs/colorValue", - "description": "Page background color" - }, - "cardBg": { - "$ref": "#/$defs/colorValue", - "description": "Card/container background color" - }, - "infoBg": { - "$ref": "#/$defs/colorValue", - "description": "Info sections background (system prompt, notices)" - } - }, - "additionalProperties": false - } - }, - "additionalProperties": false, - "$defs": { - "colorValue": { - "oneOf": [ - { - "type": "string", - "description": "Hex color (#RRGGBB), variable reference, or empty string for terminal default" - }, - { - "type": "integer", - "minimum": 0, - "maximum": 255, - "description": "256-color palette index (0-255)" - } - ] - } - } -} diff --git a/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts b/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts index e0bf4d41b..db1a524a0 100644 --- a/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +++ b/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts @@ -9,7 +9,8 @@ import { supportsLanguage, type HighlightColors, } from "@gsd/native"; -import { getCustomThemesDir, getThemesDir } from "../../../config.js"; +import { getCustomThemesDir } from "../../../config.js"; +import { builtinThemes } from "./themes.js"; // Issue #453: native preview highlighting can wedge the entire interactive // session after a successful file tool. Keep the safer plain-text path as the @@ -100,7 +101,7 @@ const ThemeJsonSchema = Type.Object({ ), }); -type ThemeJson = Static; +export type ThemeJson = Static; const validateThemeJson = TypeCompiler.Compile(ThemeJsonSchema); @@ -450,19 +451,8 @@ export class Theme { // Theme Loading // ============================================================================ -let BUILTIN_THEMES: Record | undefined; - function getBuiltinThemes(): Record { - if (!BUILTIN_THEMES) { - const themesDir = getThemesDir(); - const darkPath = path.join(themesDir, "dark.json"); - const lightPath = path.join(themesDir, "light.json"); - BUILTIN_THEMES = { - dark: JSON.parse(fs.readFileSync(darkPath, "utf-8")) as ThemeJson, - light: JSON.parse(fs.readFileSync(lightPath, "utf-8")) as ThemeJson, - }; - } - return BUILTIN_THEMES; + return builtinThemes; } export function getAvailableThemes(): string[] { @@ -488,13 +478,12 @@ export interface ThemeInfo { } export function getAvailableThemesWithPaths(): ThemeInfo[] { - const themesDir = getThemesDir(); const customThemesDir = getCustomThemesDir(); const result: ThemeInfo[] = []; - // Built-in themes + // Built-in themes (embedded in code, no file path) for (const name of Object.keys(getBuiltinThemes())) { - result.push({ name, path: path.join(themesDir, `${name}.json`) }); + result.push({ name, path: undefined }); } // Custom themes @@ -539,7 +528,7 @@ function parseThemeJson(label: string, json: unknown): ThemeJson { errorMessage += "\nMissing required color tokens:\n"; errorMessage += missingColors.map((c) => ` - ${c}`).join("\n"); errorMessage += '\n\nPlease add these colors to your theme\'s "colors" object.'; - errorMessage += "\nSee the built-in themes (dark.json, light.json) for reference values."; + errorMessage += "\nSee the built-in dark/light themes for reference values."; } if (otherErrors.length > 0) { errorMessage += `\n\nOther errors:\n${otherErrors.join("\n")}`; diff --git a/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts b/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts new file mode 100644 index 000000000..c92763543 --- /dev/null +++ b/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts @@ -0,0 +1,196 @@ +/** + * Built-in theme definitions. + * + * Each theme is a self-contained record of color values. Variable references + * (e.g. "accent") are resolved against the `vars` map at load time by the + * theme engine in theme.ts. + * + * To add a new built-in theme, add an entry to `builtinThemes` below. + */ + +// Re-use the ThemeJson type from the schema defined in theme.ts. +// We import only the type to avoid circular runtime dependencies. +import type { ThemeJson } from "./theme.js"; + +// --------------------------------------------------------------------------- +// Dark theme +// --------------------------------------------------------------------------- + +const dark: ThemeJson = { + name: "dark", + vars: { + cyan: "#00d7ff", + blue: "#5f87ff", + green: "#b5bd68", + red: "#cc6666", + yellow: "#ffff00", + gray: "#808080", + dimGray: "#666666", + darkGray: "#505050", + accent: "#8abeb7", + selectedBg: "#3a3a4a", + userMsgBg: "#343541", + toolPendingBg: "#282832", + toolSuccessBg: "#283228", + toolErrorBg: "#3c2828", + customMsgBg: "#2d2838", + }, + colors: { + accent: "accent", + border: "blue", + borderAccent: "cyan", + borderMuted: "darkGray", + success: "green", + error: "red", + warning: "yellow", + muted: "gray", + dim: "dimGray", + text: "", + thinkingText: "gray", + + selectedBg: "selectedBg", + userMessageBg: "userMsgBg", + userMessageText: "", + customMessageBg: "customMsgBg", + customMessageText: "", + customMessageLabel: "#9575cd", + toolPendingBg: "toolPendingBg", + toolSuccessBg: "toolSuccessBg", + toolErrorBg: "toolErrorBg", + toolTitle: "", + toolOutput: "gray", + + mdHeading: "#f0c674", + mdLink: "#81a2be", + mdLinkUrl: "dimGray", + mdCode: "accent", + mdCodeBlock: "green", + mdCodeBlockBorder: "gray", + mdQuote: "gray", + mdQuoteBorder: "gray", + mdHr: "gray", + mdListBullet: "accent", + + toolDiffAdded: "green", + toolDiffRemoved: "red", + toolDiffContext: "gray", + + syntaxComment: "#6A9955", + syntaxKeyword: "#569CD6", + syntaxFunction: "#DCDCAA", + syntaxVariable: "#9CDCFE", + syntaxString: "#CE9178", + syntaxNumber: "#B5CEA8", + syntaxType: "#4EC9B0", + syntaxOperator: "#D4D4D4", + syntaxPunctuation: "#D4D4D4", + + thinkingOff: "darkGray", + thinkingMinimal: "#6e6e6e", + thinkingLow: "#5f87af", + thinkingMedium: "#81a2be", + thinkingHigh: "#b294bb", + thinkingXhigh: "#d183e8", + + bashMode: "green", + }, + export: { + pageBg: "#18181e", + cardBg: "#1e1e24", + infoBg: "#3c3728", + }, +}; + +// --------------------------------------------------------------------------- +// Light theme +// --------------------------------------------------------------------------- + +const light: ThemeJson = { + name: "light", + vars: { + teal: "#5a8080", + blue: "#547da7", + green: "#588458", + red: "#aa5555", + yellow: "#9a7326", + mediumGray: "#6c6c6c", + dimGray: "#767676", + lightGray: "#b0b0b0", + selectedBg: "#d0d0e0", + userMsgBg: "#e8e8e8", + toolPendingBg: "#e8e8f0", + toolSuccessBg: "#e8f0e8", + toolErrorBg: "#f0e8e8", + customMsgBg: "#ede7f6", + }, + colors: { + accent: "teal", + border: "blue", + borderAccent: "teal", + borderMuted: "lightGray", + success: "green", + error: "red", + warning: "yellow", + muted: "mediumGray", + dim: "dimGray", + text: "", + thinkingText: "mediumGray", + + selectedBg: "selectedBg", + userMessageBg: "userMsgBg", + userMessageText: "", + customMessageBg: "customMsgBg", + customMessageText: "", + customMessageLabel: "#7e57c2", + toolPendingBg: "toolPendingBg", + toolSuccessBg: "toolSuccessBg", + toolErrorBg: "toolErrorBg", + toolTitle: "", + toolOutput: "mediumGray", + + mdHeading: "yellow", + mdLink: "blue", + mdLinkUrl: "dimGray", + mdCode: "teal", + mdCodeBlock: "green", + mdCodeBlockBorder: "mediumGray", + mdQuote: "mediumGray", + mdQuoteBorder: "mediumGray", + mdHr: "mediumGray", + mdListBullet: "green", + + toolDiffAdded: "green", + toolDiffRemoved: "red", + toolDiffContext: "mediumGray", + + syntaxComment: "#008000", + syntaxKeyword: "#0000FF", + syntaxFunction: "#795E26", + syntaxVariable: "#001080", + syntaxString: "#A31515", + syntaxNumber: "#098658", + syntaxType: "#267F99", + syntaxOperator: "#000000", + syntaxPunctuation: "#000000", + + thinkingOff: "lightGray", + thinkingMinimal: "#767676", + thinkingLow: "blue", + thinkingMedium: "teal", + thinkingHigh: "#875f87", + thinkingXhigh: "#8b008b", + + bashMode: "green", + }, + export: { + pageBg: "#f8f8f8", + cardBg: "#ffffff", + infoBg: "#fffae6", + }, +}; + +// --------------------------------------------------------------------------- +// Export +// --------------------------------------------------------------------------- + +export const builtinThemes: Record = { dark, light };