@oh-my-pi/pi-coding-agent 17.2.10 → 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 +53 -0
- package/dist/{CHANGELOG-rhwzpexa.md → CHANGELOG-k9ghy5sn.md} +53 -0
- package/dist/cli.js +10775 -10762
- package/dist/types/advisor/runtime.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +11 -0
- package/dist/types/capability/skill.d.ts +6 -0
- package/dist/types/cli/command-help.d.ts +3 -0
- package/dist/types/commands/share.d.ts +25 -0
- package/dist/types/commit/agentic/index.d.ts +3 -1
- package/dist/types/commit/execute.d.ts +32 -0
- package/dist/types/commit/index.d.ts +2 -1
- package/dist/types/commit/pipeline.d.ts +7 -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 +9 -52
- package/dist/types/config/settings-schema.d.ts +1 -34
- 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 +136 -0
- package/dist/types/discovery/agent-plugins.d.ts +1 -0
- package/dist/types/discovery/contained-path.d.ts +33 -0
- package/dist/types/discovery/index.d.ts +1 -0
- 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/extensibility/custom-tools/types.d.ts +2 -2
- package/dist/types/extensibility/extensions/loader.d.ts +7 -0
- package/dist/types/extensibility/extensions/types.d.ts +11 -0
- package/dist/types/extensibility/shared-events.d.ts +3 -2
- package/dist/types/extensibility/skills.d.ts +5 -0
- 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 +46 -0
- package/dist/types/mcp/types.d.ts +15 -0
- 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/components/agent-hub.d.ts +2 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +2 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -2
- package/dist/types/modes/components/status-line/types.d.ts +2 -0
- package/dist/types/modes/components/tool-execution.d.ts +2 -0
- 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/modes/utils/transcript-render-helpers.d.ts +3 -2
- package/dist/types/registry/agent-registry.d.ts +1 -3
- package/dist/types/secrets/index.d.ts +26 -2
- 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-events.d.ts +2 -2
- package/dist/types/session/agent-session-types.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +2 -2
- package/dist/types/session/messages.d.ts +1 -0
- package/dist/types/session/prewalk.d.ts +3 -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/session-tools.d.ts +8 -0
- package/dist/types/session/turn-recovery.d.ts +9 -4
- 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/task/index.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 +18 -1
- package/dist/types/utils/changelog.d.ts +0 -5
- package/package.json +13 -13
- package/scripts/legacy-pi-virtual-module.ts +4 -1
- package/src/advisor/runtime.ts +12 -7
- package/src/capability/mcp.ts +11 -0
- package/src/capability/skill.ts +6 -0
- package/src/cli/command-help.ts +4 -0
- package/src/cli/gallery-cli.ts +1 -1
- package/src/cli-commands.ts +5 -0
- package/src/commands/commit.ts +16 -3
- package/src/commands/share.ts +71 -0
- package/src/commit/agentic/index.ts +39 -21
- package/src/commit/execute.ts +56 -0
- package/src/commit/index.ts +2 -1
- package/src/commit/pipeline.ts +20 -7
- package/src/config/custom-models.ts +188 -0
- package/src/config/model-config-values.ts +128 -0
- package/src/config/model-discovery.ts +1 -1
- package/src/config/model-patch.ts +252 -0
- package/src/config/model-provider-discovery.ts +132 -0
- package/src/config/model-registry.ts +84 -704
- package/src/config/settings-schema.ts +2 -33
- package/src/config/settings.ts +53 -3
- package/src/debug/raw-sse-buffer.ts +20 -0
- package/src/discovery/agent-plugin-format.ts +551 -0
- package/src/discovery/agent-plugins.ts +341 -0
- package/src/discovery/claude-plugins.ts +21 -5
- package/src/discovery/contained-path.ts +75 -0
- package/src/discovery/helpers.ts +23 -9
- package/src/discovery/index.ts +1 -0
- package/src/discovery/omp-plugins.ts +20 -7
- 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/exec/non-interactive-env.ts +1 -0
- package/src/export/share.ts +2 -1
- package/src/extensibility/custom-tools/types.ts +2 -2
- package/src/extensibility/extensions/loader.ts +78 -14
- package/src/extensibility/extensions/runner.ts +6 -0
- package/src/extensibility/extensions/types.ts +12 -0
- package/src/extensibility/plugins/marketplace/manager.ts +2 -1
- package/src/extensibility/shared-events.ts +3 -2
- package/src/extensibility/skills.ts +9 -0
- package/src/internal-urls/skill-protocol.ts +14 -0
- package/src/internal-urls/vault-protocol.ts +2 -2
- package/src/launch/client.ts +11 -1
- package/src/launch/protocol.ts +7 -2
- 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/main.ts +3 -2
- package/src/mcp/config.ts +3 -0
- package/src/mcp/manager.ts +12 -9
- package/src/mcp/transports/header-policy.ts +95 -0
- package/src/mcp/transports/http.ts +35 -52
- package/src/mcp/transports/sse.ts +20 -27
- package/src/mcp/types.ts +15 -0
- package/src/modes/acp/acp-agent.ts +15 -4
- package/src/modes/components/agent-dashboard.ts +2 -0
- package/src/modes/components/agent-hub-projection.ts +2 -2
- package/src/modes/components/agent-hub-renderer.ts +3 -7
- package/src/modes/components/agent-hub.ts +5 -0
- package/src/modes/components/agent-transcript-viewer.ts +3 -0
- package/src/modes/components/chat-transcript-builder.ts +3 -0
- package/src/modes/components/custom-editor.ts +0 -9
- package/src/modes/components/status-line/component.ts +1 -0
- package/src/modes/components/status-line/segments.ts +6 -4
- package/src/modes/components/status-line/types.ts +2 -0
- package/src/modes/components/tool-execution.ts +13 -16
- package/src/modes/components/tree-selector.ts +62 -3
- package/src/modes/controllers/command-controller.ts +8 -2
- package/src/modes/controllers/event-controller.ts +15 -15
- package/src/modes/controllers/input-controller.ts +20 -2
- package/src/modes/controllers/selector-controller.ts +1 -0
- 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/modes/utils/transcript-render-helpers.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/registry/agent-registry.ts +2 -2
- package/src/sdk.ts +6 -49
- package/src/secrets/index.ts +51 -6
- 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-events.ts +2 -2
- package/src/session/agent-session-types.ts +6 -0
- package/src/session/agent-session.ts +146 -21
- package/src/session/indexed-session-storage.ts +7 -2
- package/src/session/messages.ts +1 -0
- package/src/session/prewalk.ts +57 -17
- package/src/session/session-advisors.ts +32 -34
- package/src/session/session-context.ts +3 -5
- package/src/session/session-handoff.ts +39 -16
- package/src/session/session-manager.ts +67 -3
- package/src/session/session-provider-boundary.ts +3 -6
- package/src/session/session-tools.ts +37 -3
- package/src/session/todo-tracker.ts +11 -1
- package/src/session/turn-recovery.ts +115 -84
- 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 -2996
- package/src/slash-commands/builtin-session.ts +592 -0
- package/src/task/executor.ts +17 -14
- package/src/task/index.ts +21 -6
- package/src/tools/bash-skill-urls.ts +15 -0
- package/src/tools/bash.ts +10 -11
- package/src/tools/debug.ts +1 -1
- 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/jtd-to-json-schema.ts +123 -21
- 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 +82 -1824
- package/src/tools/shell-tokenize.ts +99 -1
- package/src/utils/changelog.ts +1 -1
- package/src/utils/title-generator.ts +3 -1
- package/src/web/search/providers/exa.ts +1 -1
- package/src/web/search/providers/zai.ts +12 -3
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
+
import type { Effort } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { colorLuma, logger, relativeLuminance } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
5
|
+
import { bgAnsi, colorToAnsi, fgAnsi, resolveToHex } from "./color";
|
|
6
|
+
import type { ColorMode, ThemeBg, ThemeColor } from "./schema";
|
|
7
|
+
import {
|
|
8
|
+
SPINNER_FRAMES,
|
|
9
|
+
type SpinnerType,
|
|
10
|
+
SYMBOL_PRESETS,
|
|
11
|
+
type SymbolKey,
|
|
12
|
+
type SymbolMap,
|
|
13
|
+
type SymbolPreset,
|
|
14
|
+
} from "./symbols";
|
|
15
|
+
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Theme Class
|
|
18
|
+
// ============================================================================
|
|
19
|
+
|
|
20
|
+
const langMap: Record<string, SymbolKey> = {
|
|
21
|
+
typescript: "lang.typescript",
|
|
22
|
+
ts: "lang.typescript",
|
|
23
|
+
tsx: "lang.typescript",
|
|
24
|
+
javascript: "lang.javascript",
|
|
25
|
+
js: "lang.javascript",
|
|
26
|
+
jsx: "lang.javascript",
|
|
27
|
+
mjs: "lang.javascript",
|
|
28
|
+
cjs: "lang.javascript",
|
|
29
|
+
python: "lang.python",
|
|
30
|
+
py: "lang.python",
|
|
31
|
+
rust: "lang.rust",
|
|
32
|
+
rs: "lang.rust",
|
|
33
|
+
go: "lang.go",
|
|
34
|
+
java: "lang.java",
|
|
35
|
+
c: "lang.c",
|
|
36
|
+
cpp: "lang.cpp",
|
|
37
|
+
"c++": "lang.cpp",
|
|
38
|
+
cc: "lang.cpp",
|
|
39
|
+
cxx: "lang.cpp",
|
|
40
|
+
csharp: "lang.csharp",
|
|
41
|
+
cs: "lang.csharp",
|
|
42
|
+
ruby: "lang.ruby",
|
|
43
|
+
rb: "lang.ruby",
|
|
44
|
+
julia: "lang.julia",
|
|
45
|
+
jl: "lang.julia",
|
|
46
|
+
php: "lang.php",
|
|
47
|
+
swift: "lang.swift",
|
|
48
|
+
kotlin: "lang.kotlin",
|
|
49
|
+
kt: "lang.kotlin",
|
|
50
|
+
bash: "lang.shell",
|
|
51
|
+
sh: "lang.shell",
|
|
52
|
+
zsh: "lang.shell",
|
|
53
|
+
fish: "lang.shell",
|
|
54
|
+
powershell: "lang.shell",
|
|
55
|
+
just: "lang.shell",
|
|
56
|
+
shell: "lang.shell",
|
|
57
|
+
html: "lang.html",
|
|
58
|
+
htm: "lang.html",
|
|
59
|
+
astro: "lang.html",
|
|
60
|
+
vue: "lang.html",
|
|
61
|
+
svelte: "lang.html",
|
|
62
|
+
css: "lang.css",
|
|
63
|
+
scss: "lang.css",
|
|
64
|
+
sass: "lang.css",
|
|
65
|
+
less: "lang.css",
|
|
66
|
+
json: "lang.json",
|
|
67
|
+
yaml: "lang.yaml",
|
|
68
|
+
yml: "lang.yaml",
|
|
69
|
+
markdown: "lang.markdown",
|
|
70
|
+
md: "lang.markdown",
|
|
71
|
+
sql: "lang.sql",
|
|
72
|
+
dockerfile: "lang.docker",
|
|
73
|
+
docker: "lang.docker",
|
|
74
|
+
lua: "lang.lua",
|
|
75
|
+
text: "lang.text",
|
|
76
|
+
txt: "lang.text",
|
|
77
|
+
plain: "lang.text",
|
|
78
|
+
log: "lang.log",
|
|
79
|
+
env: "lang.env",
|
|
80
|
+
dotenv: "lang.env",
|
|
81
|
+
toml: "lang.toml",
|
|
82
|
+
xml: "lang.xml",
|
|
83
|
+
ini: "lang.ini",
|
|
84
|
+
conf: "lang.conf",
|
|
85
|
+
cfg: "lang.conf",
|
|
86
|
+
config: "lang.conf",
|
|
87
|
+
properties: "lang.conf",
|
|
88
|
+
csv: "lang.csv",
|
|
89
|
+
tsv: "lang.tsv",
|
|
90
|
+
image: "lang.image",
|
|
91
|
+
img: "lang.image",
|
|
92
|
+
png: "lang.image",
|
|
93
|
+
jpg: "lang.image",
|
|
94
|
+
jpeg: "lang.image",
|
|
95
|
+
gif: "lang.image",
|
|
96
|
+
webp: "lang.image",
|
|
97
|
+
svg: "lang.image",
|
|
98
|
+
ico: "lang.image",
|
|
99
|
+
bmp: "lang.image",
|
|
100
|
+
tiff: "lang.image",
|
|
101
|
+
pdf: "lang.pdf",
|
|
102
|
+
zip: "lang.archive",
|
|
103
|
+
tar: "lang.archive",
|
|
104
|
+
gz: "lang.archive",
|
|
105
|
+
tgz: "lang.archive",
|
|
106
|
+
bz2: "lang.archive",
|
|
107
|
+
xz: "lang.archive",
|
|
108
|
+
"7z": "lang.archive",
|
|
109
|
+
exe: "lang.binary",
|
|
110
|
+
dll: "lang.binary",
|
|
111
|
+
so: "lang.binary",
|
|
112
|
+
dylib: "lang.binary",
|
|
113
|
+
wasm: "lang.binary",
|
|
114
|
+
bin: "lang.binary",
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Brand colors for language icons, keyed by the resolved `lang.*` SymbolKey.
|
|
119
|
+
* Used by {@link Theme.getLangIconStyled} so eval-kernel cell headers tint each
|
|
120
|
+
* language with its recognizable hue (JS yellow, Ruby red, Julia purple, Python
|
|
121
|
+
* blue) instead of a flat muted gray. Applied as truecolor/256 per the active
|
|
122
|
+
* color mode; languages without an entry fall back to the muted theme color.
|
|
123
|
+
*/
|
|
124
|
+
const LANG_BRAND_COLORS: Partial<Record<SymbolKey, string>> = {
|
|
125
|
+
"lang.javascript": "#f7df1e",
|
|
126
|
+
"lang.python": "#3776ab",
|
|
127
|
+
"lang.ruby": "#cc342d",
|
|
128
|
+
"lang.julia": "#9558b2",
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export class Theme {
|
|
132
|
+
#fgColors: Record<ThemeColor, string>;
|
|
133
|
+
#bgColors: Record<ThemeBg, string>;
|
|
134
|
+
/** Resolved hex strings for foreground colors — populated at construction. */
|
|
135
|
+
readonly #hexFgColors: Record<ThemeColor, string>;
|
|
136
|
+
/** Resolved hex strings for background colors — populated at construction. */
|
|
137
|
+
readonly #hexBgColors: Record<ThemeBg, string>;
|
|
138
|
+
#symbols: SymbolMap;
|
|
139
|
+
#spinnerFramesOverrides: Partial<Record<SpinnerType, string[]>>;
|
|
140
|
+
/**
|
|
141
|
+
* Perceptual luma (0..1) of the status-line background — used to classify the
|
|
142
|
+
* theme light/dark. Undefined when it can't be resolved. Classified against the
|
|
143
|
+
* status line (the surface session accents render on) rather than the chat bubble
|
|
144
|
+
* (`userMessageBg`), which some themes (e.g. `porcelain`) style dark on an
|
|
145
|
+
* otherwise-light theme.
|
|
146
|
+
*/
|
|
147
|
+
readonly statusLineLuminance: number | undefined;
|
|
148
|
+
/** WCAG relative luminance of the status-line background — basis for accent contrast. */
|
|
149
|
+
readonly #statusLineContrastLuminance: number | undefined;
|
|
150
|
+
constructor(
|
|
151
|
+
fgColors: Record<ThemeColor, string | number>,
|
|
152
|
+
bgColors: Record<ThemeBg, string | number>,
|
|
153
|
+
private readonly mode: ColorMode,
|
|
154
|
+
private readonly symbolPreset: SymbolPreset,
|
|
155
|
+
symbolOverrides: Partial<Record<SymbolKey, string>>,
|
|
156
|
+
spinnerFramesOverrides: Partial<Record<SpinnerType, string[]>> = {},
|
|
157
|
+
) {
|
|
158
|
+
this.statusLineLuminance = colorLuma(bgColors.statusLineBg);
|
|
159
|
+
this.#statusLineContrastLuminance = relativeLuminance(bgColors.statusLineBg);
|
|
160
|
+
const slIsLight = this.statusLineLuminance !== undefined && this.statusLineLuminance > 0.5;
|
|
161
|
+
|
|
162
|
+
this.#fgColors = {} as Record<ThemeColor, string>;
|
|
163
|
+
this.#hexFgColors = {} as Record<ThemeColor, string>;
|
|
164
|
+
for (const [key, value] of Object.entries(fgColors) as [ThemeColor, string | number][]) {
|
|
165
|
+
this.#fgColors[key] = fgAnsi(value, mode);
|
|
166
|
+
this.#hexFgColors[key] = resolveToHex(value, slIsLight);
|
|
167
|
+
}
|
|
168
|
+
this.#bgColors = {} as Record<ThemeBg, string>;
|
|
169
|
+
this.#hexBgColors = {} as Record<ThemeBg, string>;
|
|
170
|
+
for (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {
|
|
171
|
+
this.#bgColors[key] = bgAnsi(value, mode);
|
|
172
|
+
this.#hexBgColors[key] = resolveToHex(value, slIsLight);
|
|
173
|
+
}
|
|
174
|
+
// Build symbol map from preset + overrides
|
|
175
|
+
const baseSymbols = SYMBOL_PRESETS[symbolPreset];
|
|
176
|
+
this.#symbols = { ...baseSymbols };
|
|
177
|
+
for (const [key, value] of Object.entries(symbolOverrides)) {
|
|
178
|
+
if (key in this.#symbols) {
|
|
179
|
+
this.#symbols[key as SymbolKey] = value;
|
|
180
|
+
} else {
|
|
181
|
+
logger.debug("Invalid symbol key in override", { key, availableKeys: Object.keys(this.#symbols) });
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
this.#spinnerFramesOverrides = spinnerFramesOverrides;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** True when the active theme has a light status-line background. */
|
|
188
|
+
get isLight(): boolean {
|
|
189
|
+
return this.statusLineLuminance !== undefined && this.statusLineLuminance > 0.5;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Surface luminance to size session accents against on light themes; undefined on
|
|
194
|
+
* dark themes so accents stay vivid. Pass straight to `getSessionAccentHex`.
|
|
195
|
+
*/
|
|
196
|
+
get accentSurfaceLuminance(): number | undefined {
|
|
197
|
+
return this.isLight ? this.#statusLineContrastLuminance : undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Get the resolved CSS hex string for a foreground theme color.
|
|
202
|
+
*/
|
|
203
|
+
getColorHex(color: ThemeColor): string {
|
|
204
|
+
const hex = this.#hexFgColors[color];
|
|
205
|
+
if (hex === undefined) throw new Error(`Unknown theme color: ${color}`);
|
|
206
|
+
return hex || (this.isLight ? "#000000" : "#e5e5e7");
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get all foreground and background theme colors as CSS hex strings.
|
|
211
|
+
* Skips colors resolved to the default terminal color (unstyled).
|
|
212
|
+
*/
|
|
213
|
+
getAllThemeColorHexes(): string[] {
|
|
214
|
+
const hexes: string[] = [];
|
|
215
|
+
for (const hex of Object.values(this.#hexFgColors)) {
|
|
216
|
+
if (hex) hexes.push(hex);
|
|
217
|
+
}
|
|
218
|
+
for (const hex of Object.values(this.#hexBgColors)) {
|
|
219
|
+
if (hex) hexes.push(hex);
|
|
220
|
+
}
|
|
221
|
+
return hexes;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Get the most visually dominant theme colors as CSS hex strings — accent,
|
|
226
|
+
* border, success, error, warning, heading, link, diff markers, etc.
|
|
227
|
+
* These are the colors the session accent could visually clash with.
|
|
228
|
+
* Skips colors resolved to the default terminal color (unstyled).
|
|
229
|
+
*/
|
|
230
|
+
getMajorThemeColorHexes(): string[] {
|
|
231
|
+
const majors: ThemeColor[] = [
|
|
232
|
+
"accent",
|
|
233
|
+
"border",
|
|
234
|
+
"borderAccent",
|
|
235
|
+
"borderMuted",
|
|
236
|
+
"success",
|
|
237
|
+
"error",
|
|
238
|
+
"warning",
|
|
239
|
+
"mdHeading",
|
|
240
|
+
"mdLink",
|
|
241
|
+
"mdCode",
|
|
242
|
+
"mdCodeBlock",
|
|
243
|
+
"mdQuoteBorder",
|
|
244
|
+
"mdListBullet",
|
|
245
|
+
"toolDiffAdded",
|
|
246
|
+
"toolDiffRemoved",
|
|
247
|
+
"customMessageLabel",
|
|
248
|
+
"thinkingText",
|
|
249
|
+
];
|
|
250
|
+
const hexes: string[] = [];
|
|
251
|
+
for (const key of majors) {
|
|
252
|
+
const hex = this.#hexFgColors[key];
|
|
253
|
+
if (hex) hexes.push(hex);
|
|
254
|
+
}
|
|
255
|
+
return hexes;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Get the resolved CSS hex string for the theme's accent color.
|
|
259
|
+
*/
|
|
260
|
+
getAccentColorHex(): string {
|
|
261
|
+
return this.getColorHex("accent");
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
fg(color: ThemeColor, text: string): string {
|
|
265
|
+
const ansi = this.#fgColors[color];
|
|
266
|
+
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
|
267
|
+
return `${ansi}${text}\x1b[39m`; // Reset only foreground color
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
bg(color: ThemeBg, text: string): string {
|
|
271
|
+
const ansi = this.#bgColors[color];
|
|
272
|
+
if (!ansi) throw new Error(`Unknown theme background color: ${color}`);
|
|
273
|
+
return `${ansi}${text}\x1b[49m`; // Reset only background color
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
bold(text: string): string {
|
|
277
|
+
return chalk.bold(text);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
italic(text: string): string {
|
|
281
|
+
return chalk.italic(text);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
underline(text: string): string {
|
|
285
|
+
return chalk.underline(text);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
strikethrough(text: string): string {
|
|
289
|
+
return chalk.strikethrough(text);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
inverse(text: string): string {
|
|
293
|
+
return chalk.inverse(text);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
getFgAnsi(color: ThemeColor): string {
|
|
297
|
+
const ansi = this.#fgColors[color];
|
|
298
|
+
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
|
299
|
+
return ansi;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
getBgAnsi(color: ThemeBg): string {
|
|
303
|
+
const ansi = this.#bgColors[color];
|
|
304
|
+
if (!ansi) throw new Error(`Unknown theme background color: ${color}`);
|
|
305
|
+
return ansi;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Foreground ANSI for text drawn **on top of** `fillColor` used as a solid
|
|
310
|
+
* background (e.g. a powerline chip). Picks near-black or near-white by the
|
|
311
|
+
* fill's perceived luminance (Rec. 601 luma) so the label stays legible on
|
|
312
|
+
* both bright and dark fills, across light and dark themes.
|
|
313
|
+
*
|
|
314
|
+
* Reads the RGB out of the already-resolved truecolor escape; when the fill
|
|
315
|
+
* is encoded as a 256-palette index (limited terminals) the RGB is
|
|
316
|
+
* unavailable, so it falls back to the theme `text` color.
|
|
317
|
+
*/
|
|
318
|
+
getContrastFgAnsi(fillColor: ThemeColor): string {
|
|
319
|
+
const ansi = this.#fgColors[fillColor];
|
|
320
|
+
const match = ansi ? /38;2;(\d+);(\d+);(\d+)/.exec(ansi) : null;
|
|
321
|
+
if (!match) return this.#fgColors.text;
|
|
322
|
+
const luma = 0.299 * Number(match[1]) + 0.587 * Number(match[2]) + 0.114 * Number(match[3]);
|
|
323
|
+
return luma > 140 ? "\x1b[38;2;0;0;0m" : "\x1b[38;2;255;255;255m";
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
getColorMode(): ColorMode {
|
|
327
|
+
return this.mode;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
getThinkingBorderColor(level: ThinkingLevel | Effort): (str: string) => string {
|
|
331
|
+
// Map thinking levels to dedicated theme colors
|
|
332
|
+
switch (level) {
|
|
333
|
+
case "off":
|
|
334
|
+
return (str: string) => this.fg("thinkingOff", str);
|
|
335
|
+
case "minimal":
|
|
336
|
+
return (str: string) => this.fg("thinkingMinimal", str);
|
|
337
|
+
case "low":
|
|
338
|
+
return (str: string) => this.fg("thinkingLow", str);
|
|
339
|
+
case "medium":
|
|
340
|
+
return (str: string) => this.fg("thinkingMedium", str);
|
|
341
|
+
case "high":
|
|
342
|
+
return (str: string) => this.fg("thinkingHigh", str);
|
|
343
|
+
case "xhigh":
|
|
344
|
+
return (str: string) => this.fg("thinkingXhigh", str);
|
|
345
|
+
case "max":
|
|
346
|
+
// thinkingMax is optional; themes without it resolve to the xhigh color.
|
|
347
|
+
return (str: string) => this.fg(this.#fgColors.thinkingMax ? "thinkingMax" : "thinkingXhigh", str);
|
|
348
|
+
default:
|
|
349
|
+
return (str: string) => this.fg("thinkingOff", str);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
getBashModeBorderColor(): (str: string) => string {
|
|
354
|
+
return (str: string) => this.fg("bashMode", str);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
getPythonModeBorderColor(): (str: string) => string {
|
|
358
|
+
return (str: string) => this.fg("pythonMode", str);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// ============================================================================
|
|
362
|
+
// Symbol Methods
|
|
363
|
+
// ============================================================================
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Get a symbol by key.
|
|
367
|
+
*/
|
|
368
|
+
symbol(key: SymbolKey): string {
|
|
369
|
+
return this.#symbols[key];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get a symbol styled with a color.
|
|
374
|
+
*/
|
|
375
|
+
styledSymbol(key: SymbolKey, color: ThemeColor): string {
|
|
376
|
+
return this.fg(color, this.#symbols[key]);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Get the current symbol preset.
|
|
381
|
+
*/
|
|
382
|
+
getSymbolPreset(): SymbolPreset {
|
|
383
|
+
return this.symbolPreset;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// ============================================================================
|
|
387
|
+
// Symbol Category Accessors
|
|
388
|
+
// ============================================================================
|
|
389
|
+
|
|
390
|
+
get status() {
|
|
391
|
+
return {
|
|
392
|
+
success: this.#symbols["status.success"],
|
|
393
|
+
error: this.#symbols["status.error"],
|
|
394
|
+
warning: this.#symbols["status.warning"],
|
|
395
|
+
info: this.#symbols["status.info"],
|
|
396
|
+
pending: this.#symbols["status.pending"],
|
|
397
|
+
disabled: this.#symbols["status.disabled"],
|
|
398
|
+
enabled: this.#symbols["status.enabled"],
|
|
399
|
+
running: this.#symbols["status.running"],
|
|
400
|
+
shadowed: this.#symbols["status.shadowed"],
|
|
401
|
+
aborted: this.#symbols["status.aborted"],
|
|
402
|
+
done: this.#symbols["status.done"],
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
get nav() {
|
|
407
|
+
return {
|
|
408
|
+
cursor: this.#symbols["nav.cursor"],
|
|
409
|
+
selected: this.#symbols["nav.selected"],
|
|
410
|
+
expand: this.#symbols["nav.expand"],
|
|
411
|
+
collapse: this.#symbols["nav.collapse"],
|
|
412
|
+
back: this.#symbols["nav.back"],
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
get tree() {
|
|
417
|
+
return {
|
|
418
|
+
branch: this.#symbols["tree.branch"],
|
|
419
|
+
last: this.#symbols["tree.last"],
|
|
420
|
+
vertical: this.#symbols["tree.vertical"],
|
|
421
|
+
horizontal: this.#symbols["tree.horizontal"],
|
|
422
|
+
hook: this.#symbols["tree.hook"],
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
get boxRound() {
|
|
427
|
+
return {
|
|
428
|
+
topLeft: this.#symbols["boxRound.topLeft"],
|
|
429
|
+
topRight: this.#symbols["boxRound.topRight"],
|
|
430
|
+
bottomLeft: this.#symbols["boxRound.bottomLeft"],
|
|
431
|
+
bottomRight: this.#symbols["boxRound.bottomRight"],
|
|
432
|
+
horizontal: this.#symbols["boxRound.horizontal"],
|
|
433
|
+
vertical: this.#symbols["boxRound.vertical"],
|
|
434
|
+
// Junctions have no rounded Unicode variant, so a rounded box reuses the
|
|
435
|
+
// sharp tee/cross glyphs. Sourcing them from the boxSharp.* tokens keeps a
|
|
436
|
+
// theme's `boxSharp.tee*` overrides effective for rounded-box dividers.
|
|
437
|
+
cross: this.#symbols["boxSharp.cross"],
|
|
438
|
+
teeDown: this.#symbols["boxSharp.teeDown"],
|
|
439
|
+
teeUp: this.#symbols["boxSharp.teeUp"],
|
|
440
|
+
teeRight: this.#symbols["boxSharp.teeRight"],
|
|
441
|
+
teeLeft: this.#symbols["boxSharp.teeLeft"],
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
get boxSharp() {
|
|
446
|
+
return {
|
|
447
|
+
topLeft: this.#symbols["boxSharp.topLeft"],
|
|
448
|
+
topRight: this.#symbols["boxSharp.topRight"],
|
|
449
|
+
bottomLeft: this.#symbols["boxSharp.bottomLeft"],
|
|
450
|
+
bottomRight: this.#symbols["boxSharp.bottomRight"],
|
|
451
|
+
horizontal: this.#symbols["boxSharp.horizontal"],
|
|
452
|
+
vertical: this.#symbols["boxSharp.vertical"],
|
|
453
|
+
cross: this.#symbols["boxSharp.cross"],
|
|
454
|
+
teeDown: this.#symbols["boxSharp.teeDown"],
|
|
455
|
+
teeUp: this.#symbols["boxSharp.teeUp"],
|
|
456
|
+
teeRight: this.#symbols["boxSharp.teeRight"],
|
|
457
|
+
teeLeft: this.#symbols["boxSharp.teeLeft"],
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
get sep() {
|
|
462
|
+
return {
|
|
463
|
+
powerline: this.#symbols["sep.powerline"],
|
|
464
|
+
powerlineThin: this.#symbols["sep.powerlineThin"],
|
|
465
|
+
powerlineLeft: this.#symbols["sep.powerlineLeft"],
|
|
466
|
+
powerlineRight: this.#symbols["sep.powerlineRight"],
|
|
467
|
+
powerlineThinLeft: this.#symbols["sep.powerlineThinLeft"],
|
|
468
|
+
powerlineThinRight: this.#symbols["sep.powerlineThinRight"],
|
|
469
|
+
block: this.#symbols["sep.block"],
|
|
470
|
+
space: this.#symbols["sep.space"],
|
|
471
|
+
asciiLeft: this.#symbols["sep.asciiLeft"],
|
|
472
|
+
asciiRight: this.#symbols["sep.asciiRight"],
|
|
473
|
+
dot: this.#symbols["sep.dot"],
|
|
474
|
+
slash: this.#symbols["sep.slash"],
|
|
475
|
+
pipe: this.#symbols["sep.pipe"],
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
get icon() {
|
|
480
|
+
return {
|
|
481
|
+
model: this.#symbols["icon.model"],
|
|
482
|
+
plan: this.#symbols["icon.plan"],
|
|
483
|
+
prewalk: this.#symbols["icon.prewalk"],
|
|
484
|
+
goal: this.#symbols["icon.goal"],
|
|
485
|
+
pause: this.#symbols["icon.pause"],
|
|
486
|
+
loop: this.#symbols["icon.loop"],
|
|
487
|
+
folder: this.#symbols["icon.folder"],
|
|
488
|
+
worktree: this.#symbols["icon.worktree"],
|
|
489
|
+
scratchFolder: this.#symbols["icon.scratchFolder"],
|
|
490
|
+
file: this.#symbols["icon.file"],
|
|
491
|
+
git: this.#symbols["icon.git"],
|
|
492
|
+
branch: this.#symbols["icon.branch"],
|
|
493
|
+
pr: this.#symbols["icon.pr"],
|
|
494
|
+
tokens: this.#symbols["icon.tokens"],
|
|
495
|
+
context: this.#symbols["icon.context"],
|
|
496
|
+
cost: this.#symbols["icon.cost"],
|
|
497
|
+
time: this.#symbols["icon.time"],
|
|
498
|
+
pi: this.#symbols["icon.pi"],
|
|
499
|
+
ghost: this.#symbols["icon.ghost"],
|
|
500
|
+
agents: this.#symbols["icon.agents"],
|
|
501
|
+
job: this.#symbols["icon.job"],
|
|
502
|
+
cache: this.#symbols["icon.cache"],
|
|
503
|
+
cacheMiss: this.#symbols["icon.cacheMiss"],
|
|
504
|
+
input: this.#symbols["icon.input"],
|
|
505
|
+
output: this.#symbols["icon.output"],
|
|
506
|
+
throughput: this.#symbols["icon.throughput"],
|
|
507
|
+
host: this.#symbols["icon.host"],
|
|
508
|
+
session: this.#symbols["icon.session"],
|
|
509
|
+
package: this.#symbols["icon.package"],
|
|
510
|
+
warning: this.#symbols["icon.warning"],
|
|
511
|
+
rewind: this.#symbols["icon.rewind"],
|
|
512
|
+
auto: this.#symbols["icon.auto"],
|
|
513
|
+
fast: this.#symbols["icon.fast"],
|
|
514
|
+
extensionSkill: this.#symbols["icon.extensionSkill"],
|
|
515
|
+
extensionTool: this.#symbols["icon.extensionTool"],
|
|
516
|
+
extensionSlashCommand: this.#symbols["icon.extensionSlashCommand"],
|
|
517
|
+
extensionMcp: this.#symbols["icon.extensionMcp"],
|
|
518
|
+
extensionRule: this.#symbols["icon.extensionRule"],
|
|
519
|
+
extensionHook: this.#symbols["icon.extensionHook"],
|
|
520
|
+
extensionPrompt: this.#symbols["icon.extensionPrompt"],
|
|
521
|
+
extensionContextFile: this.#symbols["icon.extensionContextFile"],
|
|
522
|
+
extensionInstruction: this.#symbols["icon.extensionInstruction"],
|
|
523
|
+
mic: this.#symbols["icon.mic"],
|
|
524
|
+
camera: this.#symbols["icon.camera"],
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
get thinking() {
|
|
529
|
+
return {
|
|
530
|
+
minimal: this.#symbols["thinking.minimal"],
|
|
531
|
+
low: this.#symbols["thinking.low"],
|
|
532
|
+
medium: this.#symbols["thinking.medium"],
|
|
533
|
+
high: this.#symbols["thinking.high"],
|
|
534
|
+
xhigh: this.#symbols["thinking.xhigh"],
|
|
535
|
+
max: this.#symbols["thinking.max"],
|
|
536
|
+
autoPending: this.#symbols["thinking.autoPending"],
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
get checkbox() {
|
|
541
|
+
return {
|
|
542
|
+
checked: this.#symbols["checkbox.checked"],
|
|
543
|
+
unchecked: this.#symbols["checkbox.unchecked"],
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
get radio() {
|
|
548
|
+
return {
|
|
549
|
+
selected: this.#symbols["radio.selected"],
|
|
550
|
+
unselected: this.#symbols["radio.unselected"],
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
get format() {
|
|
555
|
+
return {
|
|
556
|
+
bullet: this.#symbols["format.bullet"],
|
|
557
|
+
dash: this.#symbols["format.dash"],
|
|
558
|
+
bracketLeft: this.#symbols["format.bracketLeft"],
|
|
559
|
+
bracketRight: this.#symbols["format.bracketRight"],
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
get md() {
|
|
564
|
+
return {
|
|
565
|
+
quoteBorder: this.#symbols["md.quoteBorder"],
|
|
566
|
+
hrChar: this.#symbols["md.hrChar"],
|
|
567
|
+
bullet: this.#symbols["md.bullet"],
|
|
568
|
+
colorSwatch: this.#symbols["md.colorSwatch"],
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Default spinner frames (status spinner).
|
|
574
|
+
*/
|
|
575
|
+
get spinnerFrames(): string[] {
|
|
576
|
+
return this.getSpinnerFrames();
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Get spinner frames by type.
|
|
581
|
+
*/
|
|
582
|
+
getSpinnerFrames(type: SpinnerType = "status"): string[] {
|
|
583
|
+
return this.#spinnerFramesOverrides[type] ?? SPINNER_FRAMES[this.symbolPreset][type];
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Get language icon for a language name.
|
|
588
|
+
* Maps common language names to their corresponding symbol keys.
|
|
589
|
+
*/
|
|
590
|
+
getLangIcon(lang: string | undefined): string {
|
|
591
|
+
if (!lang) return this.#symbols["lang.default"];
|
|
592
|
+
const normalized = lang.toLowerCase();
|
|
593
|
+
const key = langMap[normalized];
|
|
594
|
+
return key ? this.#symbols[key] : this.#symbols["lang.default"];
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Language icon tinted with the language's brand color (see
|
|
599
|
+
* {@link LANG_BRAND_COLORS}). Falls back to the muted theme color for
|
|
600
|
+
* languages without a brand entry, and returns the bare (possibly empty)
|
|
601
|
+
* icon when the active symbol preset has none.
|
|
602
|
+
*/
|
|
603
|
+
getLangIconStyled(lang: string | undefined): string {
|
|
604
|
+
const icon = this.getLangIcon(lang);
|
|
605
|
+
if (!icon) return icon;
|
|
606
|
+
const key = lang ? langMap[lang.toLowerCase()] : undefined;
|
|
607
|
+
const hex = key ? LANG_BRAND_COLORS[key] : undefined;
|
|
608
|
+
if (!hex) return this.fg("muted", icon);
|
|
609
|
+
return `${colorToAnsi(hex, this.mode)}${icon}\x1b[39m`;
|
|
610
|
+
}
|
|
611
|
+
}
|