mu-agent 0.1.7 → 0.1.10
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 +52 -0
- package/README.md +34 -29
- package/dist/bundle/chunks/anthropic-messages-4PAJY4R6.js +6 -0
- package/dist/bundle/chunks/anthropic.js +2 -2
- package/dist/bundle/chunks/azure-openai-responses-Y4BBWQ3N.js +2 -0
- package/dist/bundle/chunks/bedrock-converse-stream.js +12 -9
- package/dist/bundle/chunks/{chunk-TRDNDS6A.js → chunk-3V45WARH.js} +6 -3
- package/dist/bundle/chunks/{chunk-XDUWOHPL.js → chunk-5AGIVGSV.js} +3 -3
- package/dist/bundle/chunks/chunk-6QGV3IWD.js +6 -0
- package/dist/bundle/chunks/chunk-7CBJINYJ.js +20 -0
- package/dist/bundle/chunks/{chunk-DRQDKNXA.js → chunk-7DDKXS2Q.js} +1 -1
- package/dist/bundle/chunks/chunk-BSS6T6KQ.js +2 -0
- package/dist/bundle/chunks/chunk-CF5GWZHW.js +31 -0
- package/dist/bundle/chunks/chunk-DNNHZBQR.js +4 -0
- package/dist/bundle/chunks/chunk-FN6OEZYR.js +2 -0
- package/dist/bundle/chunks/chunk-FTJT2X6E.js +2 -0
- package/dist/bundle/chunks/chunk-HZNN636Z.js +130 -0
- package/dist/bundle/chunks/{chunk-4L3WN2XY.js → chunk-JXWJOIH4.js} +1 -1
- package/dist/bundle/chunks/{chunk-4O2SCVYF.js → chunk-KIDDRTZP.js} +4 -4
- package/dist/bundle/chunks/chunk-LMNWTX2L.js +11 -0
- package/dist/bundle/chunks/chunk-LPXNMA2W.js +2 -0
- package/dist/bundle/chunks/{chunk-6CYCI3EJ.js → chunk-LVVZS2NO.js} +1 -1
- package/dist/bundle/chunks/chunk-NN4BEPYX.js +2 -0
- package/dist/bundle/chunks/chunk-O7IPTASE.js +1221 -0
- package/dist/bundle/chunks/chunk-PMRZO46Y.js +2 -0
- package/dist/bundle/chunks/chunk-QOEEJKS4.js +9 -0
- package/dist/bundle/chunks/chunk-R4VPBBDS.js +2 -0
- package/dist/bundle/chunks/chunk-RAQ6TU76.js +2 -0
- package/dist/bundle/chunks/chunk-RDZVAGPR.js +2 -0
- package/dist/bundle/chunks/chunk-SGOTYFMT.js +91 -0
- package/dist/bundle/chunks/chunk-TMTOQ7H7.js +2 -0
- package/dist/bundle/chunks/chunk-UKVNB6C4.js +2 -0
- package/dist/bundle/chunks/chunk-VJLOQZKR.js +9 -0
- package/dist/bundle/chunks/chunk-WCGU75WO.js +11 -0
- package/dist/bundle/chunks/chunk-X234222Q.js +64 -0
- package/dist/bundle/chunks/cli-722OL3K5.js +51 -0
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-MEZHQVNP.js +2 -0
- package/dist/bundle/chunks/codemode-worker.js +478 -0
- package/dist/bundle/chunks/easter-egg-3d-6H34EDGX.js +2 -0
- package/dist/bundle/chunks/execute-KVSEIDWL.js +499 -0
- package/dist/bundle/chunks/github-copilot.js +1 -1
- package/dist/bundle/chunks/{google-generative-ai-C44KRRZ4.js → google-generative-ai-D2VJQBTK.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-25DUXPTJ.js → google-vertex-SQG2BO6C.js} +1 -1
- package/dist/bundle/chunks/https-proxy-agent-LVOO5TPL.js +13 -0
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{jiti-loader-C3NU2SDH.js → jiti-loader-GL4HJFAD.js} +1 -1
- package/dist/bundle/chunks/{jiti-static-loader-E6ANQD5T.js → jiti-static-loader-2RCDI6B4.js} +1 -1
- package/dist/bundle/chunks/{lib-HGQMNYVT.js → lib-4AVRLBHM.js} +2 -2
- package/dist/bundle/chunks/llama-cpp-classify-5YHLY3RC.js +22 -0
- package/dist/bundle/chunks/mistral-conversations-BDARVJTT.js +5 -0
- package/dist/bundle/chunks/{multipart-parser-SQ6GDJL7.js → multipart-parser-FMGQ7342.js} +1 -1
- package/dist/bundle/chunks/{node-TWVVAGPO.js → node-AUOGOBCN.js} +1 -1
- package/dist/bundle/chunks/node-KCMANYC2.js +16 -0
- package/dist/bundle/chunks/openai-chatgpt.js +86 -0
- package/dist/bundle/chunks/openai-codex-responses-CECGTDYD.js +10 -0
- package/dist/bundle/chunks/openai-codex.js +2 -2
- package/dist/bundle/chunks/openai-completions-UOA7MELD.js +7 -0
- package/dist/bundle/chunks/openai-decisions-HLM372N6.js +5 -0
- package/dist/bundle/chunks/openai-responses-TII7ZY5X.js +3 -0
- package/dist/bundle/chunks/{openrouter-images-JVEJN44Q.js → openrouter-images-7T452AS5.js} +1 -1
- package/dist/bundle/chunks/openrouter.js +2 -2
- package/dist/bundle/chunks/{pi-messages-TM5SOOIW.js → pi-messages-2UDKLKOO.js} +1 -1
- package/dist/bundle/chunks/radius.js +2 -2
- package/dist/bundle/chunks/runtime-O3ELDOM7.js +2 -0
- package/dist/bundle/chunks/{src-UK77SYZE.js → src-QKGKDFSU.js} +1 -1
- package/dist/bundle/chunks/typesafe-system-one-KNCICMO5.js +2 -0
- package/dist/bundle/chunks/virtual-modules-IKEARUOZ.js +2 -0
- package/dist/bundle/cli-runtime.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/dist/bundle/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/core/export-html/template.js +37 -16
- package/dist/modes/interactive/theme/dark.json +60 -73
- package/dist/modes/interactive/theme/light.json +58 -70
- package/dist/modes/interactive/theme/system-theme.d.ts +55 -0
- package/dist/modes/interactive/theme/system-theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/system-theme.js +546 -0
- package/dist/modes/interactive/theme/system-theme.js.map +1 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +43 -9
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme-controller.js +121 -79
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.d.ts +28 -0
- package/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.js +105 -0
- package/dist/modes/interactive/theme/theme-schema.js.map +1 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts +257 -0
- package/dist/modes/interactive/theme/theme-tokens.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-tokens.js +94 -0
- package/dist/modes/interactive/theme/theme-tokens.js.map +1 -0
- package/dist/modes/interactive/theme/theme.d.ts +47 -56
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +149 -143
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/native/darwin/prebuilds/darwin-arm64/darwin-platform.node +0 -0
- package/dist/native/darwin/prebuilds/darwin-x64/darwin-platform.node +0 -0
- package/docs/cli-integration.md +1 -1
- package/docs/cli.md +78 -13
- package/docs/codemode.md +230 -0
- package/docs/compaction.md +5 -5
- package/docs/configuration.md +2 -0
- package/docs/custom-provider.md +53 -1
- package/docs/docs.json +21 -1
- package/docs/environment-variables.md +5 -3
- package/docs/extensions.md +172 -10
- package/docs/index.md +1 -1
- package/docs/json.md +3 -3
- package/docs/keybindings.md +7 -6
- package/docs/llama-cpp.md +21 -1
- package/docs/mcp.md +272 -0
- package/docs/message-types.md +22 -4
- package/docs/models-configuration.md +187 -0
- package/docs/models.md +100 -2
- package/docs/nvidia-rtx-llama-cpp.md +96 -0
- package/docs/packages.md +1 -1
- package/docs/providers.md +66 -6
- package/docs/quickstart.md +16 -2
- package/docs/rpc-commands.md +9 -3
- package/docs/rpc.md +3 -1
- package/docs/sdk.md +8 -3
- package/docs/security.md +2 -0
- package/docs/session-format.md +5 -1
- package/docs/sessions.md +1 -1
- package/docs/settings.md +30 -8
- package/docs/terminal-setup.md +14 -0
- package/docs/themes.md +31 -7
- package/docs/tui.md +71 -1
- package/docs/usage.md +2 -2
- package/docs/virtual-models.md +114 -0
- package/docs/windows.md +2 -0
- package/examples/README.md +1 -4
- package/examples/extensions/README.md +1 -0
- package/examples/extensions/built-in-tool-renderer.ts +16 -40
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +5 -5
- package/examples/extensions/dynamic-resources/dynamic.json +1 -1
- package/examples/extensions/gondolin/package-lock.json +2 -2
- package/examples/extensions/gondolin/package.json +1 -1
- package/examples/extensions/jev-router.ts +113 -0
- package/examples/extensions/minimal-mode.ts +28 -101
- package/examples/extensions/overlay-test.ts +2 -2
- package/examples/extensions/plan-mode/index.ts +6 -2
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/subagent/index.ts +4 -6
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/rpc-client.ts +1 -1
- package/examples/rpc-extension-ui.ts +1 -1
- package/examples/sdk/14-codemode-mcp.ts +61 -0
- package/examples/sdk/README.md +2 -1
- package/judge/THIRD_PARTY_NOTICES.md +63 -0
- package/judge/dist/auth.js +169 -123
- package/judge/dist/import.js +36 -30
- package/judge/dist/setup.js +250 -120
- package/judge/manifest.json +1008 -316
- package/judge/{dist/kyrn-judge.js → mu/index.js} +6933 -5995
- package/judge/themes/mu-dark.json +80 -0
- package/judge/themes/mu-light.json +80 -0
- package/kyrn/bin/kyrn-doctor +75 -25
- package/kyrn/bin/kyrn-judge-local +2 -2
- package/kyrn/bin/kyrn-ledger +1 -1
- package/kyrn/bin/mu.mjs +149 -107
- package/package.json +4 -5
- package/dist/bundle/chunks/anthropic-messages-VMESUHQ7.js +0 -6
- package/dist/bundle/chunks/azure-openai-responses-K232MQFF.js +0 -2
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
- package/dist/bundle/chunks/chunk-AYFRR3OD.js +0 -2
- package/dist/bundle/chunks/chunk-DTD7JQ7Y.js +0 -42
- package/dist/bundle/chunks/chunk-FU4XQUSL.js +0 -11
- package/dist/bundle/chunks/chunk-GNYZK6XN.js +0 -2
- package/dist/bundle/chunks/chunk-HEMXN7VJ.js +0 -1653
- package/dist/bundle/chunks/chunk-MVPXOVOJ.js +0 -2
- package/dist/bundle/chunks/chunk-NUHFSC37.js +0 -25
- package/dist/bundle/chunks/chunk-V7IXECST.js +0 -11
- package/dist/bundle/chunks/chunk-XNGRGP62.js +0 -2
- package/dist/bundle/chunks/cloudflare-workers-ai-system-one-NR2TDCXU.js +0 -2
- package/dist/bundle/chunks/https-proxy-agent-2VXB7436.js +0 -13
- package/dist/bundle/chunks/mistral-conversations-6IHQOR4R.js +0 -5
- package/dist/bundle/chunks/node-O3RIBJRH.js +0 -16
- package/dist/bundle/chunks/openai-codex-responses-3UAYOSSR.js +0 -10
- package/dist/bundle/chunks/openai-completions-FIEQGWAH.js +0 -7
- package/dist/bundle/chunks/openai-responses-OZJXLHFC.js +0 -2
- package/dist/bundle/chunks/typesafe-system-one-VKYX4OGY.js +0 -2
- package/dist/bundle/chunks/virtual-modules-V7SXVVLP.js +0 -2
- package/dist/modes/interactive/theme/theme-json.d.ts +0 -85
- package/dist/modes/interactive/theme/theme-json.d.ts.map +0 -1
- package/dist/modes/interactive/theme/theme-json.js +0 -132
- package/dist/modes/interactive/theme/theme-json.js.map +0 -1
- package/dist/modes/interactive/theme/theme-schema.json +0 -361
- package/examples/plugins/pi-example-plugin/README.md +0 -38
- package/examples/plugins/pi-example-plugin/package.json +0 -10
- package/examples/plugins/pi-example-plugin/src/contract.ts +0 -13
- package/examples/plugins/pi-example-plugin/src/session.ts +0 -24
- package/examples/plugins/pi-example-plugin/src/tui.ts +0 -31
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import { backgroundAnsi, colorToHex, colorToOklch,
|
|
3
|
+
import { backgroundAnsi, colorToHex, colorToOklch, foregroundAnsi, getTerminalColorMode, indexedColor, mixColors, parseColor, rgbColor, styleTextWithAnsi, } from "@earendil-works/pi-tui";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { getCustomThemesDir, getThemesDir } from "../../../config.js";
|
|
6
6
|
import { closeWatcher, watchWithErrorHandler } from "../../../utils/fs-watch.js";
|
|
7
7
|
import { highlight, supportsLanguage } from "../../../utils/syntax-highlight.js";
|
|
8
8
|
import { stripBom } from "../../../utils/text.js";
|
|
9
|
+
import { generateSystemThemeColors, SYSTEM_THEME_NAME, terminalAppearance } from "./system-theme.js";
|
|
10
|
+
import { THEME_TOKENS, } from "./theme-tokens.js";
|
|
11
|
+
export { SYSTEM_THEME_NAME } from "./system-theme.js";
|
|
9
12
|
let themeJsonValidator;
|
|
10
13
|
/**
|
|
11
14
|
* Install full theme validation. Without it, documents are accepted as-is, which is what built-in
|
|
@@ -19,7 +22,7 @@ export function setThemeJsonValidator(validator) {
|
|
|
19
22
|
// Color Utilities
|
|
20
23
|
// ============================================================================
|
|
21
24
|
function resolveVarRefs(value, vars, visited = new Set()) {
|
|
22
|
-
if (typeof value === "number" || value === "" || value.startsWith("#") || /^
|
|
25
|
+
if (typeof value === "number" || value === "" || value.startsWith("#") || /^ok(lch|hsl)\(/i.test(value)) {
|
|
23
26
|
return value;
|
|
24
27
|
}
|
|
25
28
|
if (visited.has(value)) {
|
|
@@ -38,22 +41,45 @@ function resolveThemeColors(colors, vars = {}) {
|
|
|
38
41
|
}
|
|
39
42
|
return resolved;
|
|
40
43
|
}
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
44
|
+
function withThemeTokenFallbacks(values) {
|
|
45
|
+
const resolved = { ...values };
|
|
46
|
+
for (const [rawName, descriptor] of Object.entries(THEME_TOKENS)) {
|
|
47
|
+
if (!("fallback" in descriptor))
|
|
48
|
+
continue;
|
|
49
|
+
const name = rawName;
|
|
50
|
+
if (resolved[name] !== undefined)
|
|
51
|
+
continue;
|
|
52
|
+
const fallback = descriptor.fallback;
|
|
53
|
+
const fallbackValue = resolved[fallback];
|
|
54
|
+
if (fallbackValue === undefined) {
|
|
55
|
+
throw new Error(`Theme token ${rawName} has unresolved fallback ${descriptor.fallback}`);
|
|
56
|
+
}
|
|
57
|
+
resolved[name] = fallbackValue;
|
|
58
|
+
}
|
|
59
|
+
return resolved;
|
|
50
60
|
}
|
|
51
|
-
// Replaced (never mutated) on update, so themes can cache resolved colors
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
// The terminal's reported colors. Replaced (never mutated) on update, so themes can cache resolved colors
|
|
62
|
+
// by identity.
|
|
63
|
+
let terminalColors = {};
|
|
64
|
+
// While the terminal color query is in flight, the system theme renders in grayscale.
|
|
65
|
+
let terminalColorsPending = false;
|
|
66
|
+
// The terminal's last light/dark report (mode 2031). Only used while it has not reported a background.
|
|
67
|
+
let terminalColorScheme;
|
|
68
|
+
/**
|
|
69
|
+
* Record the terminal's reported colors. Themes use the default colors for tokens set to "" (terminal
|
|
70
|
+
* default); the system theme is generated from all of them. Ends the pending state.
|
|
71
|
+
*/
|
|
72
|
+
export function setTerminalColors(colors) {
|
|
73
|
+
terminalColors = { ...colors };
|
|
74
|
+
terminalColorsPending = false;
|
|
75
|
+
}
|
|
76
|
+
/** Record the terminal's light/dark report, the fallback for terminals that do not report their background. */
|
|
77
|
+
export function setTerminalColorScheme(scheme) {
|
|
78
|
+
terminalColorScheme = scheme;
|
|
79
|
+
}
|
|
80
|
+
/** Render the system theme in grayscale until `setTerminalColors()` reports the terminal's colors. */
|
|
81
|
+
export function markTerminalColorsPending() {
|
|
82
|
+
terminalColorsPending = true;
|
|
57
83
|
}
|
|
58
84
|
/** Assumed terminal default colors when the terminal does not report them. */
|
|
59
85
|
const GUESSED_DEFAULT_COLORS = {
|
|
@@ -94,6 +120,8 @@ export class Theme {
|
|
|
94
120
|
concreteColors = {};
|
|
95
121
|
defaultForegroundTokens = [];
|
|
96
122
|
defaultBackgroundTokens = [];
|
|
123
|
+
// Foreground tokens rendered faint (SGR 2) on top of their color.
|
|
124
|
+
dimTokens;
|
|
97
125
|
ownAppearance;
|
|
98
126
|
resolvedColors;
|
|
99
127
|
constructor(fgColors, bgColors, mode, options = {}) {
|
|
@@ -101,14 +129,8 @@ export class Theme {
|
|
|
101
129
|
this.sourcePath = options.sourcePath;
|
|
102
130
|
this.sourceInfo = options.sourceInfo;
|
|
103
131
|
this.mode = mode;
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
scrollbarTrack: fgColors.scrollbarTrack ?? fgColors.muted,
|
|
107
|
-
scrollbarThumb: fgColors.scrollbarThumb ?? fgColors.text,
|
|
108
|
-
thinkingMax: fgColors.thinkingMax ?? fgColors.thinkingXhigh,
|
|
109
|
-
searchMatchText: fgColors.searchMatchText ?? fgColors.text,
|
|
110
|
-
};
|
|
111
|
-
const backgrounds = { ...bgColors, searchMatchBg: bgColors.searchMatchBg ?? bgColors.selectedBg };
|
|
132
|
+
this.dimTokens = new Set(options.dim);
|
|
133
|
+
const { fgColors: foregrounds, bgColors: backgrounds } = splitThemeColors(withThemeTokenFallbacks({ ...fgColors, ...bgColors }));
|
|
112
134
|
const concreteForegrounds = [];
|
|
113
135
|
const concreteBackgrounds = [];
|
|
114
136
|
// Returns the escape sequence for the token's own slot.
|
|
@@ -132,33 +154,41 @@ export class Theme {
|
|
|
132
154
|
}
|
|
133
155
|
/**
|
|
134
156
|
* The background the theme is designed for: declared in the theme JSON, detected from its colors,
|
|
135
|
-
* or, for themes without usable colors,
|
|
157
|
+
* or, for themes without usable colors, the terminal's appearance.
|
|
136
158
|
*/
|
|
137
159
|
get appearance() {
|
|
138
|
-
|
|
139
|
-
return this.ownAppearance;
|
|
140
|
-
const background = terminalDefaultColors.background;
|
|
141
|
-
return background ? getThemeForRgbColor(colorToRgb(background)) : "dark";
|
|
160
|
+
return this.ownAppearance ?? getTerminalTheme();
|
|
142
161
|
}
|
|
143
162
|
/**
|
|
144
163
|
* Concrete colors for all tokens. Tokens set to "" (terminal default) use the terminal's reported
|
|
145
|
-
* default colors, or a guess based on `appearance` when the terminal did not report them.
|
|
164
|
+
* default colors, or a guess based on `appearance` when the terminal did not report them. Faint
|
|
165
|
+
* tokens are approximated by mixing their color toward the background.
|
|
146
166
|
*/
|
|
147
167
|
get colors() {
|
|
148
|
-
const terminal =
|
|
168
|
+
const terminal = terminalColors;
|
|
149
169
|
if (this.resolvedColors?.terminal !== terminal) {
|
|
150
170
|
const guess = GUESSED_DEFAULT_COLORS[this.appearance];
|
|
171
|
+
const toColor = (rgb, fallback) => rgb ? rgbColor(rgb.r, rgb.g, rgb.b) : fallback;
|
|
172
|
+
const foreground = toColor(terminal.foreground, guess.foreground);
|
|
173
|
+
const background = toColor(terminal.background, guess.background);
|
|
151
174
|
const colors = { ...this.concreteColors };
|
|
152
175
|
for (const token of this.defaultForegroundTokens)
|
|
153
|
-
colors[token] =
|
|
176
|
+
colors[token] = foreground;
|
|
154
177
|
for (const token of this.defaultBackgroundTokens)
|
|
155
|
-
colors[token] =
|
|
178
|
+
colors[token] = background;
|
|
179
|
+
for (const token of this.dimTokens) {
|
|
180
|
+
const color = colors[token];
|
|
181
|
+
if (color)
|
|
182
|
+
colors[token] = mixColors(color, background, 0.4);
|
|
183
|
+
}
|
|
156
184
|
this.resolvedColors = { terminal, colors: Object.freeze(colors) };
|
|
157
185
|
}
|
|
158
186
|
return this.resolvedColors.colors;
|
|
159
187
|
}
|
|
160
188
|
style(text, options) {
|
|
161
189
|
const { fg, bg } = options;
|
|
190
|
+
if (typeof fg === "string" && this.dimTokens.has(fg))
|
|
191
|
+
options = { ...options, dim: true };
|
|
162
192
|
return styleTextWithAnsi(text, fg === undefined
|
|
163
193
|
? undefined
|
|
164
194
|
: typeof fg === "string"
|
|
@@ -171,6 +201,8 @@ export class Theme {
|
|
|
171
201
|
}
|
|
172
202
|
fg(color, text) {
|
|
173
203
|
const ansi = this.tokenAnsi(this.fgAnsi, color);
|
|
204
|
+
if (this.dimTokens.has(color))
|
|
205
|
+
return `${ansi}\x1b[2m${text}\x1b[22;39m`;
|
|
174
206
|
return `${ansi}${text}\x1b[39m`;
|
|
175
207
|
}
|
|
176
208
|
bg(color, text) {
|
|
@@ -198,8 +230,10 @@ export class Theme {
|
|
|
198
230
|
strikethrough(text) {
|
|
199
231
|
return chalk.strikethrough(text);
|
|
200
232
|
}
|
|
233
|
+
/** Opening escape sequence for a foreground token. Faint tokens include SGR 2, which `\x1b[22m` closes. */
|
|
201
234
|
getFgAnsi(color) {
|
|
202
|
-
|
|
235
|
+
const ansi = this.tokenAnsi(this.fgAnsi, color);
|
|
236
|
+
return this.dimTokens.has(color) ? `${ansi}\x1b[2m` : ansi;
|
|
203
237
|
}
|
|
204
238
|
getBgAnsi(color) {
|
|
205
239
|
return this.tokenAnsi(this.bgAnsi, color);
|
|
@@ -262,7 +296,8 @@ export function getAvailableThemesWithPaths() {
|
|
|
262
296
|
seen.add(themeInfo.name);
|
|
263
297
|
result.push(themeInfo);
|
|
264
298
|
};
|
|
265
|
-
// Built-in themes
|
|
299
|
+
// Built-in themes. The system theme is generated, so it has no file.
|
|
300
|
+
addTheme({ name: SYSTEM_THEME_NAME, path: undefined });
|
|
266
301
|
for (const name of Object.keys(getBuiltinThemes())) {
|
|
267
302
|
addTheme({ name, path: path.join(themesDir, `${name}.json`) });
|
|
268
303
|
}
|
|
@@ -273,7 +308,8 @@ export function getAvailableThemesWithPaths() {
|
|
|
273
308
|
for (const [name, theme] of registeredThemes.entries()) {
|
|
274
309
|
addTheme({ name, path: theme.sourcePath });
|
|
275
310
|
}
|
|
276
|
-
|
|
311
|
+
// The system theme comes first: it is the default and adapts to every terminal.
|
|
312
|
+
return result.sort((a, b) => a.name === SYSTEM_THEME_NAME ? -1 : b.name === SYSTEM_THEME_NAME ? 1 : a.name.localeCompare(b.name));
|
|
277
313
|
}
|
|
278
314
|
function getCustomThemeInfos() {
|
|
279
315
|
const customThemesDir = getCustomThemesDir();
|
|
@@ -343,40 +379,52 @@ function loadThemeJson(name) {
|
|
|
343
379
|
const content = fs.readFileSync(themePath, "utf-8");
|
|
344
380
|
return parseThemeJsonContent(name, content);
|
|
345
381
|
}
|
|
346
|
-
function
|
|
347
|
-
const colorMode = mode ?? getTerminalColorMode();
|
|
348
|
-
const resolvedColors = resolveThemeColors(withThemeColorFallbacks(themeJson.colors), themeJson.vars);
|
|
382
|
+
function splitThemeColors(colors) {
|
|
349
383
|
const fgColors = {};
|
|
350
384
|
const bgColors = {};
|
|
351
|
-
const
|
|
352
|
-
"
|
|
353
|
-
"searchMatchBg",
|
|
354
|
-
"userMessageBg",
|
|
355
|
-
"customMessageBg",
|
|
356
|
-
"toolPendingBg",
|
|
357
|
-
"toolSuccessBg",
|
|
358
|
-
"toolErrorBg",
|
|
359
|
-
]);
|
|
360
|
-
for (const [key, value] of Object.entries(resolvedColors)) {
|
|
361
|
-
if (bgColorKeys.has(key)) {
|
|
385
|
+
for (const [key, value] of Object.entries(colors)) {
|
|
386
|
+
if (THEME_TOKENS[key].slot === "background") {
|
|
362
387
|
bgColors[key] = value;
|
|
363
388
|
}
|
|
364
389
|
else {
|
|
365
390
|
fgColors[key] = value;
|
|
366
391
|
}
|
|
367
392
|
}
|
|
393
|
+
return { fgColors, bgColors };
|
|
394
|
+
}
|
|
395
|
+
function createTheme(themeJson, mode, sourcePath) {
|
|
396
|
+
const colorMode = mode ?? getTerminalColorMode();
|
|
397
|
+
const resolvedColors = withThemeTokenFallbacks(resolveThemeColors(themeJson.colors, themeJson.vars));
|
|
398
|
+
const { fgColors, bgColors } = splitThemeColors(resolvedColors);
|
|
368
399
|
return new Theme(fgColors, bgColors, colorMode, {
|
|
369
400
|
name: themeJson.name,
|
|
370
401
|
sourcePath,
|
|
371
402
|
appearance: themeJson.appearance,
|
|
372
403
|
});
|
|
373
404
|
}
|
|
405
|
+
/** Generate the system theme from the terminal's reported colors (grayscale while they are pending). */
|
|
406
|
+
function createSystemTheme(mode) {
|
|
407
|
+
const generated = generateSystemThemeColors({
|
|
408
|
+
...terminalColors,
|
|
409
|
+
saturation: terminalColorsPending ? 0 : 1,
|
|
410
|
+
appearanceHint: getTerminalTheme(),
|
|
411
|
+
});
|
|
412
|
+
const { fgColors, bgColors } = splitThemeColors(withThemeTokenFallbacks(generated.colors));
|
|
413
|
+
return new Theme(fgColors, bgColors, mode ?? getTerminalColorMode(), {
|
|
414
|
+
name: SYSTEM_THEME_NAME,
|
|
415
|
+
appearance: generated.appearance,
|
|
416
|
+
dim: generated.dim,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
374
419
|
export function loadThemeFromPath(themePath, mode) {
|
|
375
420
|
const content = fs.readFileSync(themePath, "utf-8");
|
|
376
421
|
const themeJson = parseThemeJsonContent(themePath, content);
|
|
377
422
|
return createTheme(themeJson, mode, themePath);
|
|
378
423
|
}
|
|
379
424
|
function loadTheme(name, mode) {
|
|
425
|
+
// The system theme name is reserved: it takes precedence over custom themes of the same name.
|
|
426
|
+
if (name === SYSTEM_THEME_NAME)
|
|
427
|
+
return createSystemTheme(mode);
|
|
380
428
|
const registeredTheme = registeredThemes.get(name);
|
|
381
429
|
if (registeredTheme) {
|
|
382
430
|
return registeredTheme;
|
|
@@ -417,95 +465,45 @@ export function resolveThemeSetting(themeSetting, terminalTheme) {
|
|
|
417
465
|
return themeSetting;
|
|
418
466
|
return undefined;
|
|
419
467
|
}
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
return
|
|
434
|
-
|
|
435
|
-
return 0.2126 * toLinear(r) + 0.7152 * toLinear(g) + 0.0722 * toLinear(b);
|
|
436
|
-
}
|
|
437
|
-
function getAnsiColorLuminance(index) {
|
|
438
|
-
return getRgbColorLuminance(colorToRgb(indexedColor(index)));
|
|
439
|
-
}
|
|
440
|
-
export function getThemeForRgbColor(rgb) {
|
|
441
|
-
return getRgbColorLuminance(rgb) >= 0.5 ? "light" : "dark";
|
|
442
|
-
}
|
|
443
|
-
export function detectTerminalBackgroundFromEnv(options = {}) {
|
|
444
|
-
const env = options.env ?? process.env;
|
|
445
|
-
const colorfgbg = env.COLORFGBG || "";
|
|
446
|
-
const bg = getColorFgBgBackgroundIndex(colorfgbg);
|
|
447
|
-
if (bg !== undefined) {
|
|
448
|
-
return {
|
|
449
|
-
theme: getAnsiColorLuminance(bg) >= 0.5 ? "light" : "dark",
|
|
450
|
-
source: "COLORFGBG",
|
|
451
|
-
detail: `background color index ${bg}`,
|
|
452
|
-
confidence: "high",
|
|
453
|
-
};
|
|
454
|
-
}
|
|
455
|
-
return {
|
|
456
|
-
theme: "dark",
|
|
457
|
-
source: "fallback",
|
|
458
|
-
detail: "no terminal background hint found",
|
|
459
|
-
confidence: "low",
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
export async function detectTerminalBackgroundTheme({ ui, timeoutMs, env, }) {
|
|
463
|
-
try {
|
|
464
|
-
const rgb = await ui.queryTerminalBackgroundColor({ timeoutMs });
|
|
465
|
-
if (rgb) {
|
|
466
|
-
return {
|
|
467
|
-
theme: getThemeForRgbColor(rgb),
|
|
468
|
-
source: "terminal background",
|
|
469
|
-
detail: `OSC 11 background rgb(${rgb.r}, ${rgb.g}, ${rgb.b})`,
|
|
470
|
-
confidence: "high",
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
catch {
|
|
475
|
-
// Fall back to environment-based detection when the terminal query fails.
|
|
476
|
-
}
|
|
477
|
-
return detectTerminalBackgroundFromEnv({ env });
|
|
468
|
+
/**
|
|
469
|
+
* Dark or light from the `COLORFGBG` environment variable some terminals set, or undefined without a
|
|
470
|
+
* usable background index. The value is `fg;bg` or `fg;xpm;bg` (rxvt), where a field is an ANSI color
|
|
471
|
+
* index or `default` when the color is not in the palette. The index refers to the terminal's own palette,
|
|
472
|
+
* whose colors are unknown here, so it is classified by index like Vim does: 0-6 and 8 (bright black, e.g.
|
|
473
|
+
* Solarized Dark's background) are dark, 7 and 9-15 are light.
|
|
474
|
+
*/
|
|
475
|
+
export function detectColorFgBgTheme(env = process.env) {
|
|
476
|
+
const bg = env.COLORFGBG?.split(";").at(-1)?.trim();
|
|
477
|
+
if (!bg || !/^\d{1,2}$/.test(bg))
|
|
478
|
+
return undefined;
|
|
479
|
+
const index = Number(bg);
|
|
480
|
+
if (index > 15)
|
|
481
|
+
return undefined;
|
|
482
|
+
return index <= 6 || index === 8 ? "dark" : "light";
|
|
478
483
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const colorScheme = await colorSchemePromise;
|
|
490
|
-
if (colorScheme)
|
|
491
|
-
return colorScheme;
|
|
492
|
-
}
|
|
493
|
-
catch {
|
|
494
|
-
// Fall back to the concurrently queried OSC 11 / COLORFGBG detection.
|
|
495
|
-
}
|
|
496
|
-
return (await backgroundThemePromise).theme;
|
|
484
|
+
/**
|
|
485
|
+
* Whether the terminal is dark or light. The background it renders decides, classified the same way the
|
|
486
|
+
* system theme does. Without a reported background: the terminal's light/dark report, then COLORFGBG,
|
|
487
|
+
* then dark.
|
|
488
|
+
*/
|
|
489
|
+
export function detectTerminalTheme(colors = {}, reportedScheme, env = process.env) {
|
|
490
|
+
const { background, foreground } = colors;
|
|
491
|
+
if (background)
|
|
492
|
+
return terminalAppearance(background, foreground);
|
|
493
|
+
return reportedScheme ?? detectColorFgBgTheme(env) ?? "dark";
|
|
497
494
|
}
|
|
498
|
-
|
|
499
|
-
|
|
495
|
+
/** Whether the terminal is dark or light, from everything it reported so far. See `detectTerminalTheme()`. */
|
|
496
|
+
export function getTerminalTheme() {
|
|
497
|
+
return detectTerminalTheme(terminalColors, terminalColorScheme);
|
|
500
498
|
}
|
|
501
499
|
// ============================================================================
|
|
502
500
|
// Global Theme Instance
|
|
503
501
|
// ============================================================================
|
|
504
|
-
// Use globalThis to share theme across module loaders (
|
|
502
|
+
// Use globalThis to share theme across module loaders (node + jiti in dev mode)
|
|
505
503
|
const THEME_KEY = Symbol.for("@earendil-works/pi-coding-agent:theme");
|
|
506
504
|
const THEME_KEY_OLD = Symbol.for("@mariozechner/pi-coding-agent:theme");
|
|
507
505
|
// Export theme as a getter that reads from globalThis
|
|
508
|
-
// This ensures all module instances (
|
|
506
|
+
// This ensures all module instances (node, jiti) see the same theme
|
|
509
507
|
export const theme = new Proxy({}, {
|
|
510
508
|
get(_target, prop) {
|
|
511
509
|
const t = globalThis[THEME_KEY];
|
|
@@ -533,7 +531,7 @@ export function setRegisteredThemes(themes) {
|
|
|
533
531
|
}
|
|
534
532
|
}
|
|
535
533
|
export function initTheme(themeName, enableWatcher = false) {
|
|
536
|
-
const name = themeName ??
|
|
534
|
+
const name = themeName ?? SYSTEM_THEME_NAME;
|
|
537
535
|
currentThemeName = name;
|
|
538
536
|
try {
|
|
539
537
|
setGlobalTheme(loadTheme(name));
|
|
@@ -542,9 +540,9 @@ export function initTheme(themeName, enableWatcher = false) {
|
|
|
542
540
|
}
|
|
543
541
|
}
|
|
544
542
|
catch (_error) {
|
|
545
|
-
// Theme is invalid - fall back to
|
|
546
|
-
currentThemeName =
|
|
547
|
-
setGlobalTheme(loadTheme(
|
|
543
|
+
// Theme is invalid - fall back to the system theme silently
|
|
544
|
+
currentThemeName = SYSTEM_THEME_NAME;
|
|
545
|
+
setGlobalTheme(loadTheme(SYSTEM_THEME_NAME));
|
|
548
546
|
// Don't start watcher for fallback theme
|
|
549
547
|
}
|
|
550
548
|
}
|
|
@@ -561,9 +559,9 @@ export function setTheme(name, enableWatcher = false) {
|
|
|
561
559
|
return { success: true };
|
|
562
560
|
}
|
|
563
561
|
catch (error) {
|
|
564
|
-
// Theme is invalid - fall back to
|
|
565
|
-
currentThemeName =
|
|
566
|
-
setGlobalTheme(loadTheme(
|
|
562
|
+
// Theme is invalid - fall back to the system theme
|
|
563
|
+
currentThemeName = SYSTEM_THEME_NAME;
|
|
564
|
+
setGlobalTheme(loadTheme(SYSTEM_THEME_NAME));
|
|
567
565
|
// Don't start watcher for fallback theme
|
|
568
566
|
return {
|
|
569
567
|
success: false,
|
|
@@ -585,7 +583,10 @@ export function onThemeChange(callback) {
|
|
|
585
583
|
function startThemeWatcher() {
|
|
586
584
|
stopThemeWatcher();
|
|
587
585
|
// Only watch if it's a custom theme (not built-in)
|
|
588
|
-
if (!currentThemeName ||
|
|
586
|
+
if (!currentThemeName ||
|
|
587
|
+
currentThemeName === "dark" ||
|
|
588
|
+
currentThemeName === "light" ||
|
|
589
|
+
currentThemeName === SYSTEM_THEME_NAME) {
|
|
589
590
|
return;
|
|
590
591
|
}
|
|
591
592
|
const customThemesDir = getCustomThemesDir();
|
|
@@ -659,28 +660,30 @@ export function stopThemeWatcher() {
|
|
|
659
660
|
* Used by HTML export to generate CSS custom properties.
|
|
660
661
|
*/
|
|
661
662
|
export function getResolvedThemeColors(themeName) {
|
|
662
|
-
const colors = loadTheme(themeName ?? currentThemeName ??
|
|
663
|
+
const colors = loadTheme(themeName ?? currentThemeName ?? SYSTEM_THEME_NAME).colors;
|
|
663
664
|
return Object.fromEntries(Object.entries(colors).map(([token, color]) => [token, colorToHex(color)]));
|
|
664
665
|
}
|
|
665
666
|
/**
|
|
666
667
|
* Check if a theme is a "light" theme (for CSS that needs light/dark variants).
|
|
667
668
|
*/
|
|
668
669
|
export function isLightTheme(themeName) {
|
|
669
|
-
return loadTheme(themeName ?? currentThemeName ??
|
|
670
|
+
return loadTheme(themeName ?? currentThemeName ?? SYSTEM_THEME_NAME).appearance === "light";
|
|
670
671
|
}
|
|
671
672
|
/**
|
|
672
673
|
* Get explicit export colors from theme JSON, if specified.
|
|
673
674
|
* Returns undefined for each color that isn't explicitly set.
|
|
674
675
|
*/
|
|
675
676
|
export function getThemeExportColors(themeName) {
|
|
676
|
-
const name = themeName ?? currentThemeName ??
|
|
677
|
+
const name = themeName ?? currentThemeName ?? SYSTEM_THEME_NAME;
|
|
678
|
+
if (name === SYSTEM_THEME_NAME)
|
|
679
|
+
return {};
|
|
677
680
|
try {
|
|
678
681
|
const themeJson = loadThemeJson(name);
|
|
679
682
|
const exportSection = themeJson.export;
|
|
680
683
|
if (!exportSection)
|
|
681
684
|
return {};
|
|
682
685
|
const vars = themeJson.vars ?? {};
|
|
683
|
-
// Export colors end up in CSS, which understands hex
|
|
686
|
+
// Export colors end up in CSS, which understands hex and oklch() values directly but not okhsl().
|
|
684
687
|
const resolve = (value) => {
|
|
685
688
|
if (value === undefined)
|
|
686
689
|
return undefined;
|
|
@@ -689,6 +692,8 @@ export function getThemeExportColors(themeName) {
|
|
|
689
692
|
return colorToHex(indexedColor(resolved));
|
|
690
693
|
if (resolved === "")
|
|
691
694
|
return undefined;
|
|
695
|
+
if (/^okhsl\(/i.test(resolved))
|
|
696
|
+
return colorToHex(parseColor(resolved));
|
|
692
697
|
return resolved;
|
|
693
698
|
};
|
|
694
699
|
return {
|
|
@@ -711,6 +716,7 @@ function buildCliHighlightTheme(t) {
|
|
|
711
716
|
number: (s) => t.fg("syntaxNumber", s),
|
|
712
717
|
regexp: (s) => t.fg("syntaxString", s),
|
|
713
718
|
string: (s) => t.fg("syntaxString", s),
|
|
719
|
+
subst: (s) => t.fg("text", s),
|
|
714
720
|
comment: (s) => t.fg("syntaxComment", s),
|
|
715
721
|
doctag: (s) => t.fg("syntaxComment", s),
|
|
716
722
|
meta: (s) => t.fg("muted", s),
|