@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
package/src/modes/theme/theme.ts
CHANGED
|
@@ -1,2132 +1,34 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
2
|
import * as path from "node:path";
|
|
3
|
-
import {
|
|
4
|
-
import type {
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
supportsLanguage as nativeSupportsLanguage,
|
|
12
|
-
} from "@oh-my-pi/pi-natives";
|
|
13
|
-
import type {
|
|
14
|
-
EditorTheme,
|
|
15
|
-
MarkdownTheme,
|
|
16
|
-
SelectListTheme,
|
|
17
|
-
SettingsListTheme,
|
|
18
|
-
SymbolTheme,
|
|
19
|
-
Terminal,
|
|
20
|
-
TerminalAppearance,
|
|
21
|
-
} from "@oh-my-pi/pi-tui";
|
|
22
|
-
import { adjustHsv, colorLuma, getCustomThemesDir, isEnoent, logger, relativeLuminance } from "@oh-my-pi/pi-utils";
|
|
23
|
-
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
24
|
-
import { LRUCache } from "@oh-my-pi/pi-utils/lru";
|
|
25
|
-
// Embed theme JSON files at build time
|
|
26
|
-
import darkThemeJson from "./dark.json" with { type: "json" };
|
|
27
|
-
import { defaultThemes } from "./defaults";
|
|
28
|
-
import lightThemeJson from "./light.json" with { type: "json" };
|
|
29
|
-
import { resolveMermaidAscii } from "./mermaid-cache";
|
|
3
|
+
import { detectMacOSAppearance, MacAppearanceObserver } from "@oh-my-pi/pi-natives";
|
|
4
|
+
import type { Terminal, TerminalAppearance } from "@oh-my-pi/pi-tui";
|
|
5
|
+
import { colorLuma, getCustomThemesDir, logger } from "@oh-my-pi/pi-utils";
|
|
6
|
+
import { ansi256ToHex, resolveThemeColors, resolveVarRefs } from "./color";
|
|
7
|
+
import { type CreateThemeOptions, getBuiltinThemes, loadTheme, loadThemeJson } from "./loader";
|
|
8
|
+
import type { ThemeColor, ThemeJson } from "./schema";
|
|
9
|
+
import type { SymbolPreset } from "./symbols";
|
|
10
|
+
import type { Theme } from "./theme-class";
|
|
30
11
|
|
|
31
12
|
export { getLanguageFromPath, isMarkdownPath } from "../../utils/lang-from-path";
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
export
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
| "status.running"
|
|
52
|
-
| "status.shadowed"
|
|
53
|
-
| "status.aborted"
|
|
54
|
-
| "status.done"
|
|
55
|
-
// Navigation
|
|
56
|
-
| "nav.cursor"
|
|
57
|
-
| "nav.selected"
|
|
58
|
-
| "nav.expand"
|
|
59
|
-
| "nav.collapse"
|
|
60
|
-
| "nav.back"
|
|
61
|
-
// Tree Connectors
|
|
62
|
-
| "tree.branch"
|
|
63
|
-
| "tree.last"
|
|
64
|
-
| "tree.vertical"
|
|
65
|
-
| "tree.horizontal"
|
|
66
|
-
| "tree.hook"
|
|
67
|
-
// Box Drawing - Rounded
|
|
68
|
-
| "boxRound.topLeft"
|
|
69
|
-
| "boxRound.topRight"
|
|
70
|
-
| "boxRound.bottomLeft"
|
|
71
|
-
| "boxRound.bottomRight"
|
|
72
|
-
| "boxRound.horizontal"
|
|
73
|
-
| "boxRound.vertical"
|
|
74
|
-
// Box Drawing - Sharp
|
|
75
|
-
| "boxSharp.topLeft"
|
|
76
|
-
| "boxSharp.topRight"
|
|
77
|
-
| "boxSharp.bottomLeft"
|
|
78
|
-
| "boxSharp.bottomRight"
|
|
79
|
-
| "boxSharp.horizontal"
|
|
80
|
-
| "boxSharp.vertical"
|
|
81
|
-
| "boxSharp.cross"
|
|
82
|
-
| "boxSharp.teeDown"
|
|
83
|
-
| "boxSharp.teeUp"
|
|
84
|
-
| "boxSharp.teeRight"
|
|
85
|
-
| "boxSharp.teeLeft"
|
|
86
|
-
// Separators
|
|
87
|
-
| "sep.powerline"
|
|
88
|
-
| "sep.powerlineThin"
|
|
89
|
-
| "sep.powerlineLeft"
|
|
90
|
-
| "sep.powerlineRight"
|
|
91
|
-
| "sep.powerlineThinLeft"
|
|
92
|
-
| "sep.powerlineThinRight"
|
|
93
|
-
| "sep.block"
|
|
94
|
-
| "sep.space"
|
|
95
|
-
| "sep.asciiLeft"
|
|
96
|
-
| "sep.asciiRight"
|
|
97
|
-
| "sep.dot"
|
|
98
|
-
| "sep.slash"
|
|
99
|
-
| "sep.pipe"
|
|
100
|
-
// Icons
|
|
101
|
-
| "icon.model"
|
|
102
|
-
| "icon.plan"
|
|
103
|
-
| "icon.prewalk"
|
|
104
|
-
| "icon.goal"
|
|
105
|
-
| "icon.pause"
|
|
106
|
-
| "icon.loop"
|
|
107
|
-
| "icon.folder"
|
|
108
|
-
| "icon.worktree"
|
|
109
|
-
| "icon.search"
|
|
110
|
-
| "icon.scratchFolder"
|
|
111
|
-
| "icon.file"
|
|
112
|
-
| "icon.git"
|
|
113
|
-
| "icon.branch"
|
|
114
|
-
| "icon.pr"
|
|
115
|
-
| "icon.tokens"
|
|
116
|
-
| "icon.context"
|
|
117
|
-
| "icon.cost"
|
|
118
|
-
| "icon.time"
|
|
119
|
-
| "icon.pi"
|
|
120
|
-
| "icon.ghost"
|
|
121
|
-
| "icon.agents"
|
|
122
|
-
| "icon.job"
|
|
123
|
-
| "icon.cache"
|
|
124
|
-
| "icon.cacheMiss"
|
|
125
|
-
| "icon.input"
|
|
126
|
-
| "icon.output"
|
|
127
|
-
| "icon.throughput"
|
|
128
|
-
| "icon.host"
|
|
129
|
-
| "icon.session"
|
|
130
|
-
| "icon.package"
|
|
131
|
-
| "icon.warning"
|
|
132
|
-
| "icon.rewind"
|
|
133
|
-
| "icon.auto"
|
|
134
|
-
| "icon.fast"
|
|
135
|
-
| "icon.extensionSkill"
|
|
136
|
-
| "icon.extensionTool"
|
|
137
|
-
| "icon.extensionSlashCommand"
|
|
138
|
-
| "icon.extensionMcp"
|
|
139
|
-
| "icon.extensionRule"
|
|
140
|
-
| "icon.extensionHook"
|
|
141
|
-
| "icon.extensionPrompt"
|
|
142
|
-
| "icon.extensionContextFile"
|
|
143
|
-
| "icon.extensionInstruction"
|
|
144
|
-
// STT
|
|
145
|
-
| "icon.mic"
|
|
146
|
-
// Compaction divider
|
|
147
|
-
| "icon.camera"
|
|
148
|
-
// Thinking Levels
|
|
149
|
-
| "thinking.minimal"
|
|
150
|
-
| "thinking.low"
|
|
151
|
-
| "thinking.medium"
|
|
152
|
-
| "thinking.high"
|
|
153
|
-
| "thinking.xhigh"
|
|
154
|
-
| "thinking.max"
|
|
155
|
-
| "thinking.autoPending"
|
|
156
|
-
// Checkboxes
|
|
157
|
-
| "checkbox.checked"
|
|
158
|
-
| "checkbox.unchecked"
|
|
159
|
-
// Radio (single-choice)
|
|
160
|
-
| "radio.selected"
|
|
161
|
-
| "radio.unselected"
|
|
162
|
-
// Text Formatting
|
|
163
|
-
| "format.bullet"
|
|
164
|
-
| "format.dash"
|
|
165
|
-
| "format.bracketLeft"
|
|
166
|
-
| "format.bracketRight"
|
|
167
|
-
// Markdown-specific
|
|
168
|
-
| "md.quoteBorder"
|
|
169
|
-
| "md.hrChar"
|
|
170
|
-
| "md.bullet"
|
|
171
|
-
| "md.colorSwatch"
|
|
172
|
-
// Advisor note rail
|
|
173
|
-
| "advisor.rail"
|
|
174
|
-
// Language/file type icons
|
|
175
|
-
| "lang.default"
|
|
176
|
-
| "lang.typescript"
|
|
177
|
-
| "lang.javascript"
|
|
178
|
-
| "lang.python"
|
|
179
|
-
| "lang.rust"
|
|
180
|
-
| "lang.go"
|
|
181
|
-
| "lang.java"
|
|
182
|
-
| "lang.c"
|
|
183
|
-
| "lang.cpp"
|
|
184
|
-
| "lang.csharp"
|
|
185
|
-
| "lang.ruby"
|
|
186
|
-
| "lang.julia"
|
|
187
|
-
| "lang.php"
|
|
188
|
-
| "lang.swift"
|
|
189
|
-
| "lang.kotlin"
|
|
190
|
-
| "lang.shell"
|
|
191
|
-
| "lang.html"
|
|
192
|
-
| "lang.css"
|
|
193
|
-
| "lang.json"
|
|
194
|
-
| "lang.yaml"
|
|
195
|
-
| "lang.markdown"
|
|
196
|
-
| "lang.sql"
|
|
197
|
-
| "lang.docker"
|
|
198
|
-
| "lang.lua"
|
|
199
|
-
| "lang.text"
|
|
200
|
-
| "lang.env"
|
|
201
|
-
| "lang.toml"
|
|
202
|
-
| "lang.xml"
|
|
203
|
-
| "lang.ini"
|
|
204
|
-
| "lang.conf"
|
|
205
|
-
| "lang.log"
|
|
206
|
-
| "lang.csv"
|
|
207
|
-
| "lang.tsv"
|
|
208
|
-
| "lang.image"
|
|
209
|
-
| "lang.pdf"
|
|
210
|
-
| "lang.archive"
|
|
211
|
-
| "lang.binary"
|
|
212
|
-
// Settings tab icons
|
|
213
|
-
| "tab.appearance"
|
|
214
|
-
| "tab.model"
|
|
215
|
-
| "tab.interaction"
|
|
216
|
-
| "tab.context"
|
|
217
|
-
| "tab.files"
|
|
218
|
-
| "tab.shell"
|
|
219
|
-
| "tab.tools"
|
|
220
|
-
| "tab.memory"
|
|
221
|
-
| "tab.tasks"
|
|
222
|
-
| "tab.providers"
|
|
223
|
-
// Tool identity icons
|
|
224
|
-
| "tool.write"
|
|
225
|
-
| "tool.edit"
|
|
226
|
-
| "tool.bash"
|
|
227
|
-
| "tool.ssh"
|
|
228
|
-
| "tool.lsp"
|
|
229
|
-
| "tool.gh"
|
|
230
|
-
| "tool.webSearch"
|
|
231
|
-
| "tool.exa"
|
|
232
|
-
| "tool.browser"
|
|
233
|
-
| "tool.eval"
|
|
234
|
-
| "tool.debug"
|
|
235
|
-
| "tool.mcp"
|
|
236
|
-
| "tool.job"
|
|
237
|
-
| "tool.launch"
|
|
238
|
-
| "tool.task"
|
|
239
|
-
| "tool.todo"
|
|
240
|
-
| "tool.memory"
|
|
241
|
-
| "tool.ask"
|
|
242
|
-
| "tool.resolve"
|
|
243
|
-
| "tool.review"
|
|
244
|
-
| "tool.inspectImage"
|
|
245
|
-
| "tool.goal"
|
|
246
|
-
| "tool.irc"
|
|
247
|
-
| "tool.delete"
|
|
248
|
-
| "tool.move";
|
|
249
|
-
|
|
250
|
-
type SymbolMap = Record<SymbolKey, string>;
|
|
251
|
-
|
|
252
|
-
const UNICODE_SYMBOLS: SymbolMap = {
|
|
253
|
-
// Status
|
|
254
|
-
"status.success": "✔",
|
|
255
|
-
"status.error": "✘",
|
|
256
|
-
"status.warning": "⚠",
|
|
257
|
-
"status.info": "ⓘ",
|
|
258
|
-
"status.pending": "⏳",
|
|
259
|
-
"status.disabled": "⦸",
|
|
260
|
-
"status.enabled": "●",
|
|
261
|
-
"status.running": "⟳",
|
|
262
|
-
"status.shadowed": "○",
|
|
263
|
-
"status.aborted": "⏹",
|
|
264
|
-
"status.done": "•",
|
|
265
|
-
// Navigation
|
|
266
|
-
"nav.cursor": "❯",
|
|
267
|
-
"nav.selected": "➤",
|
|
268
|
-
"nav.expand": "▸",
|
|
269
|
-
"nav.collapse": "▾",
|
|
270
|
-
"nav.back": "⟵",
|
|
271
|
-
// Tree
|
|
272
|
-
"tree.branch": "├─",
|
|
273
|
-
"tree.last": "└─",
|
|
274
|
-
"tree.vertical": "│",
|
|
275
|
-
"tree.horizontal": "─",
|
|
276
|
-
"tree.hook": "└",
|
|
277
|
-
// Box (rounded)
|
|
278
|
-
"boxRound.topLeft": "╭",
|
|
279
|
-
"boxRound.topRight": "╮",
|
|
280
|
-
"boxRound.bottomLeft": "╰",
|
|
281
|
-
"boxRound.bottomRight": "╯",
|
|
282
|
-
"boxRound.horizontal": "─",
|
|
283
|
-
"boxRound.vertical": "│",
|
|
284
|
-
// Box (sharp)
|
|
285
|
-
"boxSharp.topLeft": "┌",
|
|
286
|
-
"boxSharp.topRight": "┐",
|
|
287
|
-
"boxSharp.bottomLeft": "└",
|
|
288
|
-
"boxSharp.bottomRight": "┘",
|
|
289
|
-
"boxSharp.horizontal": "─",
|
|
290
|
-
"boxSharp.vertical": "│",
|
|
291
|
-
"boxSharp.cross": "┼",
|
|
292
|
-
"boxSharp.teeDown": "┬",
|
|
293
|
-
"boxSharp.teeUp": "┴",
|
|
294
|
-
"boxSharp.teeRight": "├",
|
|
295
|
-
"boxSharp.teeLeft": "┤",
|
|
296
|
-
// Separators (powerline-ish, but pure Unicode)
|
|
297
|
-
"sep.powerline": "▕",
|
|
298
|
-
"sep.powerlineThin": "┆",
|
|
299
|
-
"sep.powerlineLeft": "▶",
|
|
300
|
-
"sep.powerlineRight": "◀",
|
|
301
|
-
"sep.powerlineThinLeft": ">",
|
|
302
|
-
"sep.powerlineThinRight": "<",
|
|
303
|
-
"sep.block": "▌",
|
|
304
|
-
"sep.space": " ",
|
|
305
|
-
"sep.asciiLeft": ">",
|
|
306
|
-
"sep.asciiRight": "<",
|
|
307
|
-
"sep.dot": " · ",
|
|
308
|
-
"sep.slash": " / ",
|
|
309
|
-
"sep.pipe": " │ ",
|
|
310
|
-
// Icons
|
|
311
|
-
"icon.model": "⬢",
|
|
312
|
-
"icon.plan": "🗺",
|
|
313
|
-
"icon.prewalk": "🏃",
|
|
314
|
-
"icon.goal": "🎯",
|
|
315
|
-
"icon.pause": "⏸",
|
|
316
|
-
"icon.loop": "↻",
|
|
317
|
-
"icon.folder": "📁",
|
|
318
|
-
"icon.worktree": "🌳",
|
|
319
|
-
"icon.search": "🔍",
|
|
320
|
-
"icon.scratchFolder": "🗑",
|
|
321
|
-
"icon.file": "📄",
|
|
322
|
-
"icon.git": "⎇",
|
|
323
|
-
"icon.branch": "⑂",
|
|
324
|
-
"icon.pr": "⤴",
|
|
325
|
-
"icon.tokens": "🪙",
|
|
326
|
-
"icon.context": "◫",
|
|
327
|
-
"icon.cost": "💲",
|
|
328
|
-
"icon.time": "⏱",
|
|
329
|
-
"icon.pi": "π",
|
|
330
|
-
"icon.ghost": "👻",
|
|
331
|
-
"icon.agents": "👥",
|
|
332
|
-
"icon.job": "⚙",
|
|
333
|
-
"icon.cache": "💾",
|
|
334
|
-
"icon.cacheMiss": "⊘",
|
|
335
|
-
"icon.input": "⤵",
|
|
336
|
-
"icon.output": "⤴",
|
|
337
|
-
"icon.throughput": "⚡",
|
|
338
|
-
"icon.host": "🖥",
|
|
339
|
-
"icon.session": "🆔",
|
|
340
|
-
"icon.package": "📦",
|
|
341
|
-
"icon.warning": "⚠",
|
|
342
|
-
"icon.rewind": "↶",
|
|
343
|
-
"icon.auto": "⟲",
|
|
344
|
-
"icon.fast": "⚡",
|
|
345
|
-
"icon.extensionSkill": "✦",
|
|
346
|
-
"icon.extensionTool": "🛠",
|
|
347
|
-
"icon.extensionSlashCommand": "⌘",
|
|
348
|
-
"icon.extensionMcp": "🔌",
|
|
349
|
-
"icon.extensionRule": "⚖",
|
|
350
|
-
"icon.extensionHook": "🪝",
|
|
351
|
-
"icon.extensionPrompt": "✎",
|
|
352
|
-
"icon.extensionContextFile": "📎",
|
|
353
|
-
"icon.extensionInstruction": "📘",
|
|
354
|
-
// STT
|
|
355
|
-
"icon.mic": "🎤",
|
|
356
|
-
// Compaction divider
|
|
357
|
-
"icon.camera": "📷",
|
|
358
|
-
// Thinking levels
|
|
359
|
-
"thinking.minimal": "○ min",
|
|
360
|
-
"thinking.low": "◔ low",
|
|
361
|
-
"thinking.medium": "◑ med",
|
|
362
|
-
"thinking.high": "◒ high",
|
|
363
|
-
"thinking.xhigh": "◕ xhigh",
|
|
364
|
-
"thinking.max": "◉ max",
|
|
365
|
-
"thinking.autoPending": "⟳",
|
|
366
|
-
// Checkboxes
|
|
367
|
-
"checkbox.checked": "☑",
|
|
368
|
-
"checkbox.unchecked": "☐",
|
|
369
|
-
// Radio (single-choice)
|
|
370
|
-
"radio.selected": "◉",
|
|
371
|
-
"radio.unselected": "○",
|
|
372
|
-
// Formatting
|
|
373
|
-
"format.bullet": "•",
|
|
374
|
-
"format.dash": "—",
|
|
375
|
-
"format.bracketLeft": "⟦",
|
|
376
|
-
"format.bracketRight": "⟧",
|
|
377
|
-
// Markdown
|
|
378
|
-
"md.quoteBorder": "▏",
|
|
379
|
-
"md.hrChar": "─",
|
|
380
|
-
"md.bullet": "•",
|
|
381
|
-
"md.colorSwatch": "■",
|
|
382
|
-
// Advisor note rail (heavier than md.quoteBorder so notes read as a distinct voice)
|
|
383
|
-
"advisor.rail": "▎",
|
|
384
|
-
// Language/file icons (emoji-centric, no Nerd Font required)
|
|
385
|
-
"lang.default": "⌘",
|
|
386
|
-
"lang.typescript": "🟦",
|
|
387
|
-
"lang.javascript": "🟨",
|
|
388
|
-
"lang.python": "🐍",
|
|
389
|
-
"lang.rust": "🦀",
|
|
390
|
-
"lang.go": "🐹",
|
|
391
|
-
"lang.java": "☕",
|
|
392
|
-
"lang.c": "Ⓒ",
|
|
393
|
-
"lang.cpp": "➕",
|
|
394
|
-
"lang.csharp": "♯",
|
|
395
|
-
"lang.ruby": "💎",
|
|
396
|
-
"lang.julia": "Ⓙ",
|
|
397
|
-
"lang.php": "🐘",
|
|
398
|
-
"lang.swift": "🕊",
|
|
399
|
-
"lang.kotlin": "🅺",
|
|
400
|
-
"lang.shell": "💻",
|
|
401
|
-
"lang.html": "🌐",
|
|
402
|
-
"lang.css": "🎨",
|
|
403
|
-
"lang.json": "🧾",
|
|
404
|
-
"lang.yaml": "📋",
|
|
405
|
-
"lang.markdown": "📝",
|
|
406
|
-
"lang.sql": "🗄",
|
|
407
|
-
"lang.docker": "🐳",
|
|
408
|
-
"lang.lua": "🌙",
|
|
409
|
-
"lang.text": "🗒",
|
|
410
|
-
"lang.env": "🔧",
|
|
411
|
-
"lang.toml": "🧾",
|
|
412
|
-
"lang.xml": "⟨⟩",
|
|
413
|
-
"lang.ini": "⚙",
|
|
414
|
-
"lang.conf": "⚙",
|
|
415
|
-
"lang.log": "📜",
|
|
416
|
-
"lang.csv": "📑",
|
|
417
|
-
"lang.tsv": "📑",
|
|
418
|
-
"lang.image": "🖼",
|
|
419
|
-
"lang.pdf": "📕",
|
|
420
|
-
"lang.archive": "🗜",
|
|
421
|
-
"lang.binary": "⚙",
|
|
422
|
-
// Settings tabs
|
|
423
|
-
"tab.appearance": "🎨",
|
|
424
|
-
"tab.model": "🤖",
|
|
425
|
-
"tab.interaction": "⌨",
|
|
426
|
-
"tab.context": "📋",
|
|
427
|
-
"tab.files": "📁",
|
|
428
|
-
"tab.shell": "💻",
|
|
429
|
-
"tab.tools": "🔧",
|
|
430
|
-
"tab.memory": "🧠",
|
|
431
|
-
"tab.tasks": "📦",
|
|
432
|
-
"tab.providers": "🌐",
|
|
433
|
-
// Tool identity icons (per-tool signature glyph on the success header)
|
|
434
|
-
"tool.write": "✎",
|
|
435
|
-
"tool.edit": "✎",
|
|
436
|
-
"tool.bash": "❯",
|
|
437
|
-
"tool.ssh": "⇄",
|
|
438
|
-
"tool.lsp": "💡",
|
|
439
|
-
"tool.gh": "⎇",
|
|
440
|
-
"tool.webSearch": "⌕",
|
|
441
|
-
"tool.exa": "🔭",
|
|
442
|
-
"tool.browser": "🌐",
|
|
443
|
-
"tool.eval": "▶",
|
|
444
|
-
"tool.debug": "🐞",
|
|
445
|
-
"tool.mcp": "🔌",
|
|
446
|
-
"tool.job": "⚙",
|
|
447
|
-
"tool.launch": "🚀",
|
|
448
|
-
"tool.task": "⇶",
|
|
449
|
-
"tool.todo": "☑",
|
|
450
|
-
"tool.memory": "🧠",
|
|
451
|
-
"tool.ask": "?",
|
|
452
|
-
"tool.resolve": "✓",
|
|
453
|
-
"tool.review": "◉",
|
|
454
|
-
"tool.inspectImage": "🖼",
|
|
455
|
-
"tool.goal": "◎",
|
|
456
|
-
"tool.irc": "✉",
|
|
457
|
-
"tool.delete": "🗑",
|
|
458
|
-
"tool.move": "➜",
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
const NERD_SYMBOLS: SymbolMap = {
|
|
462
|
-
// Status Indicators
|
|
463
|
-
// pick: | alt:
|
|
464
|
-
"status.success": "\uf00c",
|
|
465
|
-
// pick: | alt:
|
|
466
|
-
"status.error": "\uf00d",
|
|
467
|
-
// pick: | alt:
|
|
468
|
-
"status.warning": "\uf12a",
|
|
469
|
-
// pick: | alt:
|
|
470
|
-
"status.info": "\uf129",
|
|
471
|
-
// pick: | alt:
|
|
472
|
-
"status.pending": "\uf254",
|
|
473
|
-
// pick: | alt:
|
|
474
|
-
"status.disabled": "\uf05e",
|
|
475
|
-
// pick: | alt:
|
|
476
|
-
"status.enabled": "\uf111",
|
|
477
|
-
// pick: | alt:
|
|
478
|
-
"status.running": "\uf110",
|
|
479
|
-
// pick: (nf-fa-circle_o, pairs with status.enabled's nf-fa-circle) | alt: ◐ ◑
|
|
480
|
-
"status.shadowed": "\uf10c",
|
|
481
|
-
// pick: | alt:
|
|
482
|
-
"status.aborted": "\uf04d",
|
|
483
|
-
// pick: • | alt: ● ·
|
|
484
|
-
"status.done": "•",
|
|
485
|
-
// Navigation
|
|
486
|
-
// pick: | alt:
|
|
487
|
-
"nav.cursor": "\uf054",
|
|
488
|
-
// pick: | alt:
|
|
489
|
-
"nav.selected": "\uf178",
|
|
490
|
-
// pick: | alt:
|
|
491
|
-
"nav.expand": "\uf0da",
|
|
492
|
-
// pick: | alt:
|
|
493
|
-
"nav.collapse": "\uf0d7",
|
|
494
|
-
// pick: | alt:
|
|
495
|
-
"nav.back": "\uf060",
|
|
496
|
-
// Tree Connectors (same as unicode)
|
|
497
|
-
// pick: ├─ | alt: ├╴ ├╌ ╠═ ┣━
|
|
498
|
-
"tree.branch": "├─",
|
|
499
|
-
// pick: └─ | alt: └╴ └╌ ╚═ ┗━
|
|
500
|
-
"tree.last": "└─",
|
|
501
|
-
// pick: │ | alt: ┃ ║ ▏ ▕
|
|
502
|
-
"tree.vertical": "│",
|
|
503
|
-
// pick: ─ | alt: ━ ═ ╌ ┄
|
|
504
|
-
"tree.horizontal": "─",
|
|
505
|
-
// pick: └ | alt: ╰ ⎿ ↳
|
|
506
|
-
"tree.hook": "└",
|
|
507
|
-
// Box Drawing - Rounded (same as unicode)
|
|
508
|
-
// pick: ╭ | alt: ┌ ┏ ╔
|
|
509
|
-
"boxRound.topLeft": "╭",
|
|
510
|
-
// pick: ╮ | alt: ┐ ┓ ╗
|
|
511
|
-
"boxRound.topRight": "╮",
|
|
512
|
-
// pick: ╰ | alt: └ ┗ ╚
|
|
513
|
-
"boxRound.bottomLeft": "╰",
|
|
514
|
-
// pick: ╯ | alt: ┘ ┛ ╝
|
|
515
|
-
"boxRound.bottomRight": "╯",
|
|
516
|
-
// pick: ─ | alt: ━ ═ ╌
|
|
517
|
-
"boxRound.horizontal": "─",
|
|
518
|
-
// pick: │ | alt: ┃ ║ ▏
|
|
519
|
-
"boxRound.vertical": "│",
|
|
520
|
-
// Box Drawing - Sharp (same as unicode)
|
|
521
|
-
// pick: ┌ | alt: ┏ ╭ ╔
|
|
522
|
-
"boxSharp.topLeft": "┌",
|
|
523
|
-
// pick: ┐ | alt: ┓ ╮ ╗
|
|
524
|
-
"boxSharp.topRight": "┐",
|
|
525
|
-
// pick: └ | alt: ┗ ╰ ╚
|
|
526
|
-
"boxSharp.bottomLeft": "└",
|
|
527
|
-
// pick: ┘ | alt: ┛ ╯ ╝
|
|
528
|
-
"boxSharp.bottomRight": "┘",
|
|
529
|
-
// pick: ─ | alt: ━ ═ ╌
|
|
530
|
-
"boxSharp.horizontal": "─",
|
|
531
|
-
// pick: │ | alt: ┃ ║ ▏
|
|
532
|
-
"boxSharp.vertical": "│",
|
|
533
|
-
// pick: ┼ | alt: ╋ ╬ ┿
|
|
534
|
-
"boxSharp.cross": "┼",
|
|
535
|
-
// pick: ┬ | alt: ╦ ┯ ┳
|
|
536
|
-
"boxSharp.teeDown": "┬",
|
|
537
|
-
// pick: ┴ | alt: ╩ ┷ ┻
|
|
538
|
-
"boxSharp.teeUp": "┴",
|
|
539
|
-
// pick: ├ | alt: ╠ ┝ ┣
|
|
540
|
-
"boxSharp.teeRight": "├",
|
|
541
|
-
// pick: ┤ | alt: ╣ ┥ ┫
|
|
542
|
-
"boxSharp.teeLeft": "┤",
|
|
543
|
-
// Separators - Nerd Font specific
|
|
544
|
-
// pick: | alt:
|
|
545
|
-
"sep.powerline": "\ue0b0",
|
|
546
|
-
// pick: | alt:
|
|
547
|
-
"sep.powerlineThin": "\ue0b1",
|
|
548
|
-
// pick: | alt:
|
|
549
|
-
"sep.powerlineLeft": "\ue0b0",
|
|
550
|
-
// pick: | alt:
|
|
551
|
-
"sep.powerlineRight": "\ue0b2",
|
|
552
|
-
// pick: | alt:
|
|
553
|
-
"sep.powerlineThinLeft": "\ue0b1",
|
|
554
|
-
// pick: | alt:
|
|
555
|
-
"sep.powerlineThinRight": "\ue0b3",
|
|
556
|
-
// pick: █ | alt: ▓ ▒ ░ ▉ ▌
|
|
557
|
-
"sep.block": "█",
|
|
558
|
-
// pick: space | alt: ␠ ·
|
|
559
|
-
"sep.space": " ",
|
|
560
|
-
// pick: > | alt: › » ▸
|
|
561
|
-
"sep.asciiLeft": ">",
|
|
562
|
-
// pick: < | alt: ‹ « ◂
|
|
563
|
-
"sep.asciiRight": "<",
|
|
564
|
-
// pick: · | alt: • ⋅
|
|
565
|
-
"sep.dot": " · ",
|
|
566
|
-
// pick: | alt: / ∕ ⁄
|
|
567
|
-
"sep.slash": "\ue0bb",
|
|
568
|
-
// pick: | alt: │ ┃ |
|
|
569
|
-
"sep.pipe": "\ue0b3",
|
|
570
|
-
// Icons - Nerd Font specific
|
|
571
|
-
// pick: | alt: ◆
|
|
572
|
-
"icon.model": "\uec19",
|
|
573
|
-
// pick: | alt:
|
|
574
|
-
"icon.plan": "\uf2d2",
|
|
575
|
-
"icon.prewalk": "\uf29d",
|
|
576
|
-
// pick: (nf-fa-bullseye) | alt: (nf-md-target) ◎ ⌖
|
|
577
|
-
"icon.goal": "\uf140",
|
|
578
|
-
// pick: (nf-fa-pause) | alt: ⏸ ||
|
|
579
|
-
"icon.pause": "\uf04c",
|
|
580
|
-
// pick: ↻ | alt: ⟳
|
|
581
|
-
"icon.loop": "\uf021",
|
|
582
|
-
// pick: | alt:
|
|
583
|
-
"icon.folder": "\uf115",
|
|
584
|
-
"icon.search": "\uf002",
|
|
585
|
-
// pick: | alt:
|
|
586
|
-
"icon.scratchFolder": "\uf014",
|
|
587
|
-
// pick: nf-fa-sitemap | alt: nf-cod-list_tree
|
|
588
|
-
"icon.worktree": "\uf0e8",
|
|
589
|
-
// pick: | alt:
|
|
590
|
-
"icon.file": "\uf15b",
|
|
591
|
-
// pick: | alt: ⎇
|
|
592
|
-
"icon.git": "\uf1d3",
|
|
593
|
-
// pick: | alt: ⎇
|
|
594
|
-
"icon.branch": "\uf126",
|
|
595
|
-
// pick: (nf-cod-git_pull_request) | alt: (nf-oct-git_pull_request)
|
|
596
|
-
"icon.pr": "\uea64",
|
|
597
|
-
// pick: | alt: ⊛ ◍
|
|
598
|
-
"icon.tokens": "\ue26b",
|
|
599
|
-
// pick: | alt: ◫ ▦
|
|
600
|
-
"icon.context": "\ue70f",
|
|
601
|
-
// pick: | alt: $ ¢
|
|
602
|
-
"icon.cost": "\uf155",
|
|
603
|
-
// pick: | alt: ◷ ◴
|
|
604
|
-
"icon.time": "\uf017",
|
|
605
|
-
// pick: | alt: π ∏ ∑
|
|
606
|
-
"icon.pi": "\ue22c",
|
|
607
|
-
// pick: (nf-md-ghost) | alt: 👻
|
|
608
|
-
"icon.ghost": "\u{f02a0}",
|
|
609
|
-
// pick: | alt:
|
|
610
|
-
"icon.agents": "\uf0c0",
|
|
611
|
-
// pick: (nf-fa-gear) | alt: ⚙
|
|
612
|
-
"icon.job": "\uf013",
|
|
613
|
-
// pick: | alt:
|
|
614
|
-
"icon.cache": "\uf1c0",
|
|
615
|
-
// pick: (fa-ban) | alt: ⊘
|
|
616
|
-
"icon.cacheMiss": "\uf05e",
|
|
617
|
-
// pick: | alt: →
|
|
618
|
-
"icon.input": "\uf090",
|
|
619
|
-
// pick: | alt: →
|
|
620
|
-
"icon.output": "\uf08b",
|
|
621
|
-
// pick: (nf-fa-tachometer) | alt: ⚡ ↬
|
|
622
|
-
"icon.throughput": "\uf0e4",
|
|
623
|
-
// pick: | alt:
|
|
624
|
-
"icon.host": "\uf109",
|
|
625
|
-
// pick: (nf-md-arrow_left_bold_hexagon_outline) | alt:
|
|
626
|
-
"icon.session": "\u{f0051}",
|
|
627
|
-
// pick: | alt:
|
|
628
|
-
"icon.package": "\uf487",
|
|
629
|
-
// pick: | alt:
|
|
630
|
-
"icon.warning": "\uf071",
|
|
631
|
-
// pick: | alt: ↺
|
|
632
|
-
"icon.rewind": "\uf0e2",
|
|
633
|
-
// pick: | alt:
|
|
634
|
-
"icon.auto": "\u{f0068}",
|
|
635
|
-
"icon.fast": "\uf0e7",
|
|
636
|
-
"icon.extensionSkill": "\uf0eb",
|
|
637
|
-
// pick: | alt:
|
|
638
|
-
"icon.extensionTool": "\uf0ad",
|
|
639
|
-
// pick: | alt:
|
|
640
|
-
"icon.extensionSlashCommand": "\uf120",
|
|
641
|
-
// pick: | alt:
|
|
642
|
-
"icon.extensionMcp": "\uf1e6",
|
|
643
|
-
// pick: | alt:
|
|
644
|
-
"icon.extensionRule": "\uf0e3",
|
|
645
|
-
// pick: | alt:
|
|
646
|
-
"icon.extensionHook": "\uf0c1",
|
|
647
|
-
// pick: | alt:
|
|
648
|
-
"icon.extensionPrompt": "\uf075",
|
|
649
|
-
// pick: | alt:
|
|
650
|
-
"icon.extensionContextFile": "\uf0f6",
|
|
651
|
-
// pick: | alt:
|
|
652
|
-
"icon.extensionInstruction": "\uf02d",
|
|
653
|
-
// STT - fa-microphone
|
|
654
|
-
"icon.mic": "\uf130",
|
|
655
|
-
// Compaction divider - fa-camera-retro
|
|
656
|
-
"icon.camera": "\uf083",
|
|
657
|
-
// Thinking levels — increasing circle slices, with fire reserved for max.
|
|
658
|
-
"thinking.minimal": "\u{F0A9E} min",
|
|
659
|
-
"thinking.low": "\u{F0A9F} low",
|
|
660
|
-
"thinking.medium": "\u{F0AA1} med",
|
|
661
|
-
"thinking.high": "\u{F0AA3} high",
|
|
662
|
-
"thinking.xhigh": "\u{F0AA5} xhi",
|
|
663
|
-
"thinking.max": "\u{F06D} max",
|
|
664
|
-
// Auto mode uses shuffle until the model resolves its thinking level.
|
|
665
|
-
"thinking.autoPending": "\u{F074}",
|
|
666
|
-
// Checkboxes
|
|
667
|
-
// pick: | alt:
|
|
668
|
-
"checkbox.checked": "\uf14a",
|
|
669
|
-
// pick: | alt:
|
|
670
|
-
"checkbox.unchecked": "\uf096",
|
|
671
|
-
// Radio (single-choice)
|
|
672
|
-
// pick: (fa-dot-circle-o) | alt: ◉
|
|
673
|
-
"radio.selected": "\uf192",
|
|
674
|
-
// pick: (fa-circle-o) | alt: ○
|
|
675
|
-
"radio.unselected": "\uf10c",
|
|
676
|
-
// pick: | alt: •
|
|
677
|
-
"format.bullet": "\uf111",
|
|
678
|
-
// pick: – | alt: — ― -
|
|
679
|
-
"format.dash": "–",
|
|
680
|
-
// pick: ⟨ | alt: [ ⟦
|
|
681
|
-
"format.bracketLeft": "⟨",
|
|
682
|
-
// pick: ⟩ | alt: ] ⟧
|
|
683
|
-
"format.bracketRight": "⟩",
|
|
684
|
-
// Markdown-specific
|
|
685
|
-
// pick: │ | alt: ┃ ║
|
|
686
|
-
"md.quoteBorder": "│",
|
|
687
|
-
// pick: ─ | alt: ━ ═
|
|
688
|
-
"md.hrChar": "─",
|
|
689
|
-
// pick: | alt: •
|
|
690
|
-
"md.bullet": "\uf111",
|
|
691
|
-
// pick: ■ | alt: (U+F096)
|
|
692
|
-
"md.colorSwatch": "■",
|
|
693
|
-
// pick: ▎ | alt: ┃ │
|
|
694
|
-
"advisor.rail": "▎",
|
|
695
|
-
// Language icons (nerd font devicons)
|
|
696
|
-
"lang.default": "",
|
|
697
|
-
"lang.typescript": "\u{E628}",
|
|
698
|
-
"lang.javascript": "\u{E60C}",
|
|
699
|
-
"lang.python": "\u{E606}",
|
|
700
|
-
"lang.rust": "\u{E7A8}",
|
|
701
|
-
"lang.go": "\u{E627}",
|
|
702
|
-
"lang.java": "\u{E738}",
|
|
703
|
-
"lang.c": "\u{E61E}",
|
|
704
|
-
"lang.cpp": "\u{E61D}",
|
|
705
|
-
"lang.csharp": "\u{E7BC}",
|
|
706
|
-
"lang.ruby": "\u{E791}",
|
|
707
|
-
"lang.julia": "\u{E624}",
|
|
708
|
-
"lang.php": "\u{E608}",
|
|
709
|
-
"lang.swift": "\u{E755}",
|
|
710
|
-
"lang.kotlin": "\u{E634}",
|
|
711
|
-
"lang.shell": "\u{E795}",
|
|
712
|
-
"lang.html": "\u{E736}",
|
|
713
|
-
"lang.css": "\u{E749}",
|
|
714
|
-
"lang.json": "\u{E60B}",
|
|
715
|
-
"lang.yaml": "\u{E615}",
|
|
716
|
-
"lang.markdown": "\u{E609}",
|
|
717
|
-
"lang.sql": "\u{E706}",
|
|
718
|
-
"lang.docker": "\u{E7B0}",
|
|
719
|
-
"lang.lua": "\u{E620}",
|
|
720
|
-
"lang.text": "\u{E612}",
|
|
721
|
-
"lang.env": "\u{E615}",
|
|
722
|
-
"lang.toml": "\u{E615}",
|
|
723
|
-
"lang.xml": "\u{F05C0}",
|
|
724
|
-
"lang.ini": "\u{E615}",
|
|
725
|
-
"lang.conf": "\u{E615}",
|
|
726
|
-
"lang.log": "\u{F0331}",
|
|
727
|
-
"lang.csv": "\u{F021B}",
|
|
728
|
-
"lang.tsv": "\u{F021B}",
|
|
729
|
-
"lang.image": "\u{F021F}",
|
|
730
|
-
"lang.pdf": "\u{F0226}",
|
|
731
|
-
"lang.archive": "\u{F187}",
|
|
732
|
-
"lang.binary": "\u{F019A}",
|
|
733
|
-
// Settings tab icons
|
|
734
|
-
"tab.appearance": "",
|
|
735
|
-
"tab.model": "",
|
|
736
|
-
"tab.interaction": "",
|
|
737
|
-
"tab.context": "",
|
|
738
|
-
"tab.files": "",
|
|
739
|
-
"tab.shell": "",
|
|
740
|
-
"tab.tools": "",
|
|
741
|
-
"tab.memory": "",
|
|
742
|
-
"tab.tasks": "",
|
|
743
|
-
"tab.providers": "",
|
|
744
|
-
// Tool identity icons (per-tool signature glyph on the success header)
|
|
745
|
-
"tool.write": "\uEA7F",
|
|
746
|
-
"tool.edit": "\uEA73",
|
|
747
|
-
"tool.bash": "\uEBCA",
|
|
748
|
-
"tool.ssh": "\uEB3A",
|
|
749
|
-
"tool.lsp": "\uEA61",
|
|
750
|
-
"tool.gh": "\uEA84",
|
|
751
|
-
"tool.webSearch": "\uEB01",
|
|
752
|
-
"tool.exa": "\uEB68",
|
|
753
|
-
"tool.browser": "\uEAAE",
|
|
754
|
-
"tool.eval": "\uEBAF",
|
|
755
|
-
"tool.debug": "\uEAD8",
|
|
756
|
-
"tool.mcp": "\uEB2D",
|
|
757
|
-
"tool.job": "\uEBA2",
|
|
758
|
-
"tool.launch": "\uF135",
|
|
759
|
-
"tool.task": "\uf4a0",
|
|
760
|
-
"tool.todo": "\uEAB3",
|
|
761
|
-
"tool.memory": "\uEACE",
|
|
762
|
-
"tool.ask": "\uEAC7",
|
|
763
|
-
"tool.resolve": "\uEBB1",
|
|
764
|
-
"tool.review": "\uEA70",
|
|
765
|
-
"tool.inspectImage": "\uEAEA",
|
|
766
|
-
"tool.goal": "\uEBF8",
|
|
767
|
-
"tool.irc": "\uF086",
|
|
768
|
-
"tool.delete": "\uf12d",
|
|
769
|
-
"tool.move": "\uf061",
|
|
770
|
-
};
|
|
771
|
-
|
|
772
|
-
const ASCII_SYMBOLS: SymbolMap = {
|
|
773
|
-
// Status Indicators
|
|
774
|
-
"status.success": "[ok]",
|
|
775
|
-
"status.error": "[!!]",
|
|
776
|
-
"status.warning": "[!]",
|
|
777
|
-
"status.info": "[i]",
|
|
778
|
-
"status.pending": "[*]",
|
|
779
|
-
"status.disabled": "[ ]",
|
|
780
|
-
"status.enabled": "[x]",
|
|
781
|
-
"status.running": "[~]",
|
|
782
|
-
"status.shadowed": "[/]",
|
|
783
|
-
"status.aborted": "[-]",
|
|
784
|
-
"status.done": "*",
|
|
785
|
-
// Navigation
|
|
786
|
-
"nav.cursor": ">",
|
|
787
|
-
"nav.selected": "->",
|
|
788
|
-
"nav.expand": "+",
|
|
789
|
-
"nav.collapse": "-",
|
|
790
|
-
"nav.back": "<-",
|
|
791
|
-
// Tree Connectors
|
|
792
|
-
"tree.branch": "|--",
|
|
793
|
-
"tree.last": "'--",
|
|
794
|
-
"tree.vertical": "|",
|
|
795
|
-
"tree.horizontal": "-",
|
|
796
|
-
"tree.hook": "`-",
|
|
797
|
-
// Box Drawing - Rounded (ASCII fallback)
|
|
798
|
-
"boxRound.topLeft": "+",
|
|
799
|
-
"boxRound.topRight": "+",
|
|
800
|
-
"boxRound.bottomLeft": "+",
|
|
801
|
-
"boxRound.bottomRight": "+",
|
|
802
|
-
"boxRound.horizontal": "-",
|
|
803
|
-
"boxRound.vertical": "|",
|
|
804
|
-
// Box Drawing - Sharp (ASCII fallback)
|
|
805
|
-
"boxSharp.topLeft": "+",
|
|
806
|
-
"boxSharp.topRight": "+",
|
|
807
|
-
"boxSharp.bottomLeft": "+",
|
|
808
|
-
"boxSharp.bottomRight": "+",
|
|
809
|
-
"boxSharp.horizontal": "-",
|
|
810
|
-
"boxSharp.vertical": "|",
|
|
811
|
-
"boxSharp.cross": "+",
|
|
812
|
-
"boxSharp.teeDown": "+",
|
|
813
|
-
"boxSharp.teeUp": "+",
|
|
814
|
-
"boxSharp.teeRight": "+",
|
|
815
|
-
"boxSharp.teeLeft": "+",
|
|
816
|
-
// Separators
|
|
817
|
-
"sep.powerline": ">",
|
|
818
|
-
"sep.powerlineThin": ">",
|
|
819
|
-
"sep.powerlineLeft": ">",
|
|
820
|
-
"sep.powerlineRight": "<",
|
|
821
|
-
"sep.powerlineThinLeft": ">",
|
|
822
|
-
"sep.powerlineThinRight": "<",
|
|
823
|
-
"sep.block": "#",
|
|
824
|
-
"sep.space": " ",
|
|
825
|
-
"sep.asciiLeft": ">",
|
|
826
|
-
"sep.asciiRight": "<",
|
|
827
|
-
"sep.dot": " - ",
|
|
828
|
-
"sep.slash": " / ",
|
|
829
|
-
"sep.pipe": " | ",
|
|
830
|
-
// Icons
|
|
831
|
-
"icon.model": "[M]",
|
|
832
|
-
"icon.plan": "plan",
|
|
833
|
-
"icon.prewalk": "prewalk",
|
|
834
|
-
"icon.goal": "goal",
|
|
835
|
-
"icon.pause": "||",
|
|
836
|
-
"icon.loop": "loop",
|
|
837
|
-
"icon.folder": "[D]",
|
|
838
|
-
"icon.worktree": "[wt]",
|
|
839
|
-
"icon.search": "[/]",
|
|
840
|
-
"icon.scratchFolder": "[T]",
|
|
841
|
-
"icon.file": "[F]",
|
|
842
|
-
"icon.git": "git:",
|
|
843
|
-
"icon.branch": "@",
|
|
844
|
-
"icon.pr": "PR",
|
|
845
|
-
"icon.tokens": "tok:",
|
|
846
|
-
"icon.context": "ctx:",
|
|
847
|
-
"icon.cost": "$",
|
|
848
|
-
"icon.time": "t:",
|
|
849
|
-
"icon.pi": "pi",
|
|
850
|
-
"icon.ghost": "@",
|
|
851
|
-
"icon.agents": "AG",
|
|
852
|
-
"icon.job": "bg",
|
|
853
|
-
"icon.output": "out:",
|
|
854
|
-
"icon.throughput": "tok/s:",
|
|
855
|
-
"icon.cache": "cache",
|
|
856
|
-
"icon.cacheMiss": "!",
|
|
857
|
-
"icon.input": "in:",
|
|
858
|
-
"icon.host": "host",
|
|
859
|
-
"icon.session": "id",
|
|
860
|
-
"icon.package": "[P]",
|
|
861
|
-
"icon.warning": "[!]",
|
|
862
|
-
"icon.rewind": "<-",
|
|
863
|
-
"icon.auto": "[A]",
|
|
864
|
-
"icon.fast": ">>",
|
|
865
|
-
"icon.extensionSkill": "SK",
|
|
866
|
-
"icon.extensionTool": "TL",
|
|
867
|
-
"icon.extensionSlashCommand": "/",
|
|
868
|
-
"icon.extensionMcp": "MCP",
|
|
869
|
-
"icon.extensionRule": "RL",
|
|
870
|
-
"icon.extensionHook": "HK",
|
|
871
|
-
"icon.extensionPrompt": "PR",
|
|
872
|
-
"icon.extensionContextFile": "CF",
|
|
873
|
-
"icon.extensionInstruction": "IN",
|
|
874
|
-
// STT
|
|
875
|
-
"icon.mic": "MIC",
|
|
876
|
-
// Compaction divider
|
|
877
|
-
"icon.camera": "[o]",
|
|
878
|
-
// Thinking Levels
|
|
879
|
-
"thinking.minimal": "[min]",
|
|
880
|
-
"thinking.low": "[low]",
|
|
881
|
-
"thinking.medium": "[med]",
|
|
882
|
-
"thinking.high": "[high]",
|
|
883
|
-
"thinking.xhigh": "[xhi]",
|
|
884
|
-
"thinking.max": "[max]",
|
|
885
|
-
"thinking.autoPending": "[~]",
|
|
886
|
-
// Checkboxes
|
|
887
|
-
"checkbox.checked": "[x]",
|
|
888
|
-
"checkbox.unchecked": "[ ]",
|
|
889
|
-
"radio.selected": "(o)",
|
|
890
|
-
"radio.unselected": "( )",
|
|
891
|
-
"format.bullet": "*",
|
|
892
|
-
"format.dash": "-",
|
|
893
|
-
"format.bracketLeft": "[",
|
|
894
|
-
"format.bracketRight": "]",
|
|
895
|
-
// Markdown-specific
|
|
896
|
-
"md.quoteBorder": "|",
|
|
897
|
-
"md.hrChar": "-",
|
|
898
|
-
"md.bullet": "*",
|
|
899
|
-
"md.colorSwatch": "[]",
|
|
900
|
-
"advisor.rail": "|",
|
|
901
|
-
// Language icons (ASCII uses abbreviations)
|
|
902
|
-
"lang.default": "code",
|
|
903
|
-
"lang.typescript": "ts",
|
|
904
|
-
"lang.javascript": "js",
|
|
905
|
-
"lang.python": "py",
|
|
906
|
-
"lang.rust": "rs",
|
|
907
|
-
"lang.go": "go",
|
|
908
|
-
"lang.java": "java",
|
|
909
|
-
"lang.c": "c",
|
|
910
|
-
"lang.cpp": "cpp",
|
|
911
|
-
"lang.csharp": "cs",
|
|
912
|
-
"lang.ruby": "rb",
|
|
913
|
-
"lang.julia": "jl",
|
|
914
|
-
"lang.php": "php",
|
|
915
|
-
"lang.swift": "swift",
|
|
916
|
-
"lang.kotlin": "kt",
|
|
917
|
-
"lang.shell": "sh",
|
|
918
|
-
"lang.html": "html",
|
|
919
|
-
"lang.css": "css",
|
|
920
|
-
"lang.json": "json",
|
|
921
|
-
"lang.yaml": "yaml",
|
|
922
|
-
"lang.markdown": "md",
|
|
923
|
-
"lang.sql": "sql",
|
|
924
|
-
"lang.docker": "docker",
|
|
925
|
-
"lang.lua": "lua",
|
|
926
|
-
"lang.text": "txt",
|
|
927
|
-
"lang.env": "env",
|
|
928
|
-
"lang.toml": "toml",
|
|
929
|
-
"lang.xml": "xml",
|
|
930
|
-
"lang.ini": "ini",
|
|
931
|
-
"lang.conf": "conf",
|
|
932
|
-
"lang.log": "log",
|
|
933
|
-
"lang.csv": "csv",
|
|
934
|
-
"lang.tsv": "tsv",
|
|
935
|
-
"lang.image": "img",
|
|
936
|
-
"lang.pdf": "pdf",
|
|
937
|
-
"lang.archive": "zip",
|
|
938
|
-
"lang.binary": "bin",
|
|
939
|
-
// Settings tab icons
|
|
940
|
-
"tab.appearance": "[A]",
|
|
941
|
-
"tab.model": "[M]",
|
|
942
|
-
"tab.interaction": "[I]",
|
|
943
|
-
"tab.context": "[X]",
|
|
944
|
-
"tab.files": "[F]",
|
|
945
|
-
"tab.shell": "[S]",
|
|
946
|
-
"tab.tools": "[T]",
|
|
947
|
-
"tab.memory": "[Y]",
|
|
948
|
-
"tab.tasks": "[K]",
|
|
949
|
-
"tab.providers": "[P]",
|
|
950
|
-
// Tool identity icons (per-tool signature glyph on the success header)
|
|
951
|
-
"tool.write": "+f",
|
|
952
|
-
"tool.edit": "~",
|
|
953
|
-
"tool.bash": "$",
|
|
954
|
-
"tool.ssh": "ssh",
|
|
955
|
-
"tool.lsp": "lsp",
|
|
956
|
-
"tool.gh": "gh",
|
|
957
|
-
"tool.webSearch": "web",
|
|
958
|
-
"tool.exa": "exa",
|
|
959
|
-
"tool.browser": "[w]",
|
|
960
|
-
"tool.eval": ">_",
|
|
961
|
-
"tool.debug": "dbg",
|
|
962
|
-
"tool.mcp": "<>",
|
|
963
|
-
"tool.job": "job",
|
|
964
|
-
"tool.launch": "run",
|
|
965
|
-
"tool.task": ">>>",
|
|
966
|
-
"tool.todo": "[x]",
|
|
967
|
-
"tool.memory": "mem",
|
|
968
|
-
"tool.ask": "[?]",
|
|
969
|
-
"tool.resolve": "[v]",
|
|
970
|
-
"tool.review": "rev",
|
|
971
|
-
"tool.inspectImage": "[i]",
|
|
972
|
-
"tool.goal": "(o)",
|
|
973
|
-
"tool.irc": "irc",
|
|
974
|
-
"tool.delete": "rm",
|
|
975
|
-
"tool.move": "mv",
|
|
976
|
-
};
|
|
977
|
-
|
|
978
|
-
const SYMBOL_PRESETS: Record<SymbolPreset, SymbolMap> = {
|
|
979
|
-
unicode: UNICODE_SYMBOLS,
|
|
980
|
-
nerd: NERD_SYMBOLS,
|
|
981
|
-
ascii: ASCII_SYMBOLS,
|
|
982
|
-
};
|
|
983
|
-
|
|
984
|
-
export type SpinnerType = "status" | "activity";
|
|
985
|
-
|
|
986
|
-
const SPINNER_FRAMES: Record<SymbolPreset, Record<SpinnerType, string[]>> = {
|
|
987
|
-
unicode: {
|
|
988
|
-
status: ["⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"],
|
|
989
|
-
activity: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
990
|
-
},
|
|
991
|
-
nerd: {
|
|
992
|
-
status: ["", "", "", "", "", "", "", "", "", "", "", ""],
|
|
993
|
-
activity: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
994
|
-
},
|
|
995
|
-
ascii: {
|
|
996
|
-
status: ["|", "/", "-", "\\"],
|
|
997
|
-
activity: ["-", "\\", "|", "/"],
|
|
998
|
-
},
|
|
999
|
-
};
|
|
1000
|
-
|
|
1001
|
-
/**
|
|
1002
|
-
* Shape accepted by `themeJson.symbols.spinnerFrames`. A flat array applies to
|
|
1003
|
-
* both spinner types; an object lets a theme override `status` and/or
|
|
1004
|
-
* `activity` independently. Anything not specified falls back to the symbol
|
|
1005
|
-
* preset's default frames.
|
|
1006
|
-
*/
|
|
1007
|
-
type SpinnerFramesOverride = string[] | { status?: string[]; activity?: string[] };
|
|
1008
|
-
|
|
1009
|
-
function normalizeSpinnerFramesOverride(
|
|
1010
|
-
value: SpinnerFramesOverride | undefined,
|
|
1011
|
-
): Partial<Record<SpinnerType, string[]>> {
|
|
1012
|
-
if (value === undefined) return {};
|
|
1013
|
-
if (Array.isArray(value)) return { status: value, activity: value };
|
|
1014
|
-
const result: Partial<Record<SpinnerType, string[]>> = {};
|
|
1015
|
-
if (value.status) result.status = value.status;
|
|
1016
|
-
if (value.activity) result.activity = value.activity;
|
|
1017
|
-
return result;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
// ============================================================================
|
|
1021
|
-
// Types & Schema
|
|
1022
|
-
// ============================================================================
|
|
1023
|
-
|
|
1024
|
-
type ColorValue = string | number;
|
|
1025
|
-
|
|
1026
|
-
const themeColorsSchema = type({
|
|
1027
|
-
accent: "string | number",
|
|
1028
|
-
border: "string | number",
|
|
1029
|
-
borderAccent: "string | number",
|
|
1030
|
-
borderMuted: "string | number",
|
|
1031
|
-
success: "string | number",
|
|
1032
|
-
error: "string | number",
|
|
1033
|
-
warning: "string | number",
|
|
1034
|
-
muted: "string | number",
|
|
1035
|
-
dim: "string | number",
|
|
1036
|
-
text: "string | number",
|
|
1037
|
-
thinkingText: "string | number",
|
|
1038
|
-
selectedBg: "string | number",
|
|
1039
|
-
userMessageBg: "string | number",
|
|
1040
|
-
userMessageText: "string | number",
|
|
1041
|
-
customMessageBg: "string | number",
|
|
1042
|
-
customMessageText: "string | number",
|
|
1043
|
-
customMessageLabel: "string | number",
|
|
1044
|
-
toolPendingBg: "string | number",
|
|
1045
|
-
toolSuccessBg: "string | number",
|
|
1046
|
-
toolErrorBg: "string | number",
|
|
1047
|
-
toolTitle: "string | number",
|
|
1048
|
-
toolOutput: "string | number",
|
|
1049
|
-
mdHeading: "string | number",
|
|
1050
|
-
mdLink: "string | number",
|
|
1051
|
-
mdLinkUrl: "string | number",
|
|
1052
|
-
mdCode: "string | number",
|
|
1053
|
-
mdCodeBlock: "string | number",
|
|
1054
|
-
mdCodeBlockBorder: "string | number",
|
|
1055
|
-
mdQuote: "string | number",
|
|
1056
|
-
mdQuoteBorder: "string | number",
|
|
1057
|
-
mdHr: "string | number",
|
|
1058
|
-
mdListBullet: "string | number",
|
|
1059
|
-
toolDiffAdded: "string | number",
|
|
1060
|
-
toolDiffRemoved: "string | number",
|
|
1061
|
-
toolDiffContext: "string | number",
|
|
1062
|
-
syntaxComment: "string | number",
|
|
1063
|
-
syntaxKeyword: "string | number",
|
|
1064
|
-
syntaxFunction: "string | number",
|
|
1065
|
-
syntaxVariable: "string | number",
|
|
1066
|
-
syntaxString: "string | number",
|
|
1067
|
-
syntaxNumber: "string | number",
|
|
1068
|
-
syntaxType: "string | number",
|
|
1069
|
-
syntaxOperator: "string | number",
|
|
1070
|
-
syntaxPunctuation: "string | number",
|
|
1071
|
-
thinkingOff: "string | number",
|
|
1072
|
-
thinkingMinimal: "string | number",
|
|
1073
|
-
thinkingLow: "string | number",
|
|
1074
|
-
thinkingMedium: "string | number",
|
|
1075
|
-
thinkingHigh: "string | number",
|
|
1076
|
-
thinkingXhigh: "string | number",
|
|
1077
|
-
"thinkingMax?": "string | number",
|
|
1078
|
-
bashMode: "string | number",
|
|
1079
|
-
pythonMode: "string | number",
|
|
1080
|
-
statusLineBg: "string | number",
|
|
1081
|
-
statusLineSep: "string | number",
|
|
1082
|
-
statusLineModel: "string | number",
|
|
1083
|
-
statusLinePath: "string | number",
|
|
1084
|
-
statusLineGitClean: "string | number",
|
|
1085
|
-
statusLineGitDirty: "string | number",
|
|
1086
|
-
statusLineContext: "string | number",
|
|
1087
|
-
statusLineSpend: "string | number",
|
|
1088
|
-
statusLineStaged: "string | number",
|
|
1089
|
-
statusLineDirty: "string | number",
|
|
1090
|
-
statusLineUntracked: "string | number",
|
|
1091
|
-
statusLineOutput: "string | number",
|
|
1092
|
-
statusLineCost: "string | number",
|
|
1093
|
-
statusLineSubagents: "string | number",
|
|
1094
|
-
});
|
|
1095
|
-
const spinnerFramesSchema = type("unknown").narrow((value): value is SpinnerFramesOverride => {
|
|
1096
|
-
if (Array.isArray(value)) {
|
|
1097
|
-
return value.length >= 1 && value.every(item => typeof item === "string");
|
|
1098
|
-
}
|
|
1099
|
-
if (value && typeof value === "object") {
|
|
1100
|
-
const obj = value as Record<string, unknown>;
|
|
1101
|
-
const status = obj.status;
|
|
1102
|
-
const activity = obj.activity;
|
|
1103
|
-
if (status === undefined && activity === undefined) return false;
|
|
1104
|
-
if (status !== undefined) {
|
|
1105
|
-
if (!Array.isArray(status) || status.length < 1 || !status.every(item => typeof item === "string")) {
|
|
1106
|
-
return false;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
if (activity !== undefined) {
|
|
1110
|
-
if (!Array.isArray(activity) || activity.length < 1 || !activity.every(item => typeof item === "string")) {
|
|
1111
|
-
return false;
|
|
1112
|
-
}
|
|
1113
|
-
}
|
|
1114
|
-
return true;
|
|
1115
|
-
}
|
|
1116
|
-
return false;
|
|
1117
|
-
});
|
|
1118
|
-
const themeJsonSchema = type({
|
|
1119
|
-
"$schema?": "string",
|
|
1120
|
-
name: "string",
|
|
1121
|
-
"vars?": { "[string]": "string | number" },
|
|
1122
|
-
colors: themeColorsSchema,
|
|
1123
|
-
"export?": {
|
|
1124
|
-
"pageBg?": "string | number",
|
|
1125
|
-
"cardBg?": "string | number",
|
|
1126
|
-
"infoBg?": "string | number",
|
|
1127
|
-
},
|
|
1128
|
-
"symbols?": {
|
|
1129
|
-
"preset?": "'unicode' | 'nerd' | 'ascii'",
|
|
1130
|
-
"overrides?": { "[string]": "string" },
|
|
1131
|
-
"spinnerFrames?": spinnerFramesSchema,
|
|
1132
|
-
},
|
|
1133
|
-
});
|
|
1134
|
-
|
|
1135
|
-
type ThemeJson = typeof themeJsonSchema.infer;
|
|
1136
|
-
|
|
1137
|
-
export type ThemeColor =
|
|
1138
|
-
| "accent"
|
|
1139
|
-
| "border"
|
|
1140
|
-
| "borderAccent"
|
|
1141
|
-
| "borderMuted"
|
|
1142
|
-
| "success"
|
|
1143
|
-
| "error"
|
|
1144
|
-
| "warning"
|
|
1145
|
-
| "muted"
|
|
1146
|
-
| "dim"
|
|
1147
|
-
| "text"
|
|
1148
|
-
| "thinkingText"
|
|
1149
|
-
| "userMessageText"
|
|
1150
|
-
| "customMessageText"
|
|
1151
|
-
| "customMessageLabel"
|
|
1152
|
-
| "toolTitle"
|
|
1153
|
-
| "toolOutput"
|
|
1154
|
-
| "mdHeading"
|
|
1155
|
-
| "mdLink"
|
|
1156
|
-
| "mdLinkUrl"
|
|
1157
|
-
| "mdCode"
|
|
1158
|
-
| "mdCodeBlock"
|
|
1159
|
-
| "mdCodeBlockBorder"
|
|
1160
|
-
| "mdQuote"
|
|
1161
|
-
| "mdQuoteBorder"
|
|
1162
|
-
| "mdHr"
|
|
1163
|
-
| "mdListBullet"
|
|
1164
|
-
| "toolDiffAdded"
|
|
1165
|
-
| "toolDiffRemoved"
|
|
1166
|
-
| "toolDiffContext"
|
|
1167
|
-
| "syntaxComment"
|
|
1168
|
-
| "syntaxKeyword"
|
|
1169
|
-
| "syntaxFunction"
|
|
1170
|
-
| "syntaxVariable"
|
|
1171
|
-
| "syntaxString"
|
|
1172
|
-
| "syntaxNumber"
|
|
1173
|
-
| "syntaxType"
|
|
1174
|
-
| "syntaxOperator"
|
|
1175
|
-
| "syntaxPunctuation"
|
|
1176
|
-
| "thinkingOff"
|
|
1177
|
-
| "thinkingMinimal"
|
|
1178
|
-
| "thinkingLow"
|
|
1179
|
-
| "thinkingMedium"
|
|
1180
|
-
| "thinkingHigh"
|
|
1181
|
-
| "thinkingXhigh"
|
|
1182
|
-
| "thinkingMax"
|
|
1183
|
-
| "bashMode"
|
|
1184
|
-
| "pythonMode"
|
|
1185
|
-
| "statusLineSep"
|
|
1186
|
-
| "statusLineModel"
|
|
1187
|
-
| "statusLinePath"
|
|
1188
|
-
| "statusLineGitClean"
|
|
1189
|
-
| "statusLineGitDirty"
|
|
1190
|
-
| "statusLineContext"
|
|
1191
|
-
| "statusLineSpend"
|
|
1192
|
-
| "statusLineStaged"
|
|
1193
|
-
| "statusLineDirty"
|
|
1194
|
-
| "statusLineUntracked"
|
|
1195
|
-
| "statusLineOutput"
|
|
1196
|
-
| "statusLineCost"
|
|
1197
|
-
| "statusLineSubagents";
|
|
1198
|
-
|
|
1199
|
-
/** Set of all valid ThemeColor string values for runtime validation */
|
|
1200
|
-
const THEME_COLOR_RECORD = {
|
|
1201
|
-
accent: true,
|
|
1202
|
-
border: true,
|
|
1203
|
-
borderAccent: true,
|
|
1204
|
-
borderMuted: true,
|
|
1205
|
-
success: true,
|
|
1206
|
-
error: true,
|
|
1207
|
-
warning: true,
|
|
1208
|
-
muted: true,
|
|
1209
|
-
dim: true,
|
|
1210
|
-
text: true,
|
|
1211
|
-
thinkingText: true,
|
|
1212
|
-
userMessageText: true,
|
|
1213
|
-
customMessageText: true,
|
|
1214
|
-
customMessageLabel: true,
|
|
1215
|
-
toolTitle: true,
|
|
1216
|
-
toolOutput: true,
|
|
1217
|
-
mdHeading: true,
|
|
1218
|
-
mdLink: true,
|
|
1219
|
-
mdLinkUrl: true,
|
|
1220
|
-
mdCode: true,
|
|
1221
|
-
mdCodeBlock: true,
|
|
1222
|
-
mdCodeBlockBorder: true,
|
|
1223
|
-
mdQuote: true,
|
|
1224
|
-
mdQuoteBorder: true,
|
|
1225
|
-
mdHr: true,
|
|
1226
|
-
mdListBullet: true,
|
|
1227
|
-
toolDiffAdded: true,
|
|
1228
|
-
toolDiffRemoved: true,
|
|
1229
|
-
toolDiffContext: true,
|
|
1230
|
-
syntaxComment: true,
|
|
1231
|
-
syntaxKeyword: true,
|
|
1232
|
-
syntaxFunction: true,
|
|
1233
|
-
syntaxVariable: true,
|
|
1234
|
-
syntaxString: true,
|
|
1235
|
-
syntaxNumber: true,
|
|
1236
|
-
syntaxType: true,
|
|
1237
|
-
syntaxOperator: true,
|
|
1238
|
-
syntaxPunctuation: true,
|
|
1239
|
-
thinkingOff: true,
|
|
1240
|
-
thinkingMinimal: true,
|
|
1241
|
-
thinkingLow: true,
|
|
1242
|
-
thinkingMedium: true,
|
|
1243
|
-
thinkingHigh: true,
|
|
1244
|
-
thinkingXhigh: true,
|
|
1245
|
-
thinkingMax: true,
|
|
1246
|
-
bashMode: true,
|
|
1247
|
-
pythonMode: true,
|
|
1248
|
-
statusLineSep: true,
|
|
1249
|
-
statusLineModel: true,
|
|
1250
|
-
statusLinePath: true,
|
|
1251
|
-
statusLineGitClean: true,
|
|
1252
|
-
statusLineGitDirty: true,
|
|
1253
|
-
statusLineContext: true,
|
|
1254
|
-
statusLineSpend: true,
|
|
1255
|
-
statusLineStaged: true,
|
|
1256
|
-
statusLineDirty: true,
|
|
1257
|
-
statusLineUntracked: true,
|
|
1258
|
-
statusLineOutput: true,
|
|
1259
|
-
statusLineCost: true,
|
|
1260
|
-
statusLineSubagents: true,
|
|
1261
|
-
} satisfies Record<ThemeColor, true>;
|
|
1262
|
-
|
|
1263
|
-
const VALID_THEME_COLORS: ReadonlySet<string> = new Set(Object.keys(THEME_COLOR_RECORD));
|
|
1264
|
-
|
|
1265
|
-
/** Check if a string is a valid ThemeColor value */
|
|
1266
|
-
export function isValidThemeColor(color: string): color is ThemeColor {
|
|
1267
|
-
return VALID_THEME_COLORS.has(color);
|
|
1268
|
-
}
|
|
1269
|
-
|
|
1270
|
-
export type ThemeBg =
|
|
1271
|
-
| "selectedBg"
|
|
1272
|
-
| "userMessageBg"
|
|
1273
|
-
| "customMessageBg"
|
|
1274
|
-
| "toolPendingBg"
|
|
1275
|
-
| "toolSuccessBg"
|
|
1276
|
-
| "toolErrorBg"
|
|
1277
|
-
| "statusLineBg";
|
|
1278
|
-
|
|
1279
|
-
type ColorMode = "truecolor" | "256color";
|
|
1280
|
-
|
|
1281
|
-
// ============================================================================
|
|
1282
|
-
// Color Utilities
|
|
1283
|
-
// ============================================================================
|
|
1284
|
-
|
|
1285
|
-
function detectColorMode(): ColorMode {
|
|
1286
|
-
const colorterm = Bun.env.COLORTERM;
|
|
1287
|
-
if (colorterm === "truecolor" || colorterm === "24bit") {
|
|
1288
|
-
return "truecolor";
|
|
1289
|
-
}
|
|
1290
|
-
// Windows Terminal supports truecolor
|
|
1291
|
-
if (Bun.env.WT_SESSION) {
|
|
1292
|
-
return "truecolor";
|
|
1293
|
-
}
|
|
1294
|
-
const term = Bun.env.TERM || "";
|
|
1295
|
-
// Only fall back to 256color for truly limited terminals
|
|
1296
|
-
if (term === "dumb" || term === "" || term === "linux") {
|
|
1297
|
-
return "256color";
|
|
1298
|
-
}
|
|
1299
|
-
// Assume truecolor for everything else - virtually all modern terminals support it
|
|
1300
|
-
return "truecolor";
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
function colorToAnsi(color: string, mode: ColorMode): string {
|
|
1304
|
-
const format = mode === "truecolor" ? "ansi-16m" : "ansi-256";
|
|
1305
|
-
const ansi = Bun.color(color, format);
|
|
1306
|
-
if (ansi === null) {
|
|
1307
|
-
throw new Error(`Invalid color value: ${color}`);
|
|
1308
|
-
}
|
|
1309
|
-
return ansi;
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
function fgAnsi(color: string | number, mode: ColorMode): string {
|
|
1313
|
-
if (color === "") return "\x1b[39m";
|
|
1314
|
-
if (typeof color === "number") return `\x1b[38;5;${color}m`;
|
|
1315
|
-
if (typeof color === "string") {
|
|
1316
|
-
return colorToAnsi(color, mode);
|
|
1317
|
-
}
|
|
1318
|
-
throw new Error(`Invalid color value: ${color}`);
|
|
1319
|
-
}
|
|
1320
|
-
|
|
1321
|
-
function bgAnsi(color: string | number, mode: ColorMode): string {
|
|
1322
|
-
if (color === "") return "\x1b[49m";
|
|
1323
|
-
if (typeof color === "number") return `\x1b[48;5;${color}m`;
|
|
1324
|
-
const ansi = colorToAnsi(color, mode);
|
|
1325
|
-
return ansi.replace("\x1b[38;", "\x1b[48;");
|
|
1326
|
-
}
|
|
1327
|
-
|
|
1328
|
-
function resolveVarRefs(
|
|
1329
|
-
value: ColorValue,
|
|
1330
|
-
vars: Record<string, ColorValue>,
|
|
1331
|
-
visited = new Set<string>(),
|
|
1332
|
-
): string | number {
|
|
1333
|
-
if (typeof value === "number" || value === "" || value.startsWith("#")) {
|
|
1334
|
-
return value;
|
|
1335
|
-
}
|
|
1336
|
-
if (visited.has(value)) {
|
|
1337
|
-
throw new Error(`Circular variable reference detected: ${value}`);
|
|
1338
|
-
}
|
|
1339
|
-
if (!(value in vars)) {
|
|
1340
|
-
throw new Error(`Variable reference not found: ${value}`);
|
|
1341
|
-
}
|
|
1342
|
-
visited.add(value);
|
|
1343
|
-
return resolveVarRefs(vars[value], vars, visited);
|
|
1344
|
-
}
|
|
1345
|
-
|
|
1346
|
-
function resolveThemeColors<T extends Record<string, ColorValue>>(
|
|
1347
|
-
colors: T,
|
|
1348
|
-
vars: Record<string, ColorValue> = {},
|
|
1349
|
-
): Record<keyof T, string | number> {
|
|
1350
|
-
const resolved: Record<string, string | number> = {};
|
|
1351
|
-
for (const [key, value] of Object.entries(colors)) {
|
|
1352
|
-
resolved[key] = resolveVarRefs(value, vars);
|
|
1353
|
-
}
|
|
1354
|
-
return resolved as Record<keyof T, string | number>;
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
|
-
// ============================================================================
|
|
1358
|
-
// Theme Class
|
|
1359
|
-
// ============================================================================
|
|
1360
|
-
|
|
1361
|
-
const langMap: Record<string, SymbolKey> = {
|
|
1362
|
-
typescript: "lang.typescript",
|
|
1363
|
-
ts: "lang.typescript",
|
|
1364
|
-
tsx: "lang.typescript",
|
|
1365
|
-
javascript: "lang.javascript",
|
|
1366
|
-
js: "lang.javascript",
|
|
1367
|
-
jsx: "lang.javascript",
|
|
1368
|
-
mjs: "lang.javascript",
|
|
1369
|
-
cjs: "lang.javascript",
|
|
1370
|
-
python: "lang.python",
|
|
1371
|
-
py: "lang.python",
|
|
1372
|
-
rust: "lang.rust",
|
|
1373
|
-
rs: "lang.rust",
|
|
1374
|
-
go: "lang.go",
|
|
1375
|
-
java: "lang.java",
|
|
1376
|
-
c: "lang.c",
|
|
1377
|
-
cpp: "lang.cpp",
|
|
1378
|
-
"c++": "lang.cpp",
|
|
1379
|
-
cc: "lang.cpp",
|
|
1380
|
-
cxx: "lang.cpp",
|
|
1381
|
-
csharp: "lang.csharp",
|
|
1382
|
-
cs: "lang.csharp",
|
|
1383
|
-
ruby: "lang.ruby",
|
|
1384
|
-
rb: "lang.ruby",
|
|
1385
|
-
julia: "lang.julia",
|
|
1386
|
-
jl: "lang.julia",
|
|
1387
|
-
php: "lang.php",
|
|
1388
|
-
swift: "lang.swift",
|
|
1389
|
-
kotlin: "lang.kotlin",
|
|
1390
|
-
kt: "lang.kotlin",
|
|
1391
|
-
bash: "lang.shell",
|
|
1392
|
-
sh: "lang.shell",
|
|
1393
|
-
zsh: "lang.shell",
|
|
1394
|
-
fish: "lang.shell",
|
|
1395
|
-
powershell: "lang.shell",
|
|
1396
|
-
just: "lang.shell",
|
|
1397
|
-
shell: "lang.shell",
|
|
1398
|
-
html: "lang.html",
|
|
1399
|
-
htm: "lang.html",
|
|
1400
|
-
astro: "lang.html",
|
|
1401
|
-
vue: "lang.html",
|
|
1402
|
-
svelte: "lang.html",
|
|
1403
|
-
css: "lang.css",
|
|
1404
|
-
scss: "lang.css",
|
|
1405
|
-
sass: "lang.css",
|
|
1406
|
-
less: "lang.css",
|
|
1407
|
-
json: "lang.json",
|
|
1408
|
-
yaml: "lang.yaml",
|
|
1409
|
-
yml: "lang.yaml",
|
|
1410
|
-
markdown: "lang.markdown",
|
|
1411
|
-
md: "lang.markdown",
|
|
1412
|
-
sql: "lang.sql",
|
|
1413
|
-
dockerfile: "lang.docker",
|
|
1414
|
-
docker: "lang.docker",
|
|
1415
|
-
lua: "lang.lua",
|
|
1416
|
-
text: "lang.text",
|
|
1417
|
-
txt: "lang.text",
|
|
1418
|
-
plain: "lang.text",
|
|
1419
|
-
log: "lang.log",
|
|
1420
|
-
env: "lang.env",
|
|
1421
|
-
dotenv: "lang.env",
|
|
1422
|
-
toml: "lang.toml",
|
|
1423
|
-
xml: "lang.xml",
|
|
1424
|
-
ini: "lang.ini",
|
|
1425
|
-
conf: "lang.conf",
|
|
1426
|
-
cfg: "lang.conf",
|
|
1427
|
-
config: "lang.conf",
|
|
1428
|
-
properties: "lang.conf",
|
|
1429
|
-
csv: "lang.csv",
|
|
1430
|
-
tsv: "lang.tsv",
|
|
1431
|
-
image: "lang.image",
|
|
1432
|
-
img: "lang.image",
|
|
1433
|
-
png: "lang.image",
|
|
1434
|
-
jpg: "lang.image",
|
|
1435
|
-
jpeg: "lang.image",
|
|
1436
|
-
gif: "lang.image",
|
|
1437
|
-
webp: "lang.image",
|
|
1438
|
-
svg: "lang.image",
|
|
1439
|
-
ico: "lang.image",
|
|
1440
|
-
bmp: "lang.image",
|
|
1441
|
-
tiff: "lang.image",
|
|
1442
|
-
pdf: "lang.pdf",
|
|
1443
|
-
zip: "lang.archive",
|
|
1444
|
-
tar: "lang.archive",
|
|
1445
|
-
gz: "lang.archive",
|
|
1446
|
-
tgz: "lang.archive",
|
|
1447
|
-
bz2: "lang.archive",
|
|
1448
|
-
xz: "lang.archive",
|
|
1449
|
-
"7z": "lang.archive",
|
|
1450
|
-
exe: "lang.binary",
|
|
1451
|
-
dll: "lang.binary",
|
|
1452
|
-
so: "lang.binary",
|
|
1453
|
-
dylib: "lang.binary",
|
|
1454
|
-
wasm: "lang.binary",
|
|
1455
|
-
bin: "lang.binary",
|
|
1456
|
-
};
|
|
1457
|
-
|
|
1458
|
-
/**
|
|
1459
|
-
* Brand colors for language icons, keyed by the resolved `lang.*` SymbolKey.
|
|
1460
|
-
* Used by {@link Theme.getLangIconStyled} so eval-kernel cell headers tint each
|
|
1461
|
-
* language with its recognizable hue (JS yellow, Ruby red, Julia purple, Python
|
|
1462
|
-
* blue) instead of a flat muted gray. Applied as truecolor/256 per the active
|
|
1463
|
-
* color mode; languages without an entry fall back to the muted theme color.
|
|
1464
|
-
*/
|
|
1465
|
-
const LANG_BRAND_COLORS: Partial<Record<SymbolKey, string>> = {
|
|
1466
|
-
"lang.javascript": "#f7df1e",
|
|
1467
|
-
"lang.python": "#3776ab",
|
|
1468
|
-
"lang.ruby": "#cc342d",
|
|
1469
|
-
"lang.julia": "#9558b2",
|
|
1470
|
-
};
|
|
1471
|
-
|
|
1472
|
-
/**
|
|
1473
|
-
* Resolve a theme color value (hex string or 256-color index) to a CSS hex string.
|
|
1474
|
-
* Empty string represents the default terminal color.
|
|
1475
|
-
*/
|
|
1476
|
-
function resolveToHex(value: string | number, isLight: boolean): string {
|
|
1477
|
-
if (typeof value === "number") return ansi256ToHex(value);
|
|
1478
|
-
if (value === "") return isLight ? "#000000" : "#e5e5e7";
|
|
1479
|
-
return value;
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
export class Theme {
|
|
1483
|
-
#fgColors: Record<ThemeColor, string>;
|
|
1484
|
-
#bgColors: Record<ThemeBg, string>;
|
|
1485
|
-
/** Resolved hex strings for foreground colors — populated at construction. */
|
|
1486
|
-
readonly #hexFgColors: Record<ThemeColor, string>;
|
|
1487
|
-
/** Resolved hex strings for background colors — populated at construction. */
|
|
1488
|
-
readonly #hexBgColors: Record<ThemeBg, string>;
|
|
1489
|
-
#symbols: SymbolMap;
|
|
1490
|
-
#spinnerFramesOverrides: Partial<Record<SpinnerType, string[]>>;
|
|
1491
|
-
/**
|
|
1492
|
-
* Perceptual luma (0..1) of the status-line background — used to classify the
|
|
1493
|
-
* theme light/dark. Undefined when it can't be resolved. Classified against the
|
|
1494
|
-
* status line (the surface session accents render on) rather than the chat bubble
|
|
1495
|
-
* (`userMessageBg`), which some themes (e.g. `porcelain`) style dark on an
|
|
1496
|
-
* otherwise-light theme.
|
|
1497
|
-
*/
|
|
1498
|
-
readonly statusLineLuminance: number | undefined;
|
|
1499
|
-
/** WCAG relative luminance of the status-line background — basis for accent contrast. */
|
|
1500
|
-
readonly #statusLineContrastLuminance: number | undefined;
|
|
1501
|
-
constructor(
|
|
1502
|
-
fgColors: Record<ThemeColor, string | number>,
|
|
1503
|
-
bgColors: Record<ThemeBg, string | number>,
|
|
1504
|
-
private readonly mode: ColorMode,
|
|
1505
|
-
private readonly symbolPreset: SymbolPreset,
|
|
1506
|
-
symbolOverrides: Partial<Record<SymbolKey, string>>,
|
|
1507
|
-
spinnerFramesOverrides: Partial<Record<SpinnerType, string[]>> = {},
|
|
1508
|
-
) {
|
|
1509
|
-
this.statusLineLuminance = colorLuma(bgColors.statusLineBg);
|
|
1510
|
-
this.#statusLineContrastLuminance = relativeLuminance(bgColors.statusLineBg);
|
|
1511
|
-
const slIsLight = this.statusLineLuminance !== undefined && this.statusLineLuminance > 0.5;
|
|
1512
|
-
|
|
1513
|
-
this.#fgColors = {} as Record<ThemeColor, string>;
|
|
1514
|
-
this.#hexFgColors = {} as Record<ThemeColor, string>;
|
|
1515
|
-
for (const [key, value] of Object.entries(fgColors) as [ThemeColor, string | number][]) {
|
|
1516
|
-
this.#fgColors[key] = fgAnsi(value, mode);
|
|
1517
|
-
this.#hexFgColors[key] = resolveToHex(value, slIsLight);
|
|
1518
|
-
}
|
|
1519
|
-
this.#bgColors = {} as Record<ThemeBg, string>;
|
|
1520
|
-
this.#hexBgColors = {} as Record<ThemeBg, string>;
|
|
1521
|
-
for (const [key, value] of Object.entries(bgColors) as [ThemeBg, string | number][]) {
|
|
1522
|
-
this.#bgColors[key] = bgAnsi(value, mode);
|
|
1523
|
-
this.#hexBgColors[key] = resolveToHex(value, slIsLight);
|
|
1524
|
-
}
|
|
1525
|
-
// Build symbol map from preset + overrides
|
|
1526
|
-
const baseSymbols = SYMBOL_PRESETS[symbolPreset];
|
|
1527
|
-
this.#symbols = { ...baseSymbols };
|
|
1528
|
-
for (const [key, value] of Object.entries(symbolOverrides)) {
|
|
1529
|
-
if (key in this.#symbols) {
|
|
1530
|
-
this.#symbols[key as SymbolKey] = value;
|
|
1531
|
-
} else {
|
|
1532
|
-
logger.debug("Invalid symbol key in override", { key, availableKeys: Object.keys(this.#symbols) });
|
|
1533
|
-
}
|
|
1534
|
-
}
|
|
1535
|
-
this.#spinnerFramesOverrides = spinnerFramesOverrides;
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
/** True when the active theme has a light status-line background. */
|
|
1539
|
-
get isLight(): boolean {
|
|
1540
|
-
return this.statusLineLuminance !== undefined && this.statusLineLuminance > 0.5;
|
|
1541
|
-
}
|
|
1542
|
-
|
|
1543
|
-
/**
|
|
1544
|
-
* Surface luminance to size session accents against on light themes; undefined on
|
|
1545
|
-
* dark themes so accents stay vivid. Pass straight to `getSessionAccentHex`.
|
|
1546
|
-
*/
|
|
1547
|
-
get accentSurfaceLuminance(): number | undefined {
|
|
1548
|
-
return this.isLight ? this.#statusLineContrastLuminance : undefined;
|
|
1549
|
-
}
|
|
1550
|
-
|
|
1551
|
-
/**
|
|
1552
|
-
* Get the resolved CSS hex string for a foreground theme color.
|
|
1553
|
-
*/
|
|
1554
|
-
getColorHex(color: ThemeColor): string {
|
|
1555
|
-
const hex = this.#hexFgColors[color];
|
|
1556
|
-
if (hex === undefined) throw new Error(`Unknown theme color: ${color}`);
|
|
1557
|
-
return hex || (this.isLight ? "#000000" : "#e5e5e7");
|
|
1558
|
-
}
|
|
1559
|
-
|
|
1560
|
-
/**
|
|
1561
|
-
* Get all foreground and background theme colors as CSS hex strings.
|
|
1562
|
-
* Skips colors resolved to the default terminal color (unstyled).
|
|
1563
|
-
*/
|
|
1564
|
-
getAllThemeColorHexes(): string[] {
|
|
1565
|
-
const hexes: string[] = [];
|
|
1566
|
-
for (const hex of Object.values(this.#hexFgColors)) {
|
|
1567
|
-
if (hex) hexes.push(hex);
|
|
1568
|
-
}
|
|
1569
|
-
for (const hex of Object.values(this.#hexBgColors)) {
|
|
1570
|
-
if (hex) hexes.push(hex);
|
|
1571
|
-
}
|
|
1572
|
-
return hexes;
|
|
1573
|
-
}
|
|
1574
|
-
|
|
1575
|
-
/**
|
|
1576
|
-
* Get the most visually dominant theme colors as CSS hex strings — accent,
|
|
1577
|
-
* border, success, error, warning, heading, link, diff markers, etc.
|
|
1578
|
-
* These are the colors the session accent could visually clash with.
|
|
1579
|
-
* Skips colors resolved to the default terminal color (unstyled).
|
|
1580
|
-
*/
|
|
1581
|
-
getMajorThemeColorHexes(): string[] {
|
|
1582
|
-
const majors: ThemeColor[] = [
|
|
1583
|
-
"accent",
|
|
1584
|
-
"border",
|
|
1585
|
-
"borderAccent",
|
|
1586
|
-
"borderMuted",
|
|
1587
|
-
"success",
|
|
1588
|
-
"error",
|
|
1589
|
-
"warning",
|
|
1590
|
-
"mdHeading",
|
|
1591
|
-
"mdLink",
|
|
1592
|
-
"mdCode",
|
|
1593
|
-
"mdCodeBlock",
|
|
1594
|
-
"mdQuoteBorder",
|
|
1595
|
-
"mdListBullet",
|
|
1596
|
-
"toolDiffAdded",
|
|
1597
|
-
"toolDiffRemoved",
|
|
1598
|
-
"customMessageLabel",
|
|
1599
|
-
"thinkingText",
|
|
1600
|
-
];
|
|
1601
|
-
const hexes: string[] = [];
|
|
1602
|
-
for (const key of majors) {
|
|
1603
|
-
const hex = this.#hexFgColors[key];
|
|
1604
|
-
if (hex) hexes.push(hex);
|
|
1605
|
-
}
|
|
1606
|
-
return hexes;
|
|
1607
|
-
}
|
|
1608
|
-
/**
|
|
1609
|
-
* Get the resolved CSS hex string for the theme's accent color.
|
|
1610
|
-
*/
|
|
1611
|
-
getAccentColorHex(): string {
|
|
1612
|
-
return this.getColorHex("accent");
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
fg(color: ThemeColor, text: string): string {
|
|
1616
|
-
const ansi = this.#fgColors[color];
|
|
1617
|
-
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
|
1618
|
-
return `${ansi}${text}\x1b[39m`; // Reset only foreground color
|
|
1619
|
-
}
|
|
1620
|
-
|
|
1621
|
-
bg(color: ThemeBg, text: string): string {
|
|
1622
|
-
const ansi = this.#bgColors[color];
|
|
1623
|
-
if (!ansi) throw new Error(`Unknown theme background color: ${color}`);
|
|
1624
|
-
return `${ansi}${text}\x1b[49m`; // Reset only background color
|
|
1625
|
-
}
|
|
1626
|
-
|
|
1627
|
-
bold(text: string): string {
|
|
1628
|
-
return chalk.bold(text);
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
italic(text: string): string {
|
|
1632
|
-
return chalk.italic(text);
|
|
1633
|
-
}
|
|
1634
|
-
|
|
1635
|
-
underline(text: string): string {
|
|
1636
|
-
return chalk.underline(text);
|
|
1637
|
-
}
|
|
1638
|
-
|
|
1639
|
-
strikethrough(text: string): string {
|
|
1640
|
-
return chalk.strikethrough(text);
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
inverse(text: string): string {
|
|
1644
|
-
return chalk.inverse(text);
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
getFgAnsi(color: ThemeColor): string {
|
|
1648
|
-
const ansi = this.#fgColors[color];
|
|
1649
|
-
if (!ansi) throw new Error(`Unknown theme color: ${color}`);
|
|
1650
|
-
return ansi;
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
getBgAnsi(color: ThemeBg): string {
|
|
1654
|
-
const ansi = this.#bgColors[color];
|
|
1655
|
-
if (!ansi) throw new Error(`Unknown theme background color: ${color}`);
|
|
1656
|
-
return ansi;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
/**
|
|
1660
|
-
* Foreground ANSI for text drawn **on top of** `fillColor` used as a solid
|
|
1661
|
-
* background (e.g. a powerline chip). Picks near-black or near-white by the
|
|
1662
|
-
* fill's perceived luminance (Rec. 601 luma) so the label stays legible on
|
|
1663
|
-
* both bright and dark fills, across light and dark themes.
|
|
1664
|
-
*
|
|
1665
|
-
* Reads the RGB out of the already-resolved truecolor escape; when the fill
|
|
1666
|
-
* is encoded as a 256-palette index (limited terminals) the RGB is
|
|
1667
|
-
* unavailable, so it falls back to the theme `text` color.
|
|
1668
|
-
*/
|
|
1669
|
-
getContrastFgAnsi(fillColor: ThemeColor): string {
|
|
1670
|
-
const ansi = this.#fgColors[fillColor];
|
|
1671
|
-
const match = ansi ? /38;2;(\d+);(\d+);(\d+)/.exec(ansi) : null;
|
|
1672
|
-
if (!match) return this.#fgColors.text;
|
|
1673
|
-
const luma = 0.299 * Number(match[1]) + 0.587 * Number(match[2]) + 0.114 * Number(match[3]);
|
|
1674
|
-
return luma > 140 ? "\x1b[38;2;0;0;0m" : "\x1b[38;2;255;255;255m";
|
|
1675
|
-
}
|
|
1676
|
-
|
|
1677
|
-
getColorMode(): ColorMode {
|
|
1678
|
-
return this.mode;
|
|
1679
|
-
}
|
|
1680
|
-
|
|
1681
|
-
getThinkingBorderColor(level: ThinkingLevel | Effort): (str: string) => string {
|
|
1682
|
-
// Map thinking levels to dedicated theme colors
|
|
1683
|
-
switch (level) {
|
|
1684
|
-
case "off":
|
|
1685
|
-
return (str: string) => this.fg("thinkingOff", str);
|
|
1686
|
-
case "minimal":
|
|
1687
|
-
return (str: string) => this.fg("thinkingMinimal", str);
|
|
1688
|
-
case "low":
|
|
1689
|
-
return (str: string) => this.fg("thinkingLow", str);
|
|
1690
|
-
case "medium":
|
|
1691
|
-
return (str: string) => this.fg("thinkingMedium", str);
|
|
1692
|
-
case "high":
|
|
1693
|
-
return (str: string) => this.fg("thinkingHigh", str);
|
|
1694
|
-
case "xhigh":
|
|
1695
|
-
return (str: string) => this.fg("thinkingXhigh", str);
|
|
1696
|
-
case "max":
|
|
1697
|
-
// thinkingMax is optional; themes without it resolve to the xhigh color.
|
|
1698
|
-
return (str: string) => this.fg(this.#fgColors.thinkingMax ? "thinkingMax" : "thinkingXhigh", str);
|
|
1699
|
-
default:
|
|
1700
|
-
return (str: string) => this.fg("thinkingOff", str);
|
|
1701
|
-
}
|
|
1702
|
-
}
|
|
1703
|
-
|
|
1704
|
-
getBashModeBorderColor(): (str: string) => string {
|
|
1705
|
-
return (str: string) => this.fg("bashMode", str);
|
|
1706
|
-
}
|
|
1707
|
-
|
|
1708
|
-
getPythonModeBorderColor(): (str: string) => string {
|
|
1709
|
-
return (str: string) => this.fg("pythonMode", str);
|
|
1710
|
-
}
|
|
1711
|
-
|
|
1712
|
-
// ============================================================================
|
|
1713
|
-
// Symbol Methods
|
|
1714
|
-
// ============================================================================
|
|
1715
|
-
|
|
1716
|
-
/**
|
|
1717
|
-
* Get a symbol by key.
|
|
1718
|
-
*/
|
|
1719
|
-
symbol(key: SymbolKey): string {
|
|
1720
|
-
return this.#symbols[key];
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
/**
|
|
1724
|
-
* Get a symbol styled with a color.
|
|
1725
|
-
*/
|
|
1726
|
-
styledSymbol(key: SymbolKey, color: ThemeColor): string {
|
|
1727
|
-
return this.fg(color, this.#symbols[key]);
|
|
1728
|
-
}
|
|
1729
|
-
|
|
1730
|
-
/**
|
|
1731
|
-
* Get the current symbol preset.
|
|
1732
|
-
*/
|
|
1733
|
-
getSymbolPreset(): SymbolPreset {
|
|
1734
|
-
return this.symbolPreset;
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
// ============================================================================
|
|
1738
|
-
// Symbol Category Accessors
|
|
1739
|
-
// ============================================================================
|
|
1740
|
-
|
|
1741
|
-
get status() {
|
|
1742
|
-
return {
|
|
1743
|
-
success: this.#symbols["status.success"],
|
|
1744
|
-
error: this.#symbols["status.error"],
|
|
1745
|
-
warning: this.#symbols["status.warning"],
|
|
1746
|
-
info: this.#symbols["status.info"],
|
|
1747
|
-
pending: this.#symbols["status.pending"],
|
|
1748
|
-
disabled: this.#symbols["status.disabled"],
|
|
1749
|
-
enabled: this.#symbols["status.enabled"],
|
|
1750
|
-
running: this.#symbols["status.running"],
|
|
1751
|
-
shadowed: this.#symbols["status.shadowed"],
|
|
1752
|
-
aborted: this.#symbols["status.aborted"],
|
|
1753
|
-
done: this.#symbols["status.done"],
|
|
1754
|
-
};
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
get nav() {
|
|
1758
|
-
return {
|
|
1759
|
-
cursor: this.#symbols["nav.cursor"],
|
|
1760
|
-
selected: this.#symbols["nav.selected"],
|
|
1761
|
-
expand: this.#symbols["nav.expand"],
|
|
1762
|
-
collapse: this.#symbols["nav.collapse"],
|
|
1763
|
-
back: this.#symbols["nav.back"],
|
|
1764
|
-
};
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
get tree() {
|
|
1768
|
-
return {
|
|
1769
|
-
branch: this.#symbols["tree.branch"],
|
|
1770
|
-
last: this.#symbols["tree.last"],
|
|
1771
|
-
vertical: this.#symbols["tree.vertical"],
|
|
1772
|
-
horizontal: this.#symbols["tree.horizontal"],
|
|
1773
|
-
hook: this.#symbols["tree.hook"],
|
|
1774
|
-
};
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
get boxRound() {
|
|
1778
|
-
return {
|
|
1779
|
-
topLeft: this.#symbols["boxRound.topLeft"],
|
|
1780
|
-
topRight: this.#symbols["boxRound.topRight"],
|
|
1781
|
-
bottomLeft: this.#symbols["boxRound.bottomLeft"],
|
|
1782
|
-
bottomRight: this.#symbols["boxRound.bottomRight"],
|
|
1783
|
-
horizontal: this.#symbols["boxRound.horizontal"],
|
|
1784
|
-
vertical: this.#symbols["boxRound.vertical"],
|
|
1785
|
-
// Junctions have no rounded Unicode variant, so a rounded box reuses the
|
|
1786
|
-
// sharp tee/cross glyphs. Sourcing them from the boxSharp.* tokens keeps a
|
|
1787
|
-
// theme's `boxSharp.tee*` overrides effective for rounded-box dividers.
|
|
1788
|
-
cross: this.#symbols["boxSharp.cross"],
|
|
1789
|
-
teeDown: this.#symbols["boxSharp.teeDown"],
|
|
1790
|
-
teeUp: this.#symbols["boxSharp.teeUp"],
|
|
1791
|
-
teeRight: this.#symbols["boxSharp.teeRight"],
|
|
1792
|
-
teeLeft: this.#symbols["boxSharp.teeLeft"],
|
|
1793
|
-
};
|
|
1794
|
-
}
|
|
1795
|
-
|
|
1796
|
-
get boxSharp() {
|
|
1797
|
-
return {
|
|
1798
|
-
topLeft: this.#symbols["boxSharp.topLeft"],
|
|
1799
|
-
topRight: this.#symbols["boxSharp.topRight"],
|
|
1800
|
-
bottomLeft: this.#symbols["boxSharp.bottomLeft"],
|
|
1801
|
-
bottomRight: this.#symbols["boxSharp.bottomRight"],
|
|
1802
|
-
horizontal: this.#symbols["boxSharp.horizontal"],
|
|
1803
|
-
vertical: this.#symbols["boxSharp.vertical"],
|
|
1804
|
-
cross: this.#symbols["boxSharp.cross"],
|
|
1805
|
-
teeDown: this.#symbols["boxSharp.teeDown"],
|
|
1806
|
-
teeUp: this.#symbols["boxSharp.teeUp"],
|
|
1807
|
-
teeRight: this.#symbols["boxSharp.teeRight"],
|
|
1808
|
-
teeLeft: this.#symbols["boxSharp.teeLeft"],
|
|
1809
|
-
};
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
get sep() {
|
|
1813
|
-
return {
|
|
1814
|
-
powerline: this.#symbols["sep.powerline"],
|
|
1815
|
-
powerlineThin: this.#symbols["sep.powerlineThin"],
|
|
1816
|
-
powerlineLeft: this.#symbols["sep.powerlineLeft"],
|
|
1817
|
-
powerlineRight: this.#symbols["sep.powerlineRight"],
|
|
1818
|
-
powerlineThinLeft: this.#symbols["sep.powerlineThinLeft"],
|
|
1819
|
-
powerlineThinRight: this.#symbols["sep.powerlineThinRight"],
|
|
1820
|
-
block: this.#symbols["sep.block"],
|
|
1821
|
-
space: this.#symbols["sep.space"],
|
|
1822
|
-
asciiLeft: this.#symbols["sep.asciiLeft"],
|
|
1823
|
-
asciiRight: this.#symbols["sep.asciiRight"],
|
|
1824
|
-
dot: this.#symbols["sep.dot"],
|
|
1825
|
-
slash: this.#symbols["sep.slash"],
|
|
1826
|
-
pipe: this.#symbols["sep.pipe"],
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
|
|
1830
|
-
get icon() {
|
|
1831
|
-
return {
|
|
1832
|
-
model: this.#symbols["icon.model"],
|
|
1833
|
-
plan: this.#symbols["icon.plan"],
|
|
1834
|
-
prewalk: this.#symbols["icon.prewalk"],
|
|
1835
|
-
goal: this.#symbols["icon.goal"],
|
|
1836
|
-
pause: this.#symbols["icon.pause"],
|
|
1837
|
-
loop: this.#symbols["icon.loop"],
|
|
1838
|
-
folder: this.#symbols["icon.folder"],
|
|
1839
|
-
worktree: this.#symbols["icon.worktree"],
|
|
1840
|
-
scratchFolder: this.#symbols["icon.scratchFolder"],
|
|
1841
|
-
file: this.#symbols["icon.file"],
|
|
1842
|
-
git: this.#symbols["icon.git"],
|
|
1843
|
-
branch: this.#symbols["icon.branch"],
|
|
1844
|
-
pr: this.#symbols["icon.pr"],
|
|
1845
|
-
tokens: this.#symbols["icon.tokens"],
|
|
1846
|
-
context: this.#symbols["icon.context"],
|
|
1847
|
-
cost: this.#symbols["icon.cost"],
|
|
1848
|
-
time: this.#symbols["icon.time"],
|
|
1849
|
-
pi: this.#symbols["icon.pi"],
|
|
1850
|
-
ghost: this.#symbols["icon.ghost"],
|
|
1851
|
-
agents: this.#symbols["icon.agents"],
|
|
1852
|
-
job: this.#symbols["icon.job"],
|
|
1853
|
-
cache: this.#symbols["icon.cache"],
|
|
1854
|
-
cacheMiss: this.#symbols["icon.cacheMiss"],
|
|
1855
|
-
input: this.#symbols["icon.input"],
|
|
1856
|
-
output: this.#symbols["icon.output"],
|
|
1857
|
-
throughput: this.#symbols["icon.throughput"],
|
|
1858
|
-
host: this.#symbols["icon.host"],
|
|
1859
|
-
session: this.#symbols["icon.session"],
|
|
1860
|
-
package: this.#symbols["icon.package"],
|
|
1861
|
-
warning: this.#symbols["icon.warning"],
|
|
1862
|
-
rewind: this.#symbols["icon.rewind"],
|
|
1863
|
-
auto: this.#symbols["icon.auto"],
|
|
1864
|
-
fast: this.#symbols["icon.fast"],
|
|
1865
|
-
extensionSkill: this.#symbols["icon.extensionSkill"],
|
|
1866
|
-
extensionTool: this.#symbols["icon.extensionTool"],
|
|
1867
|
-
extensionSlashCommand: this.#symbols["icon.extensionSlashCommand"],
|
|
1868
|
-
extensionMcp: this.#symbols["icon.extensionMcp"],
|
|
1869
|
-
extensionRule: this.#symbols["icon.extensionRule"],
|
|
1870
|
-
extensionHook: this.#symbols["icon.extensionHook"],
|
|
1871
|
-
extensionPrompt: this.#symbols["icon.extensionPrompt"],
|
|
1872
|
-
extensionContextFile: this.#symbols["icon.extensionContextFile"],
|
|
1873
|
-
extensionInstruction: this.#symbols["icon.extensionInstruction"],
|
|
1874
|
-
mic: this.#symbols["icon.mic"],
|
|
1875
|
-
camera: this.#symbols["icon.camera"],
|
|
1876
|
-
};
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
get thinking() {
|
|
1880
|
-
return {
|
|
1881
|
-
minimal: this.#symbols["thinking.minimal"],
|
|
1882
|
-
low: this.#symbols["thinking.low"],
|
|
1883
|
-
medium: this.#symbols["thinking.medium"],
|
|
1884
|
-
high: this.#symbols["thinking.high"],
|
|
1885
|
-
xhigh: this.#symbols["thinking.xhigh"],
|
|
1886
|
-
max: this.#symbols["thinking.max"],
|
|
1887
|
-
autoPending: this.#symbols["thinking.autoPending"],
|
|
1888
|
-
};
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
get checkbox() {
|
|
1892
|
-
return {
|
|
1893
|
-
checked: this.#symbols["checkbox.checked"],
|
|
1894
|
-
unchecked: this.#symbols["checkbox.unchecked"],
|
|
1895
|
-
};
|
|
1896
|
-
}
|
|
1897
|
-
|
|
1898
|
-
get radio() {
|
|
1899
|
-
return {
|
|
1900
|
-
selected: this.#symbols["radio.selected"],
|
|
1901
|
-
unselected: this.#symbols["radio.unselected"],
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
get format() {
|
|
1906
|
-
return {
|
|
1907
|
-
bullet: this.#symbols["format.bullet"],
|
|
1908
|
-
dash: this.#symbols["format.dash"],
|
|
1909
|
-
bracketLeft: this.#symbols["format.bracketLeft"],
|
|
1910
|
-
bracketRight: this.#symbols["format.bracketRight"],
|
|
1911
|
-
};
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
get md() {
|
|
1915
|
-
return {
|
|
1916
|
-
quoteBorder: this.#symbols["md.quoteBorder"],
|
|
1917
|
-
hrChar: this.#symbols["md.hrChar"],
|
|
1918
|
-
bullet: this.#symbols["md.bullet"],
|
|
1919
|
-
colorSwatch: this.#symbols["md.colorSwatch"],
|
|
1920
|
-
};
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
/**
|
|
1924
|
-
* Default spinner frames (status spinner).
|
|
1925
|
-
*/
|
|
1926
|
-
get spinnerFrames(): string[] {
|
|
1927
|
-
return this.getSpinnerFrames();
|
|
1928
|
-
}
|
|
1929
|
-
|
|
1930
|
-
/**
|
|
1931
|
-
* Get spinner frames by type.
|
|
1932
|
-
*/
|
|
1933
|
-
getSpinnerFrames(type: SpinnerType = "status"): string[] {
|
|
1934
|
-
return this.#spinnerFramesOverrides[type] ?? SPINNER_FRAMES[this.symbolPreset][type];
|
|
1935
|
-
}
|
|
1936
|
-
|
|
1937
|
-
/**
|
|
1938
|
-
* Get language icon for a language name.
|
|
1939
|
-
* Maps common language names to their corresponding symbol keys.
|
|
1940
|
-
*/
|
|
1941
|
-
getLangIcon(lang: string | undefined): string {
|
|
1942
|
-
if (!lang) return this.#symbols["lang.default"];
|
|
1943
|
-
const normalized = lang.toLowerCase();
|
|
1944
|
-
const key = langMap[normalized];
|
|
1945
|
-
return key ? this.#symbols[key] : this.#symbols["lang.default"];
|
|
1946
|
-
}
|
|
1947
|
-
|
|
1948
|
-
/**
|
|
1949
|
-
* Language icon tinted with the language's brand color (see
|
|
1950
|
-
* {@link LANG_BRAND_COLORS}). Falls back to the muted theme color for
|
|
1951
|
-
* languages without a brand entry, and returns the bare (possibly empty)
|
|
1952
|
-
* icon when the active symbol preset has none.
|
|
1953
|
-
*/
|
|
1954
|
-
getLangIconStyled(lang: string | undefined): string {
|
|
1955
|
-
const icon = this.getLangIcon(lang);
|
|
1956
|
-
if (!icon) return icon;
|
|
1957
|
-
const key = lang ? langMap[lang.toLowerCase()] : undefined;
|
|
1958
|
-
const hex = key ? LANG_BRAND_COLORS[key] : undefined;
|
|
1959
|
-
if (!hex) return this.fg("muted", icon);
|
|
1960
|
-
return `${colorToAnsi(hex, this.mode)}${icon}\x1b[39m`;
|
|
1961
|
-
}
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
// ============================================================================
|
|
1965
|
-
// Theme Loading
|
|
1966
|
-
// ============================================================================
|
|
1967
|
-
|
|
1968
|
-
const BUILTIN_THEMES: Record<string, ThemeJson> = {
|
|
1969
|
-
dark: darkThemeJson as ThemeJson,
|
|
1970
|
-
light: lightThemeJson as ThemeJson,
|
|
1971
|
-
...(defaultThemes as Record<string, ThemeJson>),
|
|
1972
|
-
};
|
|
1973
|
-
|
|
1974
|
-
function getBuiltinThemes(): Record<string, ThemeJson> {
|
|
1975
|
-
return BUILTIN_THEMES;
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
export async function getAvailableThemes(): Promise<string[]> {
|
|
1979
|
-
const themes = new Set<string>(Object.keys(getBuiltinThemes()));
|
|
1980
|
-
const customThemesDir = getCustomThemesDir();
|
|
1981
|
-
try {
|
|
1982
|
-
const files = await fs.promises.readdir(customThemesDir);
|
|
1983
|
-
for (const file of files) {
|
|
1984
|
-
if (file.endsWith(".json")) {
|
|
1985
|
-
themes.add(file.slice(0, -5));
|
|
1986
|
-
}
|
|
1987
|
-
}
|
|
1988
|
-
} catch {
|
|
1989
|
-
// Directory doesn't exist or isn't readable
|
|
1990
|
-
}
|
|
1991
|
-
return Array.from(themes).sort();
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
export interface ThemeInfo {
|
|
1995
|
-
name: string;
|
|
1996
|
-
path: string | undefined;
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1999
|
-
export async function getAvailableThemesWithPaths(): Promise<ThemeInfo[]> {
|
|
2000
|
-
const result: ThemeInfo[] = [];
|
|
2001
|
-
|
|
2002
|
-
// Built-in themes (embedded, no file path)
|
|
2003
|
-
for (const name of Object.keys(getBuiltinThemes())) {
|
|
2004
|
-
result.push({ name, path: undefined });
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
|
-
// Custom themes
|
|
2008
|
-
const customThemesDir = getCustomThemesDir();
|
|
2009
|
-
try {
|
|
2010
|
-
const files = await fs.promises.readdir(customThemesDir);
|
|
2011
|
-
for (const file of files) {
|
|
2012
|
-
if (file.endsWith(".json")) {
|
|
2013
|
-
const name = file.slice(0, -5);
|
|
2014
|
-
if (!result.some(themeInfo => themeInfo.name === name)) {
|
|
2015
|
-
result.push({ name, path: path.join(customThemesDir, file) });
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
}
|
|
2019
|
-
} catch {
|
|
2020
|
-
// Directory doesn't exist or isn't readable
|
|
2021
|
-
}
|
|
2022
|
-
|
|
2023
|
-
return result.sort((a, b) => a.name.localeCompare(b.name));
|
|
2024
|
-
}
|
|
2025
|
-
|
|
2026
|
-
async function loadThemeJson(name: string): Promise<ThemeJson> {
|
|
2027
|
-
const builtinThemes = getBuiltinThemes();
|
|
2028
|
-
if (name in builtinThemes) {
|
|
2029
|
-
return builtinThemes[name];
|
|
2030
|
-
}
|
|
2031
|
-
const customThemesDir = getCustomThemesDir();
|
|
2032
|
-
const themePath = path.join(customThemesDir, `${name}.json`);
|
|
2033
|
-
let content: string;
|
|
2034
|
-
try {
|
|
2035
|
-
content = await Bun.file(themePath).text();
|
|
2036
|
-
} catch (err) {
|
|
2037
|
-
if (isEnoent(err)) throw new Error(`Theme not found: ${name}`);
|
|
2038
|
-
throw err;
|
|
2039
|
-
}
|
|
2040
|
-
let json: unknown;
|
|
2041
|
-
try {
|
|
2042
|
-
json = JSON.parse(content);
|
|
2043
|
-
} catch (error) {
|
|
2044
|
-
throw new Error(`Failed to parse theme ${name}: ${error}`);
|
|
2045
|
-
}
|
|
2046
|
-
let parsed: ThemeJson;
|
|
2047
|
-
try {
|
|
2048
|
-
parsed = themeJsonSchema(json) as ThemeJson;
|
|
2049
|
-
if (parsed instanceof type.errors) {
|
|
2050
|
-
throw new Error(parsed.summary);
|
|
2051
|
-
}
|
|
2052
|
-
} catch (error) {
|
|
2053
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2054
|
-
// Extract color key information if available
|
|
2055
|
-
const missingColorMatch = errorMessage.match(/missing keys: (.+)/i);
|
|
2056
|
-
const missingColors: string[] = missingColorMatch ? missingColorMatch[1].split(",").map(s => s.trim()) : [];
|
|
2057
|
-
|
|
2058
|
-
let fullErrorMessage = `Invalid theme "${name}":\n`;
|
|
2059
|
-
if (missingColors.length > 0) {
|
|
2060
|
-
fullErrorMessage += `\nMissing required color tokens:\n`;
|
|
2061
|
-
fullErrorMessage += missingColors.map(c => ` - ${c}`).join("\n");
|
|
2062
|
-
fullErrorMessage += `\n\nPlease add these colors to your theme's "colors" object.`;
|
|
2063
|
-
fullErrorMessage += `\nSee the built-in themes (dark.json, light.json) for reference values.`;
|
|
2064
|
-
}
|
|
2065
|
-
fullErrorMessage += `\n\nValidation error:\n - ${errorMessage}`;
|
|
2066
|
-
|
|
2067
|
-
throw new Error(fullErrorMessage);
|
|
2068
|
-
}
|
|
2069
|
-
return parsed;
|
|
2070
|
-
}
|
|
2071
|
-
|
|
2072
|
-
interface CreateThemeOptions {
|
|
2073
|
-
mode?: ColorMode;
|
|
2074
|
-
symbolPresetOverride?: SymbolPreset;
|
|
2075
|
-
colorBlindMode?: boolean;
|
|
2076
|
-
}
|
|
2077
|
-
|
|
2078
|
-
/** HSV adjustment to shift green toward blue for colorblind mode (red-green colorblindness) */
|
|
2079
|
-
const COLORBLIND_ADJUSTMENT = { h: 60, s: 0.71 };
|
|
2080
|
-
|
|
2081
|
-
function createTheme(themeJson: ThemeJson, options: CreateThemeOptions = {}): Theme {
|
|
2082
|
-
const { mode, symbolPresetOverride, colorBlindMode } = options;
|
|
2083
|
-
const colorMode = mode ?? detectColorMode();
|
|
2084
|
-
const resolvedColors = resolveThemeColors(themeJson.colors, themeJson.vars);
|
|
2085
|
-
|
|
2086
|
-
if (colorBlindMode) {
|
|
2087
|
-
const added = resolvedColors.toolDiffAdded;
|
|
2088
|
-
if (typeof added === "string" && added.startsWith("#")) {
|
|
2089
|
-
resolvedColors.toolDiffAdded = adjustHsv(added, COLORBLIND_ADJUSTMENT);
|
|
2090
|
-
}
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
const fgColors: Record<ThemeColor, string | number> = {} as Record<ThemeColor, string | number>;
|
|
2094
|
-
const bgColors: Record<ThemeBg, string | number> = {} as Record<ThemeBg, string | number>;
|
|
2095
|
-
const bgColorKeys: Set<string> = new Set([
|
|
2096
|
-
"selectedBg",
|
|
2097
|
-
"userMessageBg",
|
|
2098
|
-
"customMessageBg",
|
|
2099
|
-
"toolPendingBg",
|
|
2100
|
-
"toolSuccessBg",
|
|
2101
|
-
"toolErrorBg",
|
|
2102
|
-
"statusLineBg",
|
|
2103
|
-
]);
|
|
2104
|
-
for (const [key, value] of Object.entries(resolvedColors)) {
|
|
2105
|
-
if (bgColorKeys.has(key)) {
|
|
2106
|
-
bgColors[key as ThemeBg] = value;
|
|
2107
|
-
} else {
|
|
2108
|
-
fgColors[key as ThemeColor] = value;
|
|
2109
|
-
}
|
|
2110
|
-
}
|
|
2111
|
-
// Extract symbol configuration - settings override takes precedence over theme
|
|
2112
|
-
const symbolPreset: SymbolPreset = symbolPresetOverride ?? themeJson.symbols?.preset ?? "unicode";
|
|
2113
|
-
const symbolOverrides = themeJson.symbols?.overrides ?? {};
|
|
2114
|
-
const spinnerFramesOverrides = normalizeSpinnerFramesOverride(themeJson.symbols?.spinnerFrames);
|
|
2115
|
-
return new Theme(fgColors, bgColors, colorMode, symbolPreset, symbolOverrides, spinnerFramesOverrides);
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
|
-
async function loadTheme(name: string, options: CreateThemeOptions = {}): Promise<Theme> {
|
|
2119
|
-
const themeJson = await loadThemeJson(name);
|
|
2120
|
-
return createTheme(themeJson, options);
|
|
2121
|
-
}
|
|
2122
|
-
|
|
2123
|
-
export async function getThemeByName(name: string): Promise<Theme | undefined> {
|
|
2124
|
-
try {
|
|
2125
|
-
return await loadTheme(name);
|
|
2126
|
-
} catch {
|
|
2127
|
-
return undefined;
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
13
|
+
export { getAvailableThemes, getAvailableThemesWithPaths, getThemeByName, type ThemeInfo } from "./loader";
|
|
14
|
+
export { isValidThemeColor, type ThemeBg, type ThemeColor } from "./schema";
|
|
15
|
+
export {
|
|
16
|
+
getAvailableSymbolPresets,
|
|
17
|
+
isValidSymbolPreset,
|
|
18
|
+
type SpinnerType,
|
|
19
|
+
type SymbolKey,
|
|
20
|
+
type SymbolPreset,
|
|
21
|
+
} from "./symbols";
|
|
22
|
+
export { Theme } from "./theme-class";
|
|
23
|
+
export {
|
|
24
|
+
getEditorTheme,
|
|
25
|
+
getMarkdownTheme,
|
|
26
|
+
getSelectListTheme,
|
|
27
|
+
getSettingsListTheme,
|
|
28
|
+
getSymbolTheme,
|
|
29
|
+
highlightCode,
|
|
30
|
+
setMarkdownMermaidRendering,
|
|
31
|
+
} from "./tui-adapters";
|
|
2130
32
|
|
|
2131
33
|
/** Appearance detected via OSC 11 background color query, or undefined if not yet available. */
|
|
2132
34
|
var terminalReportedAppearance: "dark" | "light" | undefined;
|
|
@@ -2423,20 +325,6 @@ function notifyThemeChange(event: ThemeChangeEvent = {}): void {
|
|
|
2423
325
|
onThemeChangeCallback?.(event);
|
|
2424
326
|
}
|
|
2425
327
|
|
|
2426
|
-
/**
|
|
2427
|
-
* Get available symbol presets.
|
|
2428
|
-
*/
|
|
2429
|
-
export function getAvailableSymbolPresets(): SymbolPreset[] {
|
|
2430
|
-
return ["unicode", "nerd", "ascii"];
|
|
2431
|
-
}
|
|
2432
|
-
|
|
2433
|
-
/**
|
|
2434
|
-
* Check if a string is a valid symbol preset.
|
|
2435
|
-
*/
|
|
2436
|
-
export function isValidSymbolPreset(preset: string): preset is SymbolPreset {
|
|
2437
|
-
return preset === "unicode" || preset === "nerd" || preset === "ascii";
|
|
2438
|
-
}
|
|
2439
|
-
|
|
2440
328
|
async function startThemeWatcher(): Promise<void> {
|
|
2441
329
|
stopThemeWatcher();
|
|
2442
330
|
|
|
@@ -2732,52 +620,6 @@ export function stopThemeWatcher(): void {
|
|
|
2732
620
|
// HTML Export Helpers
|
|
2733
621
|
// ============================================================================
|
|
2734
622
|
|
|
2735
|
-
/**
|
|
2736
|
-
* Convert a 256-color index to hex string.
|
|
2737
|
-
* Indices 0-15: basic colors (approximate)
|
|
2738
|
-
* Indices 16-231: 6x6x6 color cube
|
|
2739
|
-
* Indices 232-255: grayscale ramp
|
|
2740
|
-
*/
|
|
2741
|
-
function ansi256ToHex(index: number): string {
|
|
2742
|
-
// Basic colors (0-15) - approximate common terminal values
|
|
2743
|
-
const basicColors = [
|
|
2744
|
-
"#000000",
|
|
2745
|
-
"#800000",
|
|
2746
|
-
"#008000",
|
|
2747
|
-
"#808000",
|
|
2748
|
-
"#000080",
|
|
2749
|
-
"#800080",
|
|
2750
|
-
"#008080",
|
|
2751
|
-
"#c0c0c0",
|
|
2752
|
-
"#808080",
|
|
2753
|
-
"#ff0000",
|
|
2754
|
-
"#00ff00",
|
|
2755
|
-
"#ffff00",
|
|
2756
|
-
"#0000ff",
|
|
2757
|
-
"#ff00ff",
|
|
2758
|
-
"#00ffff",
|
|
2759
|
-
"#ffffff",
|
|
2760
|
-
];
|
|
2761
|
-
if (index < 16) {
|
|
2762
|
-
return basicColors[index];
|
|
2763
|
-
}
|
|
2764
|
-
|
|
2765
|
-
// Color cube (16-231): 6x6x6 = 216 colors
|
|
2766
|
-
if (index < 232) {
|
|
2767
|
-
const cubeIndex = index - 16;
|
|
2768
|
-
const r = Math.floor(cubeIndex / 36);
|
|
2769
|
-
const g = Math.floor((cubeIndex % 36) / 6);
|
|
2770
|
-
const b = cubeIndex % 6;
|
|
2771
|
-
const toHex = (n: number) => (n === 0 ? 0 : 55 + n * 40).toString(16).padStart(2, "0");
|
|
2772
|
-
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
2773
|
-
}
|
|
2774
|
-
|
|
2775
|
-
// Grayscale (232-255): 24 shades
|
|
2776
|
-
const gray = 8 + (index - 232) * 10;
|
|
2777
|
-
const grayHex = gray.toString(16).padStart(2, "0");
|
|
2778
|
-
return `#${grayHex}${grayHex}${grayHex}`;
|
|
2779
|
-
}
|
|
2780
|
-
|
|
2781
623
|
/**
|
|
2782
624
|
* Classify a parsed theme JSON as light/dark by the perceived luminance of its
|
|
2783
625
|
* status-line background. Mirrors {@link Theme.isLight} so the synchronous
|
|
@@ -2901,271 +743,3 @@ export async function getThemeExportColors(themeName?: string): Promise<{
|
|
|
2901
743
|
return {};
|
|
2902
744
|
}
|
|
2903
745
|
}
|
|
2904
|
-
|
|
2905
|
-
// ============================================================================
|
|
2906
|
-
// TUI Helpers
|
|
2907
|
-
// ============================================================================
|
|
2908
|
-
|
|
2909
|
-
let cachedHighlightColorsFor: Theme | undefined;
|
|
2910
|
-
let cachedHighlightColors: NativeHighlightColors | undefined;
|
|
2911
|
-
|
|
2912
|
-
function getHighlightColors(t: Theme): NativeHighlightColors {
|
|
2913
|
-
if (cachedHighlightColorsFor !== t || !cachedHighlightColors) {
|
|
2914
|
-
cachedHighlightColorsFor = t;
|
|
2915
|
-
cachedHighlightColors = {
|
|
2916
|
-
comment: t.getFgAnsi("syntaxComment"),
|
|
2917
|
-
keyword: t.getFgAnsi("syntaxKeyword"),
|
|
2918
|
-
function: t.getFgAnsi("syntaxFunction"),
|
|
2919
|
-
variable: t.getFgAnsi("syntaxVariable"),
|
|
2920
|
-
string: t.getFgAnsi("syntaxString"),
|
|
2921
|
-
number: t.getFgAnsi("syntaxNumber"),
|
|
2922
|
-
type: t.getFgAnsi("syntaxType"),
|
|
2923
|
-
operator: t.getFgAnsi("syntaxOperator"),
|
|
2924
|
-
punctuation: t.getFgAnsi("syntaxPunctuation"),
|
|
2925
|
-
inserted: t.getFgAnsi("toolDiffAdded"),
|
|
2926
|
-
deleted: t.getFgAnsi("toolDiffRemoved"),
|
|
2927
|
-
};
|
|
2928
|
-
}
|
|
2929
|
-
return cachedHighlightColors;
|
|
2930
|
-
}
|
|
2931
|
-
|
|
2932
|
-
/**
|
|
2933
|
-
* Memoized native syntax highlight. Returns the joined ANSI string, or `null`
|
|
2934
|
-
* when the native tokenizer throws so callers can apply their own fallback.
|
|
2935
|
-
*
|
|
2936
|
-
* Keyed on `(lang, code)` and reset whenever the active `theme` instance
|
|
2937
|
-
* changes — the ANSI colors are baked into the highlighted output, so a theme
|
|
2938
|
-
* switch (which always reassigns `theme`) must invalidate every entry.
|
|
2939
|
-
*
|
|
2940
|
-
* Why this exists: animated tool blocks (eval/bash) repaint their box on every
|
|
2941
|
-
* ~33ms border-shimmer frame, and markdown re-lexes on every streamed delta.
|
|
2942
|
-
* Without memoization each frame can re-tokenize an unchanged code body through
|
|
2943
|
-
* the Rust FFI — ~26ms for 100 lines, ~40ms for 150 — consuming or overrunning
|
|
2944
|
-
* the 33ms frame budget and starving the spinner/render timers (the "TUI freeze").
|
|
2945
|
-
*/
|
|
2946
|
-
const HIGHLIGHT_CACHE_MAX = 256;
|
|
2947
|
-
const highlightCache = new LRUCache<string, string>({ max: HIGHLIGHT_CACHE_MAX });
|
|
2948
|
-
let highlightCacheTheme: Theme | undefined;
|
|
2949
|
-
|
|
2950
|
-
function highlightCached(code: string, validLang: string | undefined, highlightTheme: Theme): string | null {
|
|
2951
|
-
if (highlightCacheTheme !== highlightTheme) {
|
|
2952
|
-
highlightCache.clear();
|
|
2953
|
-
highlightCacheTheme = highlightTheme;
|
|
2954
|
-
}
|
|
2955
|
-
const key = `${validLang ?? ""}\x00${code}`;
|
|
2956
|
-
const hit = highlightCache.get(key);
|
|
2957
|
-
if (hit !== undefined) {
|
|
2958
|
-
return hit;
|
|
2959
|
-
}
|
|
2960
|
-
let highlighted: string;
|
|
2961
|
-
try {
|
|
2962
|
-
highlighted = nativeHighlightCode(code, validLang, getHighlightColors(highlightTheme));
|
|
2963
|
-
} catch {
|
|
2964
|
-
return null;
|
|
2965
|
-
}
|
|
2966
|
-
highlightCache.set(key, highlighted);
|
|
2967
|
-
return highlighted;
|
|
2968
|
-
}
|
|
2969
|
-
|
|
2970
|
-
/**
|
|
2971
|
-
* Highlight code with syntax coloring based on file extension or language.
|
|
2972
|
-
* Returns array of highlighted lines.
|
|
2973
|
-
*/
|
|
2974
|
-
export function highlightCode(code: string, lang?: string, highlightTheme: Theme = theme): string[] {
|
|
2975
|
-
const validLang = lang && nativeSupportsLanguage(lang) ? lang : undefined;
|
|
2976
|
-
const highlighted = highlightCached(code, validLang, highlightTheme);
|
|
2977
|
-
// Always return a fresh array: callers (e.g. renderCodeCell) push extra lines
|
|
2978
|
-
// onto the result, which would corrupt the cached string otherwise.
|
|
2979
|
-
return (highlighted ?? code).split("\n");
|
|
2980
|
-
}
|
|
2981
|
-
|
|
2982
|
-
export function getSymbolTheme(): SymbolTheme {
|
|
2983
|
-
// Guard against `theme` being undefined (pre-init or cross-module-instance
|
|
2984
|
-
// plugin calls). Fall back to the ASCII preset so the returned symbols are
|
|
2985
|
-
// usable instead of crashing. See #2998.
|
|
2986
|
-
if (typeof theme === "undefined") {
|
|
2987
|
-
const box = {
|
|
2988
|
-
topLeft: "+",
|
|
2989
|
-
topRight: "+",
|
|
2990
|
-
bottomLeft: "+",
|
|
2991
|
-
bottomRight: "+",
|
|
2992
|
-
horizontal: "-",
|
|
2993
|
-
vertical: "|",
|
|
2994
|
-
cross: "+",
|
|
2995
|
-
teeDown: "+",
|
|
2996
|
-
teeUp: "+",
|
|
2997
|
-
teeLeft: "+",
|
|
2998
|
-
teeRight: "+",
|
|
2999
|
-
};
|
|
3000
|
-
return {
|
|
3001
|
-
cursor: ">",
|
|
3002
|
-
inputCursor: "|",
|
|
3003
|
-
boxRound: box,
|
|
3004
|
-
boxSharp: box,
|
|
3005
|
-
table: box,
|
|
3006
|
-
quoteBorder: "|",
|
|
3007
|
-
hrChar: "-",
|
|
3008
|
-
colorSwatch: "[]",
|
|
3009
|
-
spinnerFrames: ["-", "\\", "|", "/"],
|
|
3010
|
-
};
|
|
3011
|
-
}
|
|
3012
|
-
const preset = theme.getSymbolPreset();
|
|
3013
|
-
|
|
3014
|
-
return {
|
|
3015
|
-
cursor: theme.nav.cursor,
|
|
3016
|
-
inputCursor: preset === "ascii" ? "|" : "▏",
|
|
3017
|
-
boxRound: theme.boxRound,
|
|
3018
|
-
boxSharp: theme.boxSharp,
|
|
3019
|
-
table: theme.boxSharp,
|
|
3020
|
-
quoteBorder: theme.md.quoteBorder,
|
|
3021
|
-
hrChar: theme.md.hrChar,
|
|
3022
|
-
colorSwatch: theme.md.colorSwatch,
|
|
3023
|
-
spinnerFrames: theme.getSpinnerFrames("activity"),
|
|
3024
|
-
};
|
|
3025
|
-
}
|
|
3026
|
-
|
|
3027
|
-
let cachedMarkdownTheme: MarkdownTheme | undefined;
|
|
3028
|
-
let cachedMarkdownThemeRef: Theme | undefined;
|
|
3029
|
-
let markdownMermaidRendering = true;
|
|
3030
|
-
|
|
3031
|
-
export function setMarkdownMermaidRendering(enabled: boolean): void {
|
|
3032
|
-
if (markdownMermaidRendering === enabled) return;
|
|
3033
|
-
markdownMermaidRendering = enabled;
|
|
3034
|
-
cachedMarkdownTheme = undefined;
|
|
3035
|
-
}
|
|
3036
|
-
|
|
3037
|
-
export function getMarkdownTheme(): MarkdownTheme {
|
|
3038
|
-
if (cachedMarkdownTheme !== undefined && cachedMarkdownThemeRef === theme) {
|
|
3039
|
-
return cachedMarkdownTheme;
|
|
3040
|
-
}
|
|
3041
|
-
const mermaid = markdownMermaidRendering
|
|
3042
|
-
? (() => {
|
|
3043
|
-
// Mermaid ASCII diagrams render with the active palette so they read as
|
|
3044
|
-
// content rather than raw monochrome. Roles mirror the SVG renderer's
|
|
3045
|
-
// mapping; `text`/`muted`/`border`/`borderMuted`/`accent` exist in every theme.
|
|
3046
|
-
const mermaidColorMode =
|
|
3047
|
-
theme.getColorMode() === "truecolor" ? ("truecolor" as const) : ("ansi256" as const);
|
|
3048
|
-
const mermaidTheme = {
|
|
3049
|
-
fg: theme.getColorHex("text"),
|
|
3050
|
-
border: theme.getColorHex("border"),
|
|
3051
|
-
line: theme.getColorHex("muted"),
|
|
3052
|
-
arrow: theme.getColorHex("accent"),
|
|
3053
|
-
corner: theme.getColorHex("muted"),
|
|
3054
|
-
junction: theme.getColorHex("borderMuted"),
|
|
3055
|
-
};
|
|
3056
|
-
return { mermaidColorMode, mermaidTheme };
|
|
3057
|
-
})()
|
|
3058
|
-
: undefined;
|
|
3059
|
-
const markdownTheme: MarkdownTheme = {
|
|
3060
|
-
heading: (text: string) => theme.fg("mdHeading", text),
|
|
3061
|
-
link: (text: string) => theme.fg("mdLink", text),
|
|
3062
|
-
linkUrl: (text: string) => theme.fg("mdLinkUrl", text),
|
|
3063
|
-
code: (text: string) => theme.fg("mdCode", text),
|
|
3064
|
-
codeBlock: (text: string) => theme.fg("mdCodeBlock", text),
|
|
3065
|
-
codeBlockBorder: (text: string) => theme.fg("mdCodeBlockBorder", text),
|
|
3066
|
-
quote: (text: string) => theme.fg("mdQuote", text),
|
|
3067
|
-
quoteBorder: (text: string) => theme.fg("mdQuoteBorder", text),
|
|
3068
|
-
hr: (text: string) => theme.fg("mdHr", text),
|
|
3069
|
-
listBullet: (text: string) => theme.fg("mdListBullet", text),
|
|
3070
|
-
bold: (text: string) => theme.bold(text),
|
|
3071
|
-
italic: (text: string) => theme.italic(text),
|
|
3072
|
-
underline: (text: string) => theme.underline(text),
|
|
3073
|
-
strikethrough: (text: string) => chalk.strikethrough(text),
|
|
3074
|
-
symbols: getSymbolTheme(),
|
|
3075
|
-
resolveMermaidAscii: mermaid
|
|
3076
|
-
? (source, maxWidth) =>
|
|
3077
|
-
resolveMermaidAscii(source, {
|
|
3078
|
-
maxWidth,
|
|
3079
|
-
theme: mermaid.mermaidTheme,
|
|
3080
|
-
colorMode: mermaid.mermaidColorMode,
|
|
3081
|
-
})
|
|
3082
|
-
: undefined,
|
|
3083
|
-
highlightCode: (code: string, lang?: string): string[] => {
|
|
3084
|
-
const validLang = lang && nativeSupportsLanguage(lang) ? lang : undefined;
|
|
3085
|
-
const highlighted = highlightCached(code, validLang, theme);
|
|
3086
|
-
if (highlighted !== null) return highlighted.split("\n");
|
|
3087
|
-
return code.split("\n").map(line => theme.fg("mdCodeBlock", line));
|
|
3088
|
-
},
|
|
3089
|
-
};
|
|
3090
|
-
cachedMarkdownTheme = markdownTheme;
|
|
3091
|
-
cachedMarkdownThemeRef = theme;
|
|
3092
|
-
return markdownTheme;
|
|
3093
|
-
}
|
|
3094
|
-
|
|
3095
|
-
export function getSelectListTheme(): SelectListTheme {
|
|
3096
|
-
// Guard against `theme` being undefined (pre-init or cross-module-instance
|
|
3097
|
-
// plugin calls). See #2998.
|
|
3098
|
-
if (typeof theme === "undefined") {
|
|
3099
|
-
return {
|
|
3100
|
-
selectedPrefix: (text: string) => text,
|
|
3101
|
-
selectedText: (text: string) => text,
|
|
3102
|
-
description: (text: string) => text,
|
|
3103
|
-
scrollInfo: (text: string) => text,
|
|
3104
|
-
noMatch: (text: string) => text,
|
|
3105
|
-
symbols: getSymbolTheme(),
|
|
3106
|
-
hovered: (text: string) => text,
|
|
3107
|
-
};
|
|
3108
|
-
}
|
|
3109
|
-
return {
|
|
3110
|
-
selectedPrefix: (text: string) => theme.fg("accent", text),
|
|
3111
|
-
selectedText: (text: string) => theme.fg("accent", text),
|
|
3112
|
-
description: (text: string) => theme.fg("muted", text),
|
|
3113
|
-
scrollInfo: (text: string) => theme.fg("muted", text),
|
|
3114
|
-
noMatch: (text: string) => theme.fg("muted", text),
|
|
3115
|
-
symbols: getSymbolTheme(),
|
|
3116
|
-
hovered: (text: string) => theme.bg("selectedBg", text),
|
|
3117
|
-
};
|
|
3118
|
-
}
|
|
3119
|
-
|
|
3120
|
-
export function getEditorTheme(): EditorTheme {
|
|
3121
|
-
// Guard against `theme` being undefined (pre-init or cross-module-instance
|
|
3122
|
-
// plugin calls). See #2998.
|
|
3123
|
-
if (typeof theme === "undefined") {
|
|
3124
|
-
return {
|
|
3125
|
-
borderColor: (text: string) => text,
|
|
3126
|
-
selectList: getSelectListTheme(),
|
|
3127
|
-
symbols: getSymbolTheme(),
|
|
3128
|
-
hintStyle: (text: string) => text,
|
|
3129
|
-
};
|
|
3130
|
-
}
|
|
3131
|
-
return {
|
|
3132
|
-
borderColor: (text: string) => theme.fg("borderMuted", text),
|
|
3133
|
-
selectList: getSelectListTheme(),
|
|
3134
|
-
symbols: getSymbolTheme(),
|
|
3135
|
-
hintStyle: (text: string) => theme.fg("dim", text),
|
|
3136
|
-
};
|
|
3137
|
-
}
|
|
3138
|
-
|
|
3139
|
-
export function getSettingsListTheme(): SettingsListTheme {
|
|
3140
|
-
// Plugins (e.g. pi-rtk-optimizer) may call this before `initTheme()` assigns
|
|
3141
|
-
// the global `theme`, or from a separate module instance under npm-global
|
|
3142
|
-
// installs where the live binding was never initialized. Fall back to plain
|
|
3143
|
-
// text so the call returns a usable (unstyled) theme instead of crashing with
|
|
3144
|
-
// "undefined is not an object (evaluating 'theme.fg')". See #2998.
|
|
3145
|
-
if (typeof theme === "undefined") {
|
|
3146
|
-
return {
|
|
3147
|
-
label: (text: string) => text,
|
|
3148
|
-
value: (text: string) => text,
|
|
3149
|
-
description: (text: string) => text,
|
|
3150
|
-
cursor: "> ",
|
|
3151
|
-
hint: (text: string) => text,
|
|
3152
|
-
heading: (text: string) => text,
|
|
3153
|
-
section: (text: string) => text,
|
|
3154
|
-
hovered: (text: string) => text,
|
|
3155
|
-
};
|
|
3156
|
-
}
|
|
3157
|
-
return {
|
|
3158
|
-
label: (text: string, selected: boolean, changed: boolean) =>
|
|
3159
|
-
changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : text,
|
|
3160
|
-
value: (text: string, selected: boolean, changed: boolean) =>
|
|
3161
|
-
changed ? theme.fg("statusLineGitDirty", text) : selected ? theme.fg("accent", text) : theme.fg("muted", text),
|
|
3162
|
-
description: (text: string) => theme.fg("dim", text),
|
|
3163
|
-
cursor: theme.fg("accent", `${theme.nav.cursor} `),
|
|
3164
|
-
hint: (text: string) => theme.fg("dim", text),
|
|
3165
|
-
heading: (text: string, dimmed: boolean) =>
|
|
3166
|
-
dimmed ? theme.fg("dim", theme.underline(text)) : theme.fg("muted", theme.bold(theme.underline(text))),
|
|
3167
|
-
section: (text: string, active: boolean) =>
|
|
3168
|
-
active ? theme.fg("accent", theme.bold(text)) : theme.fg("muted", text),
|
|
3169
|
-
hovered: (text: string) => theme.bg("selectedBg", text),
|
|
3170
|
-
};
|
|
3171
|
-
}
|