@oh-my-pi/pi-coding-agent 17.2.11 → 17.2.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/{CHANGELOG-d9xenpn9.md → CHANGELOG-k9ghy5sn.md} +13 -0
- package/dist/cli.js +10795 -10791
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/config/custom-models.d.ts +31 -0
- package/dist/types/config/model-config-values.d.ts +19 -0
- package/dist/types/config/model-patch.d.ts +93 -0
- package/dist/types/config/model-provider-discovery.d.ts +51 -0
- package/dist/types/config/model-registry.d.ts +5 -52
- package/dist/types/config/settings.d.ts +5 -3
- package/dist/types/debug/raw-sse-buffer.d.ts +9 -0
- package/dist/types/discovery/agent-plugin-format.d.ts +6 -13
- package/dist/types/discovery/contained-path.d.ts +2 -3
- package/dist/types/eval/executor-base.d.ts +8 -2
- package/dist/types/eval/kernel-session-registry.d.ts +60 -0
- package/dist/types/export/share.d.ts +1 -1
- package/dist/types/lsp/diagnostics.d.ts +96 -0
- package/dist/types/lsp/index.d.ts +7 -128
- package/dist/types/lsp/servers.d.ts +72 -0
- package/dist/types/lsp/tool.d.ts +42 -0
- package/dist/types/lsp/workspace-diagnostics.d.ts +12 -0
- package/dist/types/lsp/writethrough.d.ts +33 -0
- package/dist/types/mcp/transports/header-policy.d.ts +2 -1
- package/dist/types/modes/components/agent-hub-projection.d.ts +2 -7
- package/dist/types/modes/components/agent-hub-renderer.d.ts +0 -7
- package/dist/types/modes/theme/color.d.ts +19 -0
- package/dist/types/modes/theme/loader.d.ts +19 -0
- package/dist/types/modes/theme/schema.d.ts +175 -0
- package/dist/types/modes/theme/symbols.d.ts +28 -0
- package/dist/types/modes/theme/theme-class.d.ts +244 -0
- package/dist/types/modes/theme/theme.d.ts +9 -280
- package/dist/types/modes/theme/tui-adapters.d.ts +13 -0
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +3 -1
- package/dist/types/secrets/message-transform.d.ts +40 -0
- package/dist/types/secrets/obfuscator.d.ts +0 -108
- package/dist/types/secrets/placeholder-scan.d.ts +95 -0
- package/dist/types/secrets/placeholder.d.ts +94 -0
- package/dist/types/secrets/replacement.d.ts +82 -0
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +1 -1
- package/dist/types/session/session-handoff.d.ts +3 -3
- package/dist/types/session/session-manager.d.ts +32 -0
- package/dist/types/session/session-provider-boundary.d.ts +1 -1
- package/dist/types/session/turn-recovery.d.ts +2 -2
- package/dist/types/slash-commands/builtin-collaboration.d.ts +2 -0
- package/dist/types/slash-commands/builtin-completions.d.ts +36 -0
- package/dist/types/slash-commands/builtin-control.d.ts +2 -0
- package/dist/types/slash-commands/builtin-lifecycle.d.ts +3 -0
- package/dist/types/slash-commands/builtin-marketplace.d.ts +11 -0
- package/dist/types/slash-commands/builtin-modes.d.ts +5 -0
- package/dist/types/slash-commands/builtin-registry.d.ts +1 -1
- package/dist/types/slash-commands/builtin-session.d.ts +2 -0
- package/dist/types/tools/gh-common.d.ts +69 -0
- package/dist/types/tools/gh-pr-checkout.d.ts +84 -0
- package/dist/types/tools/gh-pr-diff.d.ts +102 -0
- package/dist/types/tools/gh-run-watch.d.ts +78 -0
- package/dist/types/tools/gh-search.d.ts +61 -0
- package/dist/types/tools/gh-types.d.ts +340 -0
- package/dist/types/tools/gh-view.d.ts +88 -0
- package/dist/types/tools/gh.d.ts +4 -201
- package/dist/types/tools/read-archive.d.ts +16 -0
- package/dist/types/tools/read-format.d.ts +104 -0
- package/dist/types/tools/read-path-resolution.d.ts +19 -0
- package/dist/types/tools/read-pdf-images.d.ts +12 -0
- package/dist/types/tools/read-renderer.d.ts +24 -0
- package/dist/types/tools/read-selector.d.ts +27 -0
- package/dist/types/tools/read-sqlite.d.ts +16 -0
- package/dist/types/tools/read-summary.d.ts +19 -0
- package/dist/types/tools/read.d.ts +1 -23
- package/dist/types/tools/shell-tokenize.d.ts +2 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/src/advisor/runtime.ts +12 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +64 -704
- package/src/config/settings.ts +7 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +7 -7
- package/src/discovery/contained-path.ts +2 -5
- package/src/edit/hashline/diff.ts +5 -1
- package/src/eval/executor-base.ts +39 -6
- package/src/eval/jl/executor.ts +82 -371
- package/src/eval/kernel-session-registry.ts +398 -0
- package/src/eval/py/executor.ts +53 -261
- package/src/eval/rb/executor.ts +36 -279
- package/src/export/share.ts +2 -1
- package/src/lsp/diagnostics.ts +516 -0
- package/src/lsp/index.ts +20 -2819
- package/src/lsp/servers.ts +296 -0
- package/src/lsp/tool.ts +1352 -0
- package/src/lsp/workspace-diagnostics.ts +170 -0
- package/src/lsp/writethrough.ts +561 -0
- package/src/mcp/transports/header-policy.ts +1 -1
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/controllers/command-controller.ts +4 -1
- package/src/modes/theme/color.ts +133 -0
- package/src/modes/theme/loader.ts +178 -0
- package/src/modes/theme/schema.ts +263 -0
- package/src/modes/theme/symbols.ts +1000 -0
- package/src/modes/theme/theme-class.ts +611 -0
- package/src/modes/theme/theme.ts +27 -2453
- package/src/modes/theme/tui-adapters.ts +279 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/secrets/index.ts +6 -12
- package/src/secrets/message-transform.ts +287 -0
- package/src/secrets/obfuscator.ts +39 -1303
- package/src/secrets/placeholder-scan.ts +506 -0
- package/src/secrets/placeholder.ts +309 -0
- package/src/secrets/replacement.ts +216 -0
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +113 -7
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-handoff.ts +39 -20
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/turn-recovery.ts +21 -12
- package/src/slash-commands/builtin-collaboration.ts +504 -0
- package/src/slash-commands/builtin-completions.ts +291 -0
- package/src/slash-commands/builtin-control.ts +78 -0
- package/src/slash-commands/builtin-lifecycle.ts +506 -0
- package/src/slash-commands/builtin-marketplace.ts +567 -0
- package/src/slash-commands/builtin-modes.ts +518 -0
- package/src/slash-commands/builtin-registry.ts +21 -3000
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/tools/gh-common.ts +288 -0
- package/src/tools/gh-pr-checkout.ts +679 -0
- package/src/tools/gh-pr-diff.ts +473 -0
- package/src/tools/gh-run-watch.ts +1015 -0
- package/src/tools/gh-search.ts +500 -0
- package/src/tools/gh-types.ts +379 -0
- package/src/tools/gh-view.ts +612 -0
- package/src/tools/gh.ts +109 -3821
- package/src/tools/read-archive.ts +209 -0
- package/src/tools/read-format.ts +593 -0
- package/src/tools/read-path-resolution.ts +36 -0
- package/src/tools/read-pdf-images.ts +250 -0
- package/src/tools/read-renderer.ts +289 -0
- package/src/tools/read-selector.ts +84 -0
- package/src/tools/read-sqlite.ts +215 -0
- package/src/tools/read-summary.ts +199 -0
- package/src/tools/read.ts +77 -1820
- package/src/tools/shell-tokenize.ts +1 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/zai.ts +12 -3
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import type { ColorMode, ColorValue } from "./schema";
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Color Utilities
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export function detectColorMode(): ColorMode {
|
|
8
|
+
const colorterm = Bun.env.COLORTERM;
|
|
9
|
+
if (colorterm === "truecolor" || colorterm === "24bit") {
|
|
10
|
+
return "truecolor";
|
|
11
|
+
}
|
|
12
|
+
// Windows Terminal supports truecolor
|
|
13
|
+
if (Bun.env.WT_SESSION) {
|
|
14
|
+
return "truecolor";
|
|
15
|
+
}
|
|
16
|
+
const term = Bun.env.TERM || "";
|
|
17
|
+
// Only fall back to 256color for truly limited terminals
|
|
18
|
+
if (term === "dumb" || term === "" || term === "linux") {
|
|
19
|
+
return "256color";
|
|
20
|
+
}
|
|
21
|
+
// Assume truecolor for everything else - virtually all modern terminals support it
|
|
22
|
+
return "truecolor";
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function colorToAnsi(color: string, mode: ColorMode): string {
|
|
26
|
+
const format = mode === "truecolor" ? "ansi-16m" : "ansi-256";
|
|
27
|
+
const ansi = Bun.color(color, format);
|
|
28
|
+
if (ansi === null) {
|
|
29
|
+
throw new Error(`Invalid color value: ${color}`);
|
|
30
|
+
}
|
|
31
|
+
return ansi;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function fgAnsi(color: string | number, mode: ColorMode): string {
|
|
35
|
+
if (color === "") return "\x1b[39m";
|
|
36
|
+
if (typeof color === "number") return `\x1b[38;5;${color}m`;
|
|
37
|
+
if (typeof color === "string") {
|
|
38
|
+
return colorToAnsi(color, mode);
|
|
39
|
+
}
|
|
40
|
+
throw new Error(`Invalid color value: ${color}`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function bgAnsi(color: string | number, mode: ColorMode): string {
|
|
44
|
+
if (color === "") return "\x1b[49m";
|
|
45
|
+
if (typeof color === "number") return `\x1b[48;5;${color}m`;
|
|
46
|
+
const ansi = colorToAnsi(color, mode);
|
|
47
|
+
return ansi.replace("\x1b[38;", "\x1b[48;");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function resolveVarRefs(
|
|
51
|
+
value: ColorValue,
|
|
52
|
+
vars: Record<string, ColorValue>,
|
|
53
|
+
visited = new Set<string>(),
|
|
54
|
+
): string | number {
|
|
55
|
+
if (typeof value === "number" || value === "" || value.startsWith("#")) {
|
|
56
|
+
return value;
|
|
57
|
+
}
|
|
58
|
+
if (visited.has(value)) {
|
|
59
|
+
throw new Error(`Circular variable reference detected: ${value}`);
|
|
60
|
+
}
|
|
61
|
+
if (!(value in vars)) {
|
|
62
|
+
throw new Error(`Variable reference not found: ${value}`);
|
|
63
|
+
}
|
|
64
|
+
visited.add(value);
|
|
65
|
+
return resolveVarRefs(vars[value], vars, visited);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function resolveThemeColors<T extends Record<string, ColorValue>>(
|
|
69
|
+
colors: T,
|
|
70
|
+
vars: Record<string, ColorValue> = {},
|
|
71
|
+
): Record<keyof T, string | number> {
|
|
72
|
+
const resolved: Record<string, string | number> = {};
|
|
73
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
74
|
+
resolved[key] = resolveVarRefs(value, vars);
|
|
75
|
+
}
|
|
76
|
+
return resolved as Record<keyof T, string | number>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Resolve a theme color value (hex string or 256-color index) to a CSS hex string.
|
|
81
|
+
* Empty string represents the default terminal color.
|
|
82
|
+
*/
|
|
83
|
+
export function resolveToHex(value: string | number, isLight: boolean): string {
|
|
84
|
+
if (typeof value === "number") return ansi256ToHex(value);
|
|
85
|
+
if (value === "") return isLight ? "#000000" : "#e5e5e7";
|
|
86
|
+
return value;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Convert a 256-color index to hex string.
|
|
91
|
+
* Indices 0-15: basic colors (approximate)
|
|
92
|
+
* Indices 16-231: 6x6x6 color cube
|
|
93
|
+
* Indices 232-255: grayscale ramp
|
|
94
|
+
*/
|
|
95
|
+
export function ansi256ToHex(index: number): string {
|
|
96
|
+
// Basic colors (0-15) - approximate common terminal values
|
|
97
|
+
const basicColors = [
|
|
98
|
+
"#000000",
|
|
99
|
+
"#800000",
|
|
100
|
+
"#008000",
|
|
101
|
+
"#808000",
|
|
102
|
+
"#000080",
|
|
103
|
+
"#800080",
|
|
104
|
+
"#008080",
|
|
105
|
+
"#c0c0c0",
|
|
106
|
+
"#808080",
|
|
107
|
+
"#ff0000",
|
|
108
|
+
"#00ff00",
|
|
109
|
+
"#ffff00",
|
|
110
|
+
"#0000ff",
|
|
111
|
+
"#ff00ff",
|
|
112
|
+
"#00ffff",
|
|
113
|
+
"#ffffff",
|
|
114
|
+
];
|
|
115
|
+
if (index < 16) {
|
|
116
|
+
return basicColors[index];
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Color cube (16-231): 6x6x6 = 216 colors
|
|
120
|
+
if (index < 232) {
|
|
121
|
+
const cubeIndex = index - 16;
|
|
122
|
+
const r = Math.floor(cubeIndex / 36);
|
|
123
|
+
const g = Math.floor((cubeIndex % 36) / 6);
|
|
124
|
+
const b = cubeIndex % 6;
|
|
125
|
+
const toHex = (n: number) => (n === 0 ? 0 : 55 + n * 40).toString(16).padStart(2, "0");
|
|
126
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Grayscale (232-255): 24 shades
|
|
130
|
+
const gray = 8 + (index - 232) * 10;
|
|
131
|
+
const grayHex = gray.toString(16).padStart(2, "0");
|
|
132
|
+
return `#${grayHex}${grayHex}${grayHex}`;
|
|
133
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { type } from "@oh-my-pi/omptype";
|
|
4
|
+
import { adjustHsv, getCustomThemesDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
5
|
+
import { detectColorMode, resolveThemeColors } from "./color";
|
|
6
|
+
import darkThemeJson from "./dark.json" with { type: "json" };
|
|
7
|
+
import { defaultThemes } from "./defaults";
|
|
8
|
+
import lightThemeJson from "./light.json" with { type: "json" };
|
|
9
|
+
import { type ColorMode, type ThemeBg, type ThemeColor, type ThemeJson, themeJsonSchema } from "./schema";
|
|
10
|
+
import { normalizeSpinnerFramesOverride, type SymbolPreset } from "./symbols";
|
|
11
|
+
import { Theme } from "./theme-class";
|
|
12
|
+
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// Theme Loading
|
|
15
|
+
// ============================================================================
|
|
16
|
+
|
|
17
|
+
const BUILTIN_THEMES: Record<string, ThemeJson> = {
|
|
18
|
+
dark: darkThemeJson as ThemeJson,
|
|
19
|
+
light: lightThemeJson as ThemeJson,
|
|
20
|
+
...(defaultThemes as Record<string, ThemeJson>),
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function getBuiltinThemes(): Record<string, ThemeJson> {
|
|
24
|
+
return BUILTIN_THEMES;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function getAvailableThemes(): Promise<string[]> {
|
|
28
|
+
const themes = new Set<string>(Object.keys(getBuiltinThemes()));
|
|
29
|
+
const customThemesDir = getCustomThemesDir();
|
|
30
|
+
try {
|
|
31
|
+
const files = await fs.promises.readdir(customThemesDir);
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
if (file.endsWith(".json")) {
|
|
34
|
+
themes.add(file.slice(0, -5));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
} catch {
|
|
38
|
+
// Directory doesn't exist or isn't readable
|
|
39
|
+
}
|
|
40
|
+
return Array.from(themes).sort();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ThemeInfo {
|
|
44
|
+
name: string;
|
|
45
|
+
path: string | undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function getAvailableThemesWithPaths(): Promise<ThemeInfo[]> {
|
|
49
|
+
const result: ThemeInfo[] = [];
|
|
50
|
+
|
|
51
|
+
// Built-in themes (embedded, no file path)
|
|
52
|
+
for (const name of Object.keys(getBuiltinThemes())) {
|
|
53
|
+
result.push({ name, path: undefined });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Custom themes
|
|
57
|
+
const customThemesDir = getCustomThemesDir();
|
|
58
|
+
try {
|
|
59
|
+
const files = await fs.promises.readdir(customThemesDir);
|
|
60
|
+
for (const file of files) {
|
|
61
|
+
if (file.endsWith(".json")) {
|
|
62
|
+
const name = file.slice(0, -5);
|
|
63
|
+
if (!result.some(themeInfo => themeInfo.name === name)) {
|
|
64
|
+
result.push({ name, path: path.join(customThemesDir, file) });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch {
|
|
69
|
+
// Directory doesn't exist or isn't readable
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return result.sort((a, b) => a.name.localeCompare(b.name));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export async function loadThemeJson(name: string): Promise<ThemeJson> {
|
|
76
|
+
const builtinThemes = getBuiltinThemes();
|
|
77
|
+
if (name in builtinThemes) {
|
|
78
|
+
return builtinThemes[name];
|
|
79
|
+
}
|
|
80
|
+
const customThemesDir = getCustomThemesDir();
|
|
81
|
+
const themePath = path.join(customThemesDir, `${name}.json`);
|
|
82
|
+
let content: string;
|
|
83
|
+
try {
|
|
84
|
+
content = await Bun.file(themePath).text();
|
|
85
|
+
} catch (err) {
|
|
86
|
+
if (isEnoent(err)) throw new Error(`Theme not found: ${name}`);
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
let json: unknown;
|
|
90
|
+
try {
|
|
91
|
+
json = JSON.parse(content);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new Error(`Failed to parse theme ${name}: ${error}`);
|
|
94
|
+
}
|
|
95
|
+
let parsed: ThemeJson;
|
|
96
|
+
try {
|
|
97
|
+
parsed = themeJsonSchema(json) as ThemeJson;
|
|
98
|
+
if (parsed instanceof type.errors) {
|
|
99
|
+
throw new Error(parsed.summary);
|
|
100
|
+
}
|
|
101
|
+
} catch (error) {
|
|
102
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
103
|
+
// Extract color key information if available
|
|
104
|
+
const missingColorMatch = errorMessage.match(/missing keys: (.+)/i);
|
|
105
|
+
const missingColors: string[] = missingColorMatch ? missingColorMatch[1].split(",").map(s => s.trim()) : [];
|
|
106
|
+
|
|
107
|
+
let fullErrorMessage = `Invalid theme "${name}":\n`;
|
|
108
|
+
if (missingColors.length > 0) {
|
|
109
|
+
fullErrorMessage += `\nMissing required color tokens:\n`;
|
|
110
|
+
fullErrorMessage += missingColors.map(c => ` - ${c}`).join("\n");
|
|
111
|
+
fullErrorMessage += `\n\nPlease add these colors to your theme's "colors" object.`;
|
|
112
|
+
fullErrorMessage += `\nSee the built-in themes (dark.json, light.json) for reference values.`;
|
|
113
|
+
}
|
|
114
|
+
fullErrorMessage += `\n\nValidation error:\n - ${errorMessage}`;
|
|
115
|
+
|
|
116
|
+
throw new Error(fullErrorMessage);
|
|
117
|
+
}
|
|
118
|
+
return parsed;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export interface CreateThemeOptions {
|
|
122
|
+
mode?: ColorMode;
|
|
123
|
+
symbolPresetOverride?: SymbolPreset;
|
|
124
|
+
colorBlindMode?: boolean;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** HSV adjustment to shift green toward blue for colorblind mode (red-green colorblindness) */
|
|
128
|
+
const COLORBLIND_ADJUSTMENT = { h: 60, s: 0.71 };
|
|
129
|
+
|
|
130
|
+
export function createTheme(themeJson: ThemeJson, options: CreateThemeOptions = {}): Theme {
|
|
131
|
+
const { mode, symbolPresetOverride, colorBlindMode } = options;
|
|
132
|
+
const colorMode = mode ?? detectColorMode();
|
|
133
|
+
const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
|
|
134
|
+
|
|
135
|
+
if (colorBlindMode) {
|
|
136
|
+
const added = resolvedColors.toolDiffAdded;
|
|
137
|
+
if (typeof added === "string" && added.startsWith("#")) {
|
|
138
|
+
resolvedColors.toolDiffAdded = adjustHsv(added, COLORBLIND_ADJUSTMENT);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
|
|
143
|
+
const bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;
|
|
144
|
+
const bgColorKeys: Set<string> = new Set([
|
|
145
|
+
"selectedBg",
|
|
146
|
+
"userMessageBg",
|
|
147
|
+
"customMessageBg",
|
|
148
|
+
"toolPendingBg",
|
|
149
|
+
"toolSuccessBg",
|
|
150
|
+
"toolErrorBg",
|
|
151
|
+
"statusLineBg",
|
|
152
|
+
]);
|
|
153
|
+
for (const [key, value] of Object.entries(resolvedColors)) {
|
|
154
|
+
if (bgColorKeys.has(key)) {
|
|
155
|
+
bgColors[key as ThemeBg] = value;
|
|
156
|
+
} else {
|
|
157
|
+
fgColors[key as ThemeColor] = value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Extract symbol configuration - settings override takes precedence over theme
|
|
161
|
+
const symbolPreset: SymbolPreset = symbolPresetOverride ?? themeJson.symbols?.preset ?? "unicode";
|
|
162
|
+
const symbolOverrides = themeJson.symbols?.overrides ?? {};
|
|
163
|
+
const spinnerFramesOverrides = normalizeSpinnerFramesOverride(themeJson.symbols?.spinnerFrames);
|
|
164
|
+
return new Theme(fgColors, bgColors, colorMode, symbolPreset, symbolOverrides, spinnerFramesOverrides);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function loadTheme(name: string, options: CreateThemeOptions = {}): Promise<Theme> {
|
|
168
|
+
const themeJson = await loadThemeJson(name);
|
|
169
|
+
return createTheme(themeJson, options);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function getThemeByName(name: string): Promise<Theme | undefined> {
|
|
173
|
+
try {
|
|
174
|
+
return await loadTheme(name);
|
|
175
|
+
} catch {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import { type } from "@oh-my-pi/omptype";
|
|
2
|
+
import type { SpinnerFramesOverride } from "./symbols";
|
|
3
|
+
|
|
4
|
+
// ============================================================================
|
|
5
|
+
// Types & Schema
|
|
6
|
+
// ============================================================================
|
|
7
|
+
|
|
8
|
+
export type ColorValue = string | number;
|
|
9
|
+
|
|
10
|
+
const themeColorsSchema = type({
|
|
11
|
+
accent: "string | number",
|
|
12
|
+
border: "string | number",
|
|
13
|
+
borderAccent: "string | number",
|
|
14
|
+
borderMuted: "string | number",
|
|
15
|
+
success: "string | number",
|
|
16
|
+
error: "string | number",
|
|
17
|
+
warning: "string | number",
|
|
18
|
+
muted: "string | number",
|
|
19
|
+
dim: "string | number",
|
|
20
|
+
text: "string | number",
|
|
21
|
+
thinkingText: "string | number",
|
|
22
|
+
selectedBg: "string | number",
|
|
23
|
+
userMessageBg: "string | number",
|
|
24
|
+
userMessageText: "string | number",
|
|
25
|
+
customMessageBg: "string | number",
|
|
26
|
+
customMessageText: "string | number",
|
|
27
|
+
customMessageLabel: "string | number",
|
|
28
|
+
toolPendingBg: "string | number",
|
|
29
|
+
toolSuccessBg: "string | number",
|
|
30
|
+
toolErrorBg: "string | number",
|
|
31
|
+
toolTitle: "string | number",
|
|
32
|
+
toolOutput: "string | number",
|
|
33
|
+
mdHeading: "string | number",
|
|
34
|
+
mdLink: "string | number",
|
|
35
|
+
mdLinkUrl: "string | number",
|
|
36
|
+
mdCode: "string | number",
|
|
37
|
+
mdCodeBlock: "string | number",
|
|
38
|
+
mdCodeBlockBorder: "string | number",
|
|
39
|
+
mdQuote: "string | number",
|
|
40
|
+
mdQuoteBorder: "string | number",
|
|
41
|
+
mdHr: "string | number",
|
|
42
|
+
mdListBullet: "string | number",
|
|
43
|
+
toolDiffAdded: "string | number",
|
|
44
|
+
toolDiffRemoved: "string | number",
|
|
45
|
+
toolDiffContext: "string | number",
|
|
46
|
+
syntaxComment: "string | number",
|
|
47
|
+
syntaxKeyword: "string | number",
|
|
48
|
+
syntaxFunction: "string | number",
|
|
49
|
+
syntaxVariable: "string | number",
|
|
50
|
+
syntaxString: "string | number",
|
|
51
|
+
syntaxNumber: "string | number",
|
|
52
|
+
syntaxType: "string | number",
|
|
53
|
+
syntaxOperator: "string | number",
|
|
54
|
+
syntaxPunctuation: "string | number",
|
|
55
|
+
thinkingOff: "string | number",
|
|
56
|
+
thinkingMinimal: "string | number",
|
|
57
|
+
thinkingLow: "string | number",
|
|
58
|
+
thinkingMedium: "string | number",
|
|
59
|
+
thinkingHigh: "string | number",
|
|
60
|
+
thinkingXhigh: "string | number",
|
|
61
|
+
"thinkingMax?": "string | number",
|
|
62
|
+
bashMode: "string | number",
|
|
63
|
+
pythonMode: "string | number",
|
|
64
|
+
statusLineBg: "string | number",
|
|
65
|
+
statusLineSep: "string | number",
|
|
66
|
+
statusLineModel: "string | number",
|
|
67
|
+
statusLinePath: "string | number",
|
|
68
|
+
statusLineGitClean: "string | number",
|
|
69
|
+
statusLineGitDirty: "string | number",
|
|
70
|
+
statusLineContext: "string | number",
|
|
71
|
+
statusLineSpend: "string | number",
|
|
72
|
+
statusLineStaged: "string | number",
|
|
73
|
+
statusLineDirty: "string | number",
|
|
74
|
+
statusLineUntracked: "string | number",
|
|
75
|
+
statusLineOutput: "string | number",
|
|
76
|
+
statusLineCost: "string | number",
|
|
77
|
+
statusLineSubagents: "string | number",
|
|
78
|
+
});
|
|
79
|
+
const spinnerFramesSchema = type("unknown").narrow((value): value is SpinnerFramesOverride => {
|
|
80
|
+
if (Array.isArray(value)) {
|
|
81
|
+
return value.length >= 1 && value.every(item => typeof item === "string");
|
|
82
|
+
}
|
|
83
|
+
if (value && typeof value === "object") {
|
|
84
|
+
const obj = value as Record<string, unknown>;
|
|
85
|
+
const status = obj.status;
|
|
86
|
+
const activity = obj.activity;
|
|
87
|
+
if (status === undefined && activity === undefined) return false;
|
|
88
|
+
if (status !== undefined) {
|
|
89
|
+
if (!Array.isArray(status) || status.length < 1 || !status.every(item => typeof item === "string")) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (activity !== undefined) {
|
|
94
|
+
if (!Array.isArray(activity) || activity.length < 1 || !activity.every(item => typeof item === "string")) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
});
|
|
102
|
+
export const themeJsonSchema = type({
|
|
103
|
+
"$schema?": "string",
|
|
104
|
+
name: "string",
|
|
105
|
+
"vars?": { "[string]": "string | number" },
|
|
106
|
+
colors: themeColorsSchema,
|
|
107
|
+
"export?": {
|
|
108
|
+
"pageBg?": "string | number",
|
|
109
|
+
"cardBg?": "string | number",
|
|
110
|
+
"infoBg?": "string | number",
|
|
111
|
+
},
|
|
112
|
+
"symbols?": {
|
|
113
|
+
"preset?": "'unicode' | 'nerd' | 'ascii'",
|
|
114
|
+
"overrides?": { "[string]": "string" },
|
|
115
|
+
"spinnerFrames?": spinnerFramesSchema,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
export type ThemeJson = typeof themeJsonSchema.infer;
|
|
120
|
+
|
|
121
|
+
export type ThemeColor =
|
|
122
|
+
| "accent"
|
|
123
|
+
| "border"
|
|
124
|
+
| "borderAccent"
|
|
125
|
+
| "borderMuted"
|
|
126
|
+
| "success"
|
|
127
|
+
| "error"
|
|
128
|
+
| "warning"
|
|
129
|
+
| "muted"
|
|
130
|
+
| "dim"
|
|
131
|
+
| "text"
|
|
132
|
+
| "thinkingText"
|
|
133
|
+
| "userMessageText"
|
|
134
|
+
| "customMessageText"
|
|
135
|
+
| "customMessageLabel"
|
|
136
|
+
| "toolTitle"
|
|
137
|
+
| "toolOutput"
|
|
138
|
+
| "mdHeading"
|
|
139
|
+
| "mdLink"
|
|
140
|
+
| "mdLinkUrl"
|
|
141
|
+
| "mdCode"
|
|
142
|
+
| "mdCodeBlock"
|
|
143
|
+
| "mdCodeBlockBorder"
|
|
144
|
+
| "mdQuote"
|
|
145
|
+
| "mdQuoteBorder"
|
|
146
|
+
| "mdHr"
|
|
147
|
+
| "mdListBullet"
|
|
148
|
+
| "toolDiffAdded"
|
|
149
|
+
| "toolDiffRemoved"
|
|
150
|
+
| "toolDiffContext"
|
|
151
|
+
| "syntaxComment"
|
|
152
|
+
| "syntaxKeyword"
|
|
153
|
+
| "syntaxFunction"
|
|
154
|
+
| "syntaxVariable"
|
|
155
|
+
| "syntaxString"
|
|
156
|
+
| "syntaxNumber"
|
|
157
|
+
| "syntaxType"
|
|
158
|
+
| "syntaxOperator"
|
|
159
|
+
| "syntaxPunctuation"
|
|
160
|
+
| "thinkingOff"
|
|
161
|
+
| "thinkingMinimal"
|
|
162
|
+
| "thinkingLow"
|
|
163
|
+
| "thinkingMedium"
|
|
164
|
+
| "thinkingHigh"
|
|
165
|
+
| "thinkingXhigh"
|
|
166
|
+
| "thinkingMax"
|
|
167
|
+
| "bashMode"
|
|
168
|
+
| "pythonMode"
|
|
169
|
+
| "statusLineSep"
|
|
170
|
+
| "statusLineModel"
|
|
171
|
+
| "statusLinePath"
|
|
172
|
+
| "statusLineGitClean"
|
|
173
|
+
| "statusLineGitDirty"
|
|
174
|
+
| "statusLineContext"
|
|
175
|
+
| "statusLineSpend"
|
|
176
|
+
| "statusLineStaged"
|
|
177
|
+
| "statusLineDirty"
|
|
178
|
+
| "statusLineUntracked"
|
|
179
|
+
| "statusLineOutput"
|
|
180
|
+
| "statusLineCost"
|
|
181
|
+
| "statusLineSubagents";
|
|
182
|
+
|
|
183
|
+
/** Set of all valid ThemeColor string values for runtime validation */
|
|
184
|
+
const THEME_COLOR_RECORD = {
|
|
185
|
+
accent: true,
|
|
186
|
+
border: true,
|
|
187
|
+
borderAccent: true,
|
|
188
|
+
borderMuted: true,
|
|
189
|
+
success: true,
|
|
190
|
+
error: true,
|
|
191
|
+
warning: true,
|
|
192
|
+
muted: true,
|
|
193
|
+
dim: true,
|
|
194
|
+
text: true,
|
|
195
|
+
thinkingText: true,
|
|
196
|
+
userMessageText: true,
|
|
197
|
+
customMessageText: true,
|
|
198
|
+
customMessageLabel: true,
|
|
199
|
+
toolTitle: true,
|
|
200
|
+
toolOutput: true,
|
|
201
|
+
mdHeading: true,
|
|
202
|
+
mdLink: true,
|
|
203
|
+
mdLinkUrl: true,
|
|
204
|
+
mdCode: true,
|
|
205
|
+
mdCodeBlock: true,
|
|
206
|
+
mdCodeBlockBorder: true,
|
|
207
|
+
mdQuote: true,
|
|
208
|
+
mdQuoteBorder: true,
|
|
209
|
+
mdHr: true,
|
|
210
|
+
mdListBullet: true,
|
|
211
|
+
toolDiffAdded: true,
|
|
212
|
+
toolDiffRemoved: true,
|
|
213
|
+
toolDiffContext: true,
|
|
214
|
+
syntaxComment: true,
|
|
215
|
+
syntaxKeyword: true,
|
|
216
|
+
syntaxFunction: true,
|
|
217
|
+
syntaxVariable: true,
|
|
218
|
+
syntaxString: true,
|
|
219
|
+
syntaxNumber: true,
|
|
220
|
+
syntaxType: true,
|
|
221
|
+
syntaxOperator: true,
|
|
222
|
+
syntaxPunctuation: true,
|
|
223
|
+
thinkingOff: true,
|
|
224
|
+
thinkingMinimal: true,
|
|
225
|
+
thinkingLow: true,
|
|
226
|
+
thinkingMedium: true,
|
|
227
|
+
thinkingHigh: true,
|
|
228
|
+
thinkingXhigh: true,
|
|
229
|
+
thinkingMax: true,
|
|
230
|
+
bashMode: true,
|
|
231
|
+
pythonMode: true,
|
|
232
|
+
statusLineSep: true,
|
|
233
|
+
statusLineModel: true,
|
|
234
|
+
statusLinePath: true,
|
|
235
|
+
statusLineGitClean: true,
|
|
236
|
+
statusLineGitDirty: true,
|
|
237
|
+
statusLineContext: true,
|
|
238
|
+
statusLineSpend: true,
|
|
239
|
+
statusLineStaged: true,
|
|
240
|
+
statusLineDirty: true,
|
|
241
|
+
statusLineUntracked: true,
|
|
242
|
+
statusLineOutput: true,
|
|
243
|
+
statusLineCost: true,
|
|
244
|
+
statusLineSubagents: true,
|
|
245
|
+
} satisfies Record<ThemeColor, true>;
|
|
246
|
+
|
|
247
|
+
const VALID_THEME_COLORS: ReadonlySet<string> = new Set(Object.keys(THEME_COLOR_RECORD));
|
|
248
|
+
|
|
249
|
+
/** Check if a string is a valid ThemeColor value */
|
|
250
|
+
export function isValidThemeColor(color: string): color is ThemeColor {
|
|
251
|
+
return VALID_THEME_COLORS.has(color);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export type ThemeBg =
|
|
255
|
+
| "selectedBg"
|
|
256
|
+
| "userMessageBg"
|
|
257
|
+
| "customMessageBg"
|
|
258
|
+
| "toolPendingBg"
|
|
259
|
+
| "toolSuccessBg"
|
|
260
|
+
| "toolErrorBg"
|
|
261
|
+
| "statusLineBg";
|
|
262
|
+
|
|
263
|
+
export type ColorMode = "truecolor" | "256color";
|