@agi-fans/dsh-tui 0.20.0 → 0.22.0
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/lib/CHANGELOG.md +249 -3
- package/lib/chrome/box.d.ts +47 -16
- package/lib/chrome/box.js +172 -130
- package/lib/chrome/box.js.map +1 -1
- package/lib/chrome/code-highlight.d.ts +15 -0
- package/lib/chrome/code-highlight.js +60 -4
- package/lib/chrome/code-highlight.js.map +1 -1
- package/lib/chrome/command-output.d.ts +29 -2
- package/lib/chrome/command-output.js +100 -4
- package/lib/chrome/command-output.js.map +1 -1
- package/lib/chrome/main-screen-renderer.d.ts +18 -0
- package/lib/chrome/main-screen-renderer.js +58 -5
- package/lib/chrome/main-screen-renderer.js.map +1 -1
- package/lib/chrome/markdown.js +30 -10
- package/lib/chrome/markdown.js.map +1 -1
- package/lib/chrome/relative-time.d.ts +17 -0
- package/lib/chrome/relative-time.js +36 -0
- package/lib/chrome/relative-time.js.map +1 -1
- package/lib/chrome/renderer.d.ts +9 -0
- package/lib/chrome/renderer.js +1 -1
- package/lib/chrome/renderer.js.map +1 -1
- package/lib/chrome/secret-mask.d.ts +35 -0
- package/lib/chrome/secret-mask.js +180 -0
- package/lib/chrome/secret-mask.js.map +1 -0
- package/lib/chrome/status-config.d.ts +161 -51
- package/lib/chrome/status-config.js +237 -126
- package/lib/chrome/status-config.js.map +1 -1
- package/lib/chrome/status-line.d.ts +3 -2
- package/lib/chrome/status-line.js +105 -63
- package/lib/chrome/status-line.js.map +1 -1
- package/lib/chrome/terminal-colors.d.ts +85 -0
- package/lib/chrome/terminal-colors.js +230 -0
- package/lib/chrome/terminal-colors.js.map +1 -0
- package/lib/chrome/terminal-images.d.ts +9 -1
- package/lib/chrome/terminal-images.js +27 -4
- package/lib/chrome/terminal-images.js.map +1 -1
- package/lib/chrome/theme.d.ts +32 -9
- package/lib/chrome/theme.js +616 -520
- package/lib/chrome/theme.js.map +1 -1
- package/lib/chrome/tool-args.d.ts +1 -1
- package/lib/chrome/tool-args.js +3 -1
- package/lib/chrome/tool-args.js.map +1 -1
- package/lib/chrome/tool-renderers.d.ts +2 -0
- package/lib/chrome/tool-renderers.js +69 -3
- package/lib/chrome/tool-renderers.js.map +1 -1
- package/lib/chrome/tools-list.d.ts +1 -4
- package/lib/chrome/tools-list.js +1 -41
- package/lib/chrome/tools-list.js.map +1 -1
- package/lib/chrome/welcome-tips.d.ts +20 -7
- package/lib/chrome/welcome-tips.js +22 -41
- package/lib/chrome/welcome-tips.js.map +1 -1
- package/lib/commands/auth-ids.d.ts +25 -0
- package/lib/commands/auth-ids.js +27 -0
- package/lib/commands/auth-ids.js.map +1 -0
- package/lib/commands/auth-input.d.ts +34 -0
- package/lib/commands/auth-input.js +96 -0
- package/lib/commands/auth-input.js.map +1 -0
- package/lib/commands/auth-providers.d.ts +39 -0
- package/lib/commands/auth-providers.js +125 -0
- package/lib/commands/auth-providers.js.map +1 -0
- package/lib/commands/auth.d.ts +16 -22
- package/lib/commands/auth.js +346 -299
- package/lib/commands/auth.js.map +1 -1
- package/lib/commands/btw.d.ts +27 -0
- package/lib/commands/btw.js +59 -0
- package/lib/commands/btw.js.map +1 -0
- package/lib/commands/diff.js +30 -5
- package/lib/commands/diff.js.map +1 -1
- package/lib/commands/export.d.ts +1 -1
- package/lib/commands/export.js +5 -3
- package/lib/commands/export.js.map +1 -1
- package/lib/commands/files.d.ts +17 -1
- package/lib/commands/files.js +69 -61
- package/lib/commands/files.js.map +1 -1
- package/lib/commands/integrations.js +18 -6
- package/lib/commands/integrations.js.map +1 -1
- package/lib/commands/jobs.d.ts +19 -4
- package/lib/commands/jobs.js +50 -7
- package/lib/commands/jobs.js.map +1 -1
- package/lib/commands/model.js +109 -76
- package/lib/commands/model.js.map +1 -1
- package/lib/commands/permission.js +46 -13
- package/lib/commands/permission.js.map +1 -1
- package/lib/commands/plugins.js +196 -42
- package/lib/commands/plugins.js.map +1 -1
- package/lib/commands/schedule.d.ts +29 -0
- package/lib/commands/schedule.js +228 -0
- package/lib/commands/schedule.js.map +1 -0
- package/lib/commands/session-configuration.d.ts +1 -1
- package/lib/commands/session-configuration.js +29 -28
- package/lib/commands/session-configuration.js.map +1 -1
- package/lib/commands/session.js +75 -80
- package/lib/commands/session.js.map +1 -1
- package/lib/commands/terminal.d.ts +24 -4
- package/lib/commands/terminal.js +38 -94
- package/lib/commands/terminal.js.map +1 -1
- package/lib/commands/trajectory.js +4 -1
- package/lib/commands/trajectory.js.map +1 -1
- package/lib/definition.d.ts +150 -11
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/index.js.map +1 -1
- package/lib/input/actions.d.ts +51 -0
- package/lib/input/actions.js +120 -0
- package/lib/input/actions.js.map +1 -0
- package/lib/input/editor-discovery.js +2 -2
- package/lib/input/editor-discovery.js.map +1 -1
- package/lib/input/editor.d.ts +0 -10
- package/lib/input/editor.js +11 -9
- package/lib/input/editor.js.map +1 -1
- package/lib/input/image-paste.d.ts +19 -1
- package/lib/input/image-paste.js +82 -1
- package/lib/input/image-paste.js.map +1 -1
- package/lib/input/keybindings-config.d.ts +15 -4
- package/lib/input/keybindings-config.js +57 -35
- package/lib/input/keybindings-config.js.map +1 -1
- package/lib/input/keys.d.ts +18 -0
- package/lib/input/keys.js +48 -3
- package/lib/input/keys.js.map +1 -1
- package/lib/input/wheel-scroll.d.ts +46 -0
- package/lib/input/wheel-scroll.js +69 -0
- package/lib/input/wheel-scroll.js.map +1 -0
- package/lib/prompt-intents.d.ts +182 -0
- package/lib/prompt-intents.js +127 -0
- package/lib/prompt-intents.js.map +1 -0
- package/lib/runtime/agent-profile.d.ts +5 -0
- package/lib/runtime/agent-profile.js +12 -0
- package/lib/runtime/agent-profile.js.map +1 -1
- package/lib/runtime/approval-rules.d.ts +59 -0
- package/lib/runtime/approval-rules.js +179 -0
- package/lib/runtime/approval-rules.js.map +1 -0
- package/lib/runtime/btw-policy.d.ts +7 -0
- package/lib/runtime/btw-policy.js +18 -0
- package/lib/runtime/btw-policy.js.map +1 -0
- package/lib/runtime/btw.d.ts +47 -0
- package/lib/runtime/btw.js +200 -0
- package/lib/runtime/btw.js.map +1 -0
- package/lib/runtime/cancellable.d.ts +75 -0
- package/lib/runtime/cancellable.js +76 -0
- package/lib/runtime/cancellable.js.map +1 -0
- package/lib/runtime/composer-images.js +5 -4
- package/lib/runtime/composer-images.js.map +1 -1
- package/lib/runtime/composer-shell.d.ts +30 -0
- package/lib/runtime/composer-shell.js +150 -0
- package/lib/runtime/composer-shell.js.map +1 -0
- package/lib/runtime/file-recovery-local.d.ts +73 -0
- package/lib/runtime/file-recovery-local.js +351 -0
- package/lib/runtime/file-recovery-local.js.map +1 -0
- package/lib/runtime/file-recovery-recording.d.ts +23 -0
- package/lib/runtime/file-recovery-recording.js +131 -0
- package/lib/runtime/file-recovery-recording.js.map +1 -0
- package/lib/runtime/file-recovery-review.d.ts +3 -0
- package/lib/runtime/file-recovery-review.js +130 -0
- package/lib/runtime/file-recovery-review.js.map +1 -0
- package/lib/runtime/file-recovery-store.d.ts +83 -0
- package/lib/runtime/file-recovery-store.js +329 -0
- package/lib/runtime/file-recovery-store.js.map +1 -0
- package/lib/runtime/file-recovery-worker.d.ts +22 -0
- package/lib/runtime/file-recovery-worker.js +153 -0
- package/lib/runtime/file-recovery-worker.js.map +1 -0
- package/lib/runtime/file-review.d.ts +6 -3
- package/lib/runtime/file-review.js +44 -7
- package/lib/runtime/file-review.js.map +1 -1
- package/lib/runtime/herdr-agent.d.ts +4 -13
- package/lib/runtime/herdr-agent.js +19 -63
- package/lib/runtime/herdr-agent.js.map +1 -1
- package/lib/runtime/plain-tui.d.ts +2 -0
- package/lib/runtime/plain-tui.js +7 -1
- package/lib/runtime/plain-tui.js.map +1 -1
- package/lib/runtime/plugin-settings.d.ts +25 -2
- package/lib/runtime/plugin-settings.js +329 -31
- package/lib/runtime/plugin-settings.js.map +1 -1
- package/lib/runtime/provider-local.d.ts +81 -24
- package/lib/runtime/provider-local.js +1807 -608
- package/lib/runtime/provider-local.js.map +1 -1
- package/lib/runtime/runner.js +52 -12
- package/lib/runtime/runner.js.map +1 -1
- package/lib/runtime/session-archive.d.ts +6 -0
- package/lib/runtime/session-archive.js +129 -3
- package/lib/runtime/session-archive.js.map +1 -1
- package/lib/runtime/session-runtime.js +1 -1
- package/lib/runtime/session-runtime.js.map +1 -1
- package/lib/runtime/settings-preferences.d.ts +5 -0
- package/lib/runtime/settings-preferences.js +34 -0
- package/lib/runtime/settings-preferences.js.map +1 -0
- package/lib/runtime/startup-notices.js +2 -0
- package/lib/runtime/startup-notices.js.map +1 -1
- package/lib/runtime/terminal-modes.d.ts +47 -0
- package/lib/runtime/terminal-modes.js +86 -0
- package/lib/runtime/terminal-modes.js.map +1 -0
- package/lib/runtime/terminal-notifications.d.ts +3 -5
- package/lib/runtime/terminal-notifications.js +4 -8
- package/lib/runtime/terminal-notifications.js.map +1 -1
- package/lib/runtime/terminal-probe.d.ts +36 -0
- package/lib/runtime/terminal-probe.js +37 -0
- package/lib/runtime/terminal-probe.js.map +1 -0
- package/lib/runtime/terminal-session.d.ts +57 -0
- package/lib/runtime/terminal-session.js +164 -0
- package/lib/runtime/terminal-session.js.map +1 -0
- package/lib/runtime/terminal-state.d.ts +29 -0
- package/lib/runtime/terminal-state.js +62 -0
- package/lib/runtime/terminal-state.js.map +1 -0
- package/lib/runtime/tool-presentation.js +50 -14
- package/lib/runtime/tool-presentation.js.map +1 -1
- package/lib/session/external-agents.d.ts +5 -0
- package/lib/session/external-agents.js +27 -0
- package/lib/session/external-agents.js.map +1 -0
- package/lib/session/feature-toggles.d.ts +1 -1
- package/lib/session/feature-toggles.js +1 -1
- package/lib/session/interaction-adapter.d.ts +1 -6
- package/lib/session/interaction-adapter.js +95 -18
- package/lib/session/interaction-adapter.js.map +1 -1
- package/lib/session/message-queue.js +1 -1
- package/lib/session/message-queue.js.map +1 -1
- package/lib/session/model-readiness.d.ts +16 -0
- package/lib/session/model-readiness.js +43 -0
- package/lib/session/model-readiness.js.map +1 -0
- package/lib/session/package-metadata.d.ts +8 -0
- package/lib/session/package-metadata.js +8 -0
- package/lib/session/package-metadata.js.map +1 -1
- package/lib/session/project-context.d.ts +5 -0
- package/lib/session/project-context.js +36 -0
- package/lib/session/project-context.js.map +1 -1
- package/lib/session/session-controller.d.ts +10 -13
- package/lib/session/session-controller.js +183 -98
- package/lib/session/session-controller.js.map +1 -1
- package/lib/session/session-library.js +2 -0
- package/lib/session/session-library.js.map +1 -1
- package/lib/session/session-tree.d.ts +3 -0
- package/lib/session/session-tree.js +3 -1
- package/lib/session/session-tree.js.map +1 -1
- package/lib/session/subagent-roster.d.ts +2 -1
- package/lib/session/subagent-roster.js +24 -3
- package/lib/session/subagent-roster.js.map +1 -1
- package/lib/session/subagent-tracker.d.ts +6 -2
- package/lib/session/subagent-tracker.js +49 -2
- package/lib/session/subagent-tracker.js.map +1 -1
- package/lib/session/tui-settings.d.ts +47 -13
- package/lib/session/tui-settings.js +76 -34
- package/lib/session/tui-settings.js.map +1 -1
- package/lib/session/working-directory.d.ts +4 -0
- package/lib/session/working-directory.js +4 -0
- package/lib/session/working-directory.js.map +1 -0
- package/lib/session/worktrees.d.ts +13 -0
- package/lib/session/worktrees.js +49 -0
- package/lib/session/worktrees.js.map +1 -0
- package/lib/views/approval-rule.d.ts +3 -0
- package/lib/views/approval-rule.js +4 -0
- package/lib/views/approval-rule.js.map +1 -0
- package/lib/views/at-complete.d.ts +8 -0
- package/lib/views/at-complete.js +64 -2
- package/lib/views/at-complete.js.map +1 -1
- package/lib/views/autocomplete.d.ts +22 -2
- package/lib/views/autocomplete.js +70 -40
- package/lib/views/autocomplete.js.map +1 -1
- package/lib/views/copy-targets.d.ts +13 -14
- package/lib/views/copy-targets.js +42 -54
- package/lib/views/copy-targets.js.map +1 -1
- package/lib/views/document-reader.js +18 -5
- package/lib/views/document-reader.js.map +1 -1
- package/lib/views/event-views.d.ts +1 -1
- package/lib/views/event-views.js +60 -6
- package/lib/views/event-views.js.map +1 -1
- package/lib/views/file-actions.d.ts +12 -0
- package/lib/views/file-actions.js +72 -0
- package/lib/views/file-actions.js.map +1 -0
- package/lib/views/history-search.js +15 -21
- package/lib/views/history-search.js.map +1 -1
- package/lib/views/hotkeys.d.ts +8 -1
- package/lib/views/hotkeys.js +53 -26
- package/lib/views/hotkeys.js.map +1 -1
- package/lib/views/list-navigation.d.ts +62 -0
- package/lib/views/list-navigation.js +73 -0
- package/lib/views/list-navigation.js.map +1 -0
- package/lib/views/list-view.d.ts +99 -0
- package/lib/views/list-view.js +165 -0
- package/lib/views/list-view.js.map +1 -0
- package/lib/views/prompt-selector.d.ts +34 -4
- package/lib/views/prompt-selector.js +270 -134
- package/lib/views/prompt-selector.js.map +1 -1
- package/lib/views/session-files.d.ts +1 -2
- package/lib/views/session-files.js +1 -11
- package/lib/views/session-files.js.map +1 -1
- package/lib/views/settings-list.d.ts +51 -10
- package/lib/views/settings-list.js +267 -315
- package/lib/views/settings-list.js.map +1 -1
- package/lib/views/trajectory-timeline.d.ts +72 -0
- package/lib/views/trajectory-timeline.js +133 -0
- package/lib/views/trajectory-timeline.js.map +1 -0
- package/lib/views/trajectory.d.ts +15 -4
- package/lib/views/trajectory.js +557 -123
- package/lib/views/trajectory.js.map +1 -1
- package/lib/views/transcript-export.js +9 -0
- package/lib/views/transcript-export.js.map +1 -1
- package/lib/views/transcript-render.d.ts +97 -25
- package/lib/views/transcript-render.js +542 -170
- package/lib/views/transcript-render.js.map +1 -1
- package/lib/views/transcript-search.d.ts +39 -6
- package/lib/views/transcript-search.js +46 -11
- package/lib/views/transcript-search.js.map +1 -1
- package/lib/views/transcript-types.d.ts +63 -5
- package/lib/views/transcript-types.js +23 -11
- package/lib/views/transcript-types.js.map +1 -1
- package/package.json +95 -65
- package/lib/commands/loop.d.ts +0 -52
- package/lib/commands/loop.js +0 -307
- package/lib/commands/loop.js.map +0 -1
- package/lib/commands/steer.d.ts +0 -5
- package/lib/commands/steer.js +0 -24
- package/lib/commands/steer.js.map +0 -1
- package/lib/runtime/terminal-reader.d.ts +0 -11
- package/lib/runtime/terminal-reader.js +0 -87
- package/lib/runtime/terminal-reader.js.map +0 -1
- package/lib/views/agent-hub.d.ts +0 -32
- package/lib/views/agent-hub.js +0 -206
- package/lib/views/agent-hub.js.map +0 -1
- package/lib/views/copy-selector.d.ts +0 -51
- package/lib/views/copy-selector.js +0 -139
- package/lib/views/copy-selector.js.map +0 -1
package/lib/chrome/theme.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* glyphs used by terminal views.
|
|
4
4
|
* @module @agi-fans/dsh-tui
|
|
5
5
|
*/
|
|
6
|
+
import { contrastRatio, labDistance, nearestPaletteIndex, nearestXterm256, nearestXterm256Grey, relativeLuminance, toOklab, terminalColorsKey, } from "./terminal-colors.js";
|
|
6
7
|
/** Rounded-box drawing characters (OMP unicode preset). */
|
|
7
8
|
export const BOX = {
|
|
8
9
|
topLeft: '╭',
|
|
@@ -51,394 +52,121 @@ export const SYMBOL = {
|
|
|
51
52
|
export const SPINNER = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
52
53
|
/** Dark palette with paired card ink and backgrounds. */
|
|
53
54
|
const DARK_PALETTE = {
|
|
54
|
-
accent: '#
|
|
55
|
-
border: '#
|
|
56
|
-
borderAccent: '#
|
|
55
|
+
accent: '#7c93ff',
|
|
56
|
+
border: '#3556b8',
|
|
57
|
+
borderAccent: '#4d6bfe',
|
|
57
58
|
borderMuted: '#3d424a',
|
|
59
|
+
// Every content colour sits near 67% lightness, which is what lets one ink
|
|
60
|
+
// stay readable across all three card fills. A 55% red reached 4.5:1 on a
|
|
61
|
+
// near-black card and only 3.4:1 once that card became a real grey.
|
|
58
62
|
success: '#89d281',
|
|
59
|
-
error: '#
|
|
63
|
+
error: '#ff8a8a',
|
|
60
64
|
warning: '#e4c00f',
|
|
61
|
-
muted: '#
|
|
62
|
-
dim: '#
|
|
65
|
+
muted: '#a3a8b2',
|
|
66
|
+
dim: '#858c99',
|
|
63
67
|
text: '',
|
|
64
|
-
userMessageText: '#
|
|
65
|
-
userMessageBg: '#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
68
|
+
userMessageText: '#e4e9f7',
|
|
69
|
+
userMessageBg: '#141b30',
|
|
70
|
+
// The three states differ by hue and saturation at one lightness, the way pi's
|
|
71
|
+
// do. Three near-black fills a few units apart could only ever read as "dark";
|
|
72
|
+
// at 24% lightness a neutral pending, a green success, and a red failure name
|
|
73
|
+
// themselves without any status glyph.
|
|
74
|
+
toolPendingBg: '#34383a',
|
|
75
|
+
toolSuccessBg: '#254131',
|
|
76
|
+
toolAltBg: '#2b2f31',
|
|
77
|
+
toolErrorBg: '#5b282a',
|
|
69
78
|
toolTitle: '#e0e0e0',
|
|
70
|
-
toolOutput: '#
|
|
79
|
+
toolOutput: '#a3a8b2',
|
|
71
80
|
toolDiffAdded: '#89d281',
|
|
72
|
-
toolDiffRemoved: '#
|
|
73
|
-
toolDiffContext: '#
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
81
|
+
toolDiffRemoved: '#ff8a8a',
|
|
82
|
+
toolDiffContext: '#a8aeb8',
|
|
83
|
+
// Content inks share one lightness and differ only in hue, the way pi's dark
|
|
84
|
+
// palette does. Two rules follow from that and neither held before:
|
|
85
|
+
//
|
|
86
|
+
// - A hue names a role, so no single hue may carry several. `mdHeading`,
|
|
87
|
+
// `mdLink` and `mdListBullet` were all the accent blue, which made every
|
|
88
|
+
// document look like one long link.
|
|
89
|
+
// - Nothing that is prose may be achromatic. Inline code was `#8a9099`
|
|
90
|
+
// (chroma 0.015) and a prose-shaped codespan was `muted` — two greys the
|
|
91
|
+
// reader cannot tell apart, for the one role that has to read as "this is
|
|
92
|
+
// code".
|
|
93
|
+
//
|
|
94
|
+
// The syntax set used to be VSCode Dark+ values, which are drawn for a #1e1e1e
|
|
95
|
+
// editor and span 29 points of lightness here. These sit at 78% with chroma
|
|
96
|
+
// under 0.12, so a highlighted block varies in hue and not in weight.
|
|
97
|
+
mdHeading: '#ceb75b',
|
|
98
|
+
mdLink: '#83bdf8',
|
|
99
|
+
mdLinkUrl: '#7e95ad',
|
|
100
|
+
mdCode: '#d3a1ea',
|
|
101
|
+
mdCodeBlock: '#82cb9b',
|
|
79
102
|
mdCodeBlockBorder: '#3d424a',
|
|
80
|
-
mdKeyword: '#
|
|
81
|
-
syntaxString: '#
|
|
82
|
-
syntaxNumber: '#
|
|
83
|
-
syntaxFunction: '#
|
|
84
|
-
syntaxType: '#
|
|
103
|
+
mdKeyword: '#83bdf8',
|
|
104
|
+
syntaxString: '#e7a875',
|
|
105
|
+
syntaxNumber: '#82cb9b',
|
|
106
|
+
syntaxFunction: '#cdb760',
|
|
107
|
+
syntaxType: '#d1a3e6',
|
|
85
108
|
mdQuote: '#777d88',
|
|
86
|
-
|
|
109
|
+
mdQuoteBorder: '#3d424a',
|
|
110
|
+
mdHr: '#3d424a',
|
|
111
|
+
mdListBullet: '#a595c4',
|
|
112
|
+
// The composer's border while a `!` line is being typed. Green because that
|
|
113
|
+
// is the one signal that says "this leaves the model and runs on your machine".
|
|
114
|
+
shellMode: '#89d281',
|
|
87
115
|
thinkingText: '#6b7280',
|
|
88
116
|
customMessageLabel: '#b281d6',
|
|
89
117
|
};
|
|
90
118
|
/** Light palette with paired card ink and backgrounds. */
|
|
91
119
|
const LIGHT_PALETTE = {
|
|
92
|
-
accent: '#
|
|
93
|
-
border: '#
|
|
94
|
-
borderAccent: '#
|
|
120
|
+
accent: '#3b57e0',
|
|
121
|
+
border: '#8aa0dd',
|
|
122
|
+
borderAccent: '#4d6bfe',
|
|
95
123
|
borderMuted: '#b0b0b0',
|
|
96
124
|
success: '#588458',
|
|
97
|
-
error: '#
|
|
125
|
+
error: '#8f3a3a',
|
|
98
126
|
warning: '#9a7326',
|
|
99
127
|
muted: '#666666',
|
|
100
128
|
dim: '#767676',
|
|
101
129
|
text: '',
|
|
102
|
-
userMessageText: '#
|
|
103
|
-
userMessageBg: '#
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
130
|
+
userMessageText: '#1c2438',
|
|
131
|
+
userMessageBg: '#e7ecf9',
|
|
132
|
+
// The same three states on paper: one lightness, three hues.
|
|
133
|
+
toolPendingBg: '#e7e8ea',
|
|
134
|
+
toolSuccessBg: '#dee9e1',
|
|
135
|
+
toolAltBg: '#eff0f2',
|
|
136
|
+
toolErrorBg: '#eee2e1',
|
|
107
137
|
toolTitle: '#242424',
|
|
108
138
|
toolOutput: '#666666',
|
|
109
139
|
toolDiffAdded: '#4a6f4a',
|
|
110
|
-
toolDiffRemoved: '#
|
|
140
|
+
toolDiffRemoved: '#93403f',
|
|
111
141
|
toolDiffContext: '#666666',
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
142
|
+
// The same hue-per-role map as the dark palette, darkened for paper. Both
|
|
143
|
+
// palettes have to name a role with a hue, or switching themes would teach a
|
|
144
|
+
// reader a second visual language.
|
|
145
|
+
mdHeading: '#6d5900',
|
|
146
|
+
mdLink: '#1c62b3',
|
|
147
|
+
mdLinkUrl: '#465f7f',
|
|
148
|
+
mdCode: '#804797',
|
|
149
|
+
mdCodeBlock: '#247448',
|
|
117
150
|
mdCodeBlockBorder: '#6c6c6c',
|
|
118
|
-
mdKeyword: '#
|
|
119
|
-
syntaxString: '#
|
|
120
|
-
syntaxNumber: '#
|
|
121
|
-
syntaxFunction: '#
|
|
122
|
-
syntaxType: '#
|
|
151
|
+
mdKeyword: '#1d5da7',
|
|
152
|
+
syntaxString: '#a24e10',
|
|
153
|
+
syntaxNumber: '#247448',
|
|
154
|
+
syntaxFunction: '#6d5900',
|
|
155
|
+
syntaxType: '#7e4a93',
|
|
123
156
|
mdQuote: '#6c6c6c',
|
|
124
|
-
|
|
157
|
+
mdQuoteBorder: '#b8b8b8',
|
|
158
|
+
mdHr: '#b8b8b8',
|
|
159
|
+
mdListBullet: '#66508b',
|
|
160
|
+
shellMode: '#2f8f4e',
|
|
125
161
|
thinkingText: '#6c6c6c',
|
|
126
162
|
customMessageLabel: '#7e57c2',
|
|
127
163
|
};
|
|
128
|
-
const MIDNIGHT_PALETTE = {
|
|
129
|
-
...DARK_PALETTE,
|
|
130
|
-
accent: '#7aa2f7',
|
|
131
|
-
border: '#3d59a1',
|
|
132
|
-
borderAccent: '#7dcfff',
|
|
133
|
-
borderMuted: '#3d424a',
|
|
134
|
-
success: '#9ece6a',
|
|
135
|
-
error: '#f7768e',
|
|
136
|
-
warning: '#e0af68',
|
|
137
|
-
muted: '#878d96',
|
|
138
|
-
dim: '#686f7e',
|
|
139
|
-
text: '',
|
|
140
|
-
userMessageBg: '#1a1b26',
|
|
141
|
-
toolPendingBg: '#16161e',
|
|
142
|
-
toolSuccessBg: '#1b2430',
|
|
143
|
-
toolErrorBg: '#2a1b26',
|
|
144
|
-
toolOutput: '#878d96',
|
|
145
|
-
toolDiffAdded: '#9ece6a',
|
|
146
|
-
toolDiffRemoved: '#f7768e',
|
|
147
|
-
toolDiffContext: '#858d9a',
|
|
148
|
-
mdHeading: '#bb9af7',
|
|
149
|
-
mdLink: '#7dcfff',
|
|
150
|
-
mdLinkUrl: '#626a77',
|
|
151
|
-
mdCode: '#7f8799',
|
|
152
|
-
mdCodeBlock: '#9aa5ce',
|
|
153
|
-
mdCodeBlockBorder: '#3d424a',
|
|
154
|
-
mdKeyword: '#bb9af7',
|
|
155
|
-
syntaxString: '#9ece6a',
|
|
156
|
-
syntaxNumber: '#ff9e64',
|
|
157
|
-
syntaxFunction: '#7aa2f7',
|
|
158
|
-
syntaxType: '#2ac3de',
|
|
159
|
-
mdQuote: '#777d88',
|
|
160
|
-
mdListBullet: '#7aa2f7',
|
|
161
|
-
thinkingText: '#6a7394',
|
|
162
|
-
customMessageLabel: '#bb9af7',
|
|
163
|
-
userMessageText: '#c0caf5',
|
|
164
|
-
toolTitle: '#c0caf5',
|
|
165
|
-
};
|
|
166
|
-
const SOLARIZED_PALETTE = {
|
|
167
|
-
...DARK_PALETTE,
|
|
168
|
-
accent: '#c39400',
|
|
169
|
-
border: '#268bd2',
|
|
170
|
-
borderAccent: '#2aa198',
|
|
171
|
-
borderMuted: '#586e75',
|
|
172
|
-
success: '#859900',
|
|
173
|
-
error: '#e87b79',
|
|
174
|
-
warning: '#cb4b16',
|
|
175
|
-
muted: '#8e9e9f',
|
|
176
|
-
dim: '#668088',
|
|
177
|
-
text: '',
|
|
178
|
-
userMessageBg: '#073642',
|
|
179
|
-
toolPendingBg: '#002b36',
|
|
180
|
-
toolSuccessBg: '#073642',
|
|
181
|
-
toolErrorBg: '#3b2020',
|
|
182
|
-
toolOutput: '#8e9e9f',
|
|
183
|
-
toolDiffAdded: '#8fa500',
|
|
184
|
-
toolDiffRemoved: '#e87b79',
|
|
185
|
-
toolDiffContext: '#889fa6',
|
|
186
|
-
mdHeading: '#b58900',
|
|
187
|
-
mdLink: '#3496da',
|
|
188
|
-
mdLinkUrl: '#5e767d',
|
|
189
|
-
mdCode: '#93a1a1',
|
|
190
|
-
mdCodeBlock: '#2aa198',
|
|
191
|
-
mdCodeBlockBorder: '#073642',
|
|
192
|
-
mdKeyword: '#859900',
|
|
193
|
-
syntaxString: '#2aa198',
|
|
194
|
-
syntaxNumber: '#de66a0',
|
|
195
|
-
syntaxFunction: '#3496da',
|
|
196
|
-
syntaxType: '#b58900',
|
|
197
|
-
mdQuote: '#839496',
|
|
198
|
-
mdListBullet: '#b58900',
|
|
199
|
-
thinkingText: '#5e767d',
|
|
200
|
-
customMessageLabel: '#6c71c4',
|
|
201
|
-
userMessageText: '#93a1a1',
|
|
202
|
-
toolTitle: '#93a1a1',
|
|
203
|
-
};
|
|
204
|
-
const CATPPUCCIN_PALETTE = {
|
|
205
|
-
...DARK_PALETTE,
|
|
206
|
-
accent: '#fab387',
|
|
207
|
-
border: '#89b4fa',
|
|
208
|
-
borderAccent: '#b4befe',
|
|
209
|
-
borderMuted: '#313244',
|
|
210
|
-
success: '#a6e3a1',
|
|
211
|
-
error: '#f38ba8',
|
|
212
|
-
warning: '#f9e2af',
|
|
213
|
-
muted: '#989db0',
|
|
214
|
-
dim: '#7a7d93',
|
|
215
|
-
text: '',
|
|
216
|
-
userMessageBg: '#181825',
|
|
217
|
-
toolPendingBg: '#313244',
|
|
218
|
-
toolSuccessBg: '#181825',
|
|
219
|
-
toolErrorBg: '#11111b',
|
|
220
|
-
toolTitle: '#b4befe',
|
|
221
|
-
toolOutput: '#989db0',
|
|
222
|
-
toolDiffAdded: '#a6e3a1',
|
|
223
|
-
toolDiffRemoved: '#f38ba8',
|
|
224
|
-
toolDiffContext: '#989db0',
|
|
225
|
-
mdHeading: '#fab387',
|
|
226
|
-
mdLink: '#89b4fa',
|
|
227
|
-
mdLinkUrl: '#6c7086',
|
|
228
|
-
mdCode: '#a6adc8',
|
|
229
|
-
mdCodeBlock: '#cdd6f4',
|
|
230
|
-
mdCodeBlockBorder: '#313244',
|
|
231
|
-
mdKeyword: '#cba6f7',
|
|
232
|
-
syntaxString: '#a6e3a1',
|
|
233
|
-
syntaxNumber: '#fab387',
|
|
234
|
-
syntaxFunction: '#89b4fa',
|
|
235
|
-
syntaxType: '#f9e2af',
|
|
236
|
-
mdQuote: '#7f849c',
|
|
237
|
-
mdListBullet: '#fab387',
|
|
238
|
-
thinkingText: '#6c7086',
|
|
239
|
-
customMessageLabel: '#cba6f7',
|
|
240
|
-
userMessageText: '#cdd6f4',
|
|
241
|
-
};
|
|
242
|
-
const DRACULA_PALETTE = {
|
|
243
|
-
...DARK_PALETTE,
|
|
244
|
-
accent: '#bd93f9',
|
|
245
|
-
border: '#bd93f9',
|
|
246
|
-
borderAccent: '#ff79c6',
|
|
247
|
-
borderMuted: '#44475a',
|
|
248
|
-
success: '#50fa7b',
|
|
249
|
-
error: '#ff5a5a',
|
|
250
|
-
warning: '#f1fa8c',
|
|
251
|
-
muted: '#8693b9',
|
|
252
|
-
dim: '#6f7492',
|
|
253
|
-
text: '',
|
|
254
|
-
userMessageBg: '#1f2029',
|
|
255
|
-
toolPendingBg: '#21222c',
|
|
256
|
-
toolSuccessBg: '#1a1f1e',
|
|
257
|
-
toolErrorBg: '#2a2028',
|
|
258
|
-
toolTitle: '#8be9fd',
|
|
259
|
-
toolOutput: '#8693b9',
|
|
260
|
-
toolDiffAdded: '#50fa7b',
|
|
261
|
-
toolDiffRemoved: '#ff5a5a',
|
|
262
|
-
toolDiffContext: '#8693b9',
|
|
263
|
-
mdHeading: '#bd93f9',
|
|
264
|
-
mdLink: '#8be9fd',
|
|
265
|
-
mdLinkUrl: '#6474a5',
|
|
266
|
-
mdCode: '#9aa3c7',
|
|
267
|
-
mdCodeBlock: '#f8f8f2',
|
|
268
|
-
mdCodeBlockBorder: '#44475a',
|
|
269
|
-
mdKeyword: '#ff79c6',
|
|
270
|
-
syntaxString: '#f1fa8c',
|
|
271
|
-
syntaxNumber: '#bd93f9',
|
|
272
|
-
syntaxFunction: '#50fa7b',
|
|
273
|
-
syntaxType: '#8be9fd',
|
|
274
|
-
mdQuote: '#6474a5',
|
|
275
|
-
mdListBullet: '#ff79c6',
|
|
276
|
-
thinkingText: '#6474a5',
|
|
277
|
-
customMessageLabel: '#bd93f9',
|
|
278
|
-
userMessageText: '#f8f8f2',
|
|
279
|
-
};
|
|
280
|
-
const NORD_PALETTE = {
|
|
281
|
-
...DARK_PALETTE,
|
|
282
|
-
accent: '#88c0d0',
|
|
283
|
-
border: '#5e81ac',
|
|
284
|
-
borderAccent: '#88c0d0',
|
|
285
|
-
borderMuted: '#434c5e',
|
|
286
|
-
success: '#a3be8c',
|
|
287
|
-
error: '#daa2a8',
|
|
288
|
-
warning: '#ebcb8b',
|
|
289
|
-
muted: '#a8b1c1',
|
|
290
|
-
dim: '#8490a7',
|
|
291
|
-
text: '',
|
|
292
|
-
userMessageBg: '#3b4252',
|
|
293
|
-
toolPendingBg: '#3b4252',
|
|
294
|
-
toolSuccessBg: '#2e3440',
|
|
295
|
-
toolErrorBg: '#3b2f31',
|
|
296
|
-
toolTitle: '#88c0d0',
|
|
297
|
-
toolOutput: '#a8b1c1',
|
|
298
|
-
toolDiffAdded: '#a3be8c',
|
|
299
|
-
toolDiffRemoved: '#daa2a8',
|
|
300
|
-
toolDiffContext: '#a8b1c1',
|
|
301
|
-
mdHeading: '#88c0d0',
|
|
302
|
-
mdLink: '#88c0d0',
|
|
303
|
-
mdLinkUrl: '#727f9a',
|
|
304
|
-
mdCode: '#82a2c1',
|
|
305
|
-
mdCodeBlock: '#d8dee9',
|
|
306
|
-
mdCodeBlockBorder: '#434c5e',
|
|
307
|
-
mdKeyword: '#82a2c1',
|
|
308
|
-
syntaxString: '#a3be8c',
|
|
309
|
-
syntaxNumber: '#b793b1',
|
|
310
|
-
syntaxFunction: '#88c0d0',
|
|
311
|
-
syntaxType: '#8fbcbb',
|
|
312
|
-
mdQuote: '#7b88a1',
|
|
313
|
-
mdListBullet: '#81a1c1',
|
|
314
|
-
thinkingText: '#747f95',
|
|
315
|
-
customMessageLabel: '#b48ead',
|
|
316
|
-
userMessageText: '#d8dee9',
|
|
317
|
-
};
|
|
318
|
-
const GRUVBOX_PALETTE = {
|
|
319
|
-
...DARK_PALETTE,
|
|
320
|
-
accent: '#fe8019',
|
|
321
|
-
border: '#458588',
|
|
322
|
-
borderAccent: '#8ec07c',
|
|
323
|
-
borderMuted: '#504945',
|
|
324
|
-
success: '#b8bb26',
|
|
325
|
-
error: '#fc6c5c',
|
|
326
|
-
warning: '#fabd2f',
|
|
327
|
-
muted: '#a4988b',
|
|
328
|
-
dim: '#87796d',
|
|
329
|
-
text: '',
|
|
330
|
-
userMessageBg: '#1d2021',
|
|
331
|
-
toolPendingBg: '#32302f',
|
|
332
|
-
toolSuccessBg: '#1d2021',
|
|
333
|
-
toolErrorBg: '#3c2021',
|
|
334
|
-
toolTitle: '#ebdbb2',
|
|
335
|
-
toolOutput: '#a4988b',
|
|
336
|
-
toolDiffAdded: '#b8bb26',
|
|
337
|
-
toolDiffRemoved: '#fc6c5c',
|
|
338
|
-
toolDiffContext: '#a4988b',
|
|
339
|
-
mdHeading: '#fabd2f',
|
|
340
|
-
mdLink: '#8ec07c',
|
|
341
|
-
mdLinkUrl: '#7e7165',
|
|
342
|
-
mdCode: '#bdae93',
|
|
343
|
-
mdCodeBlock: '#ebdbb2',
|
|
344
|
-
mdCodeBlockBorder: '#504945',
|
|
345
|
-
mdKeyword: '#d3869b',
|
|
346
|
-
syntaxString: '#b8bb26',
|
|
347
|
-
syntaxNumber: '#d3869b',
|
|
348
|
-
syntaxFunction: '#fabd2f',
|
|
349
|
-
syntaxType: '#8ec07c',
|
|
350
|
-
mdQuote: '#928374',
|
|
351
|
-
mdListBullet: '#fe8019',
|
|
352
|
-
thinkingText: '#7e7165',
|
|
353
|
-
customMessageLabel: '#d3869b',
|
|
354
|
-
userMessageText: '#ebdbb2',
|
|
355
|
-
};
|
|
356
|
-
const ROSE_PINE_PALETTE = {
|
|
357
|
-
...DARK_PALETTE,
|
|
358
|
-
accent: '#c4a7e7',
|
|
359
|
-
border: '#31748f',
|
|
360
|
-
borderAccent: '#9ccfd8',
|
|
361
|
-
borderMuted: '#403d52',
|
|
362
|
-
success: '#9ccfd8',
|
|
363
|
-
error: '#eb6f92',
|
|
364
|
-
warning: '#f6c177',
|
|
365
|
-
muted: '#8c89a2',
|
|
366
|
-
dim: '#6f6b8b',
|
|
367
|
-
text: '',
|
|
368
|
-
userMessageBg: '#21202e',
|
|
369
|
-
toolPendingBg: '#1f1d2e',
|
|
370
|
-
toolSuccessBg: '#21202e',
|
|
371
|
-
toolErrorBg: '#2d1f26',
|
|
372
|
-
toolTitle: '#9ccfd8',
|
|
373
|
-
toolOutput: '#8c89a2',
|
|
374
|
-
toolDiffAdded: '#9ccfd8',
|
|
375
|
-
toolDiffRemoved: '#eb6f92',
|
|
376
|
-
toolDiffContext: '#8c89a2',
|
|
377
|
-
mdHeading: '#c4a7e7',
|
|
378
|
-
mdLink: '#9ccfd8',
|
|
379
|
-
mdLinkUrl: '#908caa',
|
|
380
|
-
mdCode: '#a8a4c4',
|
|
381
|
-
mdCodeBlock: '#e0def4',
|
|
382
|
-
mdCodeBlockBorder: '#403d52',
|
|
383
|
-
mdKeyword: '#3b8cac',
|
|
384
|
-
syntaxString: '#f6c177',
|
|
385
|
-
syntaxNumber: '#ebbcba',
|
|
386
|
-
syntaxFunction: '#9ccfd8',
|
|
387
|
-
syntaxType: '#c4a7e7',
|
|
388
|
-
mdQuote: '#6e6a86',
|
|
389
|
-
mdListBullet: '#c4a7e7',
|
|
390
|
-
thinkingText: '#6e6a86',
|
|
391
|
-
customMessageLabel: '#c4a7e7',
|
|
392
|
-
userMessageText: '#e0def4',
|
|
393
|
-
};
|
|
394
|
-
const MONO_PALETTE = {
|
|
395
|
-
accent: '#e8e8e8',
|
|
396
|
-
border: '#888888',
|
|
397
|
-
borderAccent: '#b8b8b8',
|
|
398
|
-
borderMuted: '#444444',
|
|
399
|
-
success: '#c0c0c0',
|
|
400
|
-
error: '#f0f0f0',
|
|
401
|
-
warning: '#a8a8a8',
|
|
402
|
-
muted: '#939393',
|
|
403
|
-
dim: '#757575',
|
|
404
|
-
text: '',
|
|
405
|
-
userMessageText: '#e8e8e8',
|
|
406
|
-
userMessageBg: '#202020',
|
|
407
|
-
toolPendingBg: '#242424',
|
|
408
|
-
toolSuccessBg: '#1c1c1c',
|
|
409
|
-
toolErrorBg: '#2a2a2a',
|
|
410
|
-
toolTitle: '#e8e8e8',
|
|
411
|
-
toolOutput: '#939393',
|
|
412
|
-
toolDiffAdded: '#c0c0c0',
|
|
413
|
-
toolDiffRemoved: '#f0f0f0',
|
|
414
|
-
toolDiffContext: '#939393',
|
|
415
|
-
mdHeading: '#e8e8e8',
|
|
416
|
-
mdLink: '#b8b8b8',
|
|
417
|
-
mdLinkUrl: '#666666',
|
|
418
|
-
mdCode: '#a0a0a0',
|
|
419
|
-
mdCodeBlock: '#c0c0c0',
|
|
420
|
-
mdCodeBlockBorder: '#444444',
|
|
421
|
-
mdKeyword: '#b8b8b8',
|
|
422
|
-
syntaxString: '#c0c0c0',
|
|
423
|
-
syntaxNumber: '#a0a0a0',
|
|
424
|
-
syntaxFunction: '#e8e8e8',
|
|
425
|
-
syntaxType: '#b8b8b8',
|
|
426
|
-
mdQuote: '#888888',
|
|
427
|
-
mdListBullet: '#b8b8b8',
|
|
428
|
-
thinkingText: '#767676',
|
|
429
|
-
customMessageLabel: '#a8a8a8',
|
|
430
|
-
};
|
|
431
164
|
const PALETTES = {
|
|
432
165
|
dark: DARK_PALETTE,
|
|
433
166
|
light: LIGHT_PALETTE,
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
dracula: DRACULA_PALETTE,
|
|
438
|
-
nord: NORD_PALETTE,
|
|
439
|
-
gruvbox: GRUVBOX_PALETTE,
|
|
440
|
-
'rose-pine': ROSE_PINE_PALETTE,
|
|
441
|
-
mono: MONO_PALETTE,
|
|
167
|
+
// `system` starts from the neutral palette and is rebuilt from the reported
|
|
168
|
+
// terminal colors when a launch can probe them.
|
|
169
|
+
system: DARK_PALETTE,
|
|
442
170
|
};
|
|
443
171
|
/**
|
|
444
172
|
* 16-color foreground fallbacks when the terminal is not truecolor. Entries
|
|
@@ -450,7 +178,7 @@ const DARK_ANSI16 = {
|
|
|
450
178
|
syntaxNumber: '35',
|
|
451
179
|
syntaxFunction: '33',
|
|
452
180
|
syntaxType: '36',
|
|
453
|
-
accent: '
|
|
181
|
+
accent: '94',
|
|
454
182
|
border: '36',
|
|
455
183
|
borderAccent: '36',
|
|
456
184
|
borderMuted: '90',
|
|
@@ -465,21 +193,25 @@ const DARK_ANSI16 = {
|
|
|
465
193
|
// Neutral fills avoid same-index collisions with error and diff ink.
|
|
466
194
|
toolPendingBg: '30',
|
|
467
195
|
toolSuccessBg: '30',
|
|
196
|
+
toolAltBg: '30',
|
|
468
197
|
toolErrorBg: '30',
|
|
469
198
|
toolTitle: '37',
|
|
470
199
|
toolOutput: '37',
|
|
471
200
|
toolDiffAdded: '32',
|
|
472
201
|
toolDiffRemoved: '31',
|
|
473
202
|
toolDiffContext: '90',
|
|
474
|
-
mdHeading: '
|
|
475
|
-
mdLink: '
|
|
203
|
+
mdHeading: '94',
|
|
204
|
+
mdLink: '94',
|
|
476
205
|
mdLinkUrl: '90',
|
|
477
206
|
mdCode: '90',
|
|
478
207
|
mdCodeBlock: '36',
|
|
479
208
|
mdCodeBlockBorder: '90',
|
|
480
209
|
mdKeyword: '36',
|
|
481
210
|
mdQuote: '37',
|
|
482
|
-
|
|
211
|
+
mdQuoteBorder: '90',
|
|
212
|
+
mdHr: '90',
|
|
213
|
+
mdListBullet: '94',
|
|
214
|
+
shellMode: '92',
|
|
483
215
|
thinkingText: '90',
|
|
484
216
|
customMessageLabel: '35',
|
|
485
217
|
};
|
|
@@ -500,15 +232,19 @@ const LIGHT_ANSI16 = {
|
|
|
500
232
|
text: '39',
|
|
501
233
|
userMessageText: '30',
|
|
502
234
|
userMessageBg: '37',
|
|
235
|
+
// This table holds *foreground* codes; `toBackgrounds` adds the 10 that turns
|
|
236
|
+
// them into fills, so `37` here is the white `\x1b[47m` card a light terminal
|
|
237
|
+
// needs under its dark ink.
|
|
503
238
|
toolPendingBg: '37',
|
|
504
239
|
toolSuccessBg: '37',
|
|
240
|
+
toolAltBg: '37',
|
|
505
241
|
toolErrorBg: '37',
|
|
506
242
|
toolTitle: '30',
|
|
507
243
|
toolOutput: '30',
|
|
508
244
|
toolDiffAdded: '30',
|
|
509
245
|
toolDiffRemoved: '31',
|
|
510
246
|
toolDiffContext: '90',
|
|
511
|
-
mdHeading: '
|
|
247
|
+
mdHeading: '34',
|
|
512
248
|
mdLink: '34',
|
|
513
249
|
mdLinkUrl: '90',
|
|
514
250
|
mdCode: '30',
|
|
@@ -516,119 +252,17 @@ const LIGHT_ANSI16 = {
|
|
|
516
252
|
mdCodeBlockBorder: '90',
|
|
517
253
|
mdKeyword: '34',
|
|
518
254
|
mdQuote: '30',
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
customMessageLabel: '35',
|
|
522
|
-
};
|
|
523
|
-
const MIDNIGHT_ANSI16 = {
|
|
524
|
-
...DARK_ANSI16,
|
|
525
|
-
accent: '34',
|
|
526
|
-
border: '34',
|
|
527
|
-
borderAccent: '36',
|
|
528
|
-
mdHeading: '35',
|
|
529
|
-
mdLink: '36',
|
|
530
|
-
mdKeyword: '35',
|
|
531
|
-
mdListBullet: '34',
|
|
532
|
-
customMessageLabel: '35',
|
|
533
|
-
};
|
|
534
|
-
const SOLARIZED_ANSI16 = {
|
|
535
|
-
...DARK_ANSI16,
|
|
536
|
-
accent: '33',
|
|
537
|
-
border: '34',
|
|
538
|
-
borderAccent: '36',
|
|
539
|
-
mdHeading: '33',
|
|
540
|
-
mdLink: '34',
|
|
541
|
-
mdKeyword: '32',
|
|
542
|
-
mdCodeBlock: '36',
|
|
543
|
-
mdListBullet: '33',
|
|
544
|
-
customMessageLabel: '35',
|
|
545
|
-
};
|
|
546
|
-
const CATPPUCCIN_ANSI16 = {
|
|
547
|
-
...DARK_ANSI16,
|
|
548
|
-
accent: '33',
|
|
549
|
-
border: '34',
|
|
550
|
-
borderAccent: '35',
|
|
551
|
-
mdHeading: '33',
|
|
552
|
-
mdLink: '34',
|
|
553
|
-
mdKeyword: '35',
|
|
554
|
-
mdListBullet: '33',
|
|
555
|
-
customMessageLabel: '35',
|
|
556
|
-
};
|
|
557
|
-
const DRACULA_ANSI16 = {
|
|
558
|
-
...DARK_ANSI16,
|
|
559
|
-
accent: '35',
|
|
560
|
-
border: '35',
|
|
561
|
-
borderAccent: '35',
|
|
562
|
-
mdHeading: '35',
|
|
563
|
-
mdLink: '36',
|
|
564
|
-
mdKeyword: '35',
|
|
565
|
-
mdListBullet: '35',
|
|
566
|
-
customMessageLabel: '35',
|
|
567
|
-
};
|
|
568
|
-
const NORD_ANSI16 = {
|
|
569
|
-
...DARK_ANSI16,
|
|
570
|
-
accent: '36',
|
|
571
|
-
border: '34',
|
|
572
|
-
borderAccent: '36',
|
|
573
|
-
mdHeading: '36',
|
|
574
|
-
mdLink: '36',
|
|
575
|
-
mdKeyword: '34',
|
|
255
|
+
mdQuoteBorder: '90',
|
|
256
|
+
mdHr: '90',
|
|
576
257
|
mdListBullet: '34',
|
|
258
|
+
shellMode: '32',
|
|
259
|
+
thinkingText: '90',
|
|
577
260
|
customMessageLabel: '35',
|
|
578
261
|
};
|
|
579
|
-
const GRUVBOX_ANSI16 = {
|
|
580
|
-
...DARK_ANSI16,
|
|
581
|
-
accent: '33',
|
|
582
|
-
border: '36',
|
|
583
|
-
borderAccent: '32',
|
|
584
|
-
mdHeading: '33',
|
|
585
|
-
mdLink: '32',
|
|
586
|
-
mdKeyword: '35',
|
|
587
|
-
mdListBullet: '33',
|
|
588
|
-
customMessageLabel: '35',
|
|
589
|
-
};
|
|
590
|
-
const ROSE_PINE_ANSI16 = {
|
|
591
|
-
...DARK_ANSI16,
|
|
592
|
-
accent: '35',
|
|
593
|
-
border: '36',
|
|
594
|
-
borderAccent: '36',
|
|
595
|
-
mdHeading: '35',
|
|
596
|
-
mdLink: '36',
|
|
597
|
-
mdKeyword: '36',
|
|
598
|
-
mdListBullet: '35',
|
|
599
|
-
customMessageLabel: '35',
|
|
600
|
-
};
|
|
601
|
-
const MONO_ANSI16 = {
|
|
602
|
-
...DARK_ANSI16,
|
|
603
|
-
syntaxString: '37',
|
|
604
|
-
syntaxNumber: '37',
|
|
605
|
-
syntaxFunction: '97',
|
|
606
|
-
syntaxType: '37',
|
|
607
|
-
accent: '97',
|
|
608
|
-
border: '37',
|
|
609
|
-
borderAccent: '97',
|
|
610
|
-
success: '37',
|
|
611
|
-
error: '97',
|
|
612
|
-
warning: '37',
|
|
613
|
-
muted: '37',
|
|
614
|
-
mdHeading: '97',
|
|
615
|
-
mdLink: '37',
|
|
616
|
-
mdCodeBlock: '37',
|
|
617
|
-
mdKeyword: '37',
|
|
618
|
-
mdListBullet: '37',
|
|
619
|
-
customMessageLabel: '37',
|
|
620
|
-
};
|
|
621
262
|
const ANSI16 = {
|
|
622
263
|
dark: DARK_ANSI16,
|
|
623
264
|
light: LIGHT_ANSI16,
|
|
624
|
-
|
|
625
|
-
solarized: SOLARIZED_ANSI16,
|
|
626
|
-
catppuccin: CATPPUCCIN_ANSI16,
|
|
627
|
-
dracula: DRACULA_ANSI16,
|
|
628
|
-
nord: NORD_ANSI16,
|
|
629
|
-
gruvbox: GRUVBOX_ANSI16,
|
|
630
|
-
'rose-pine': ROSE_PINE_ANSI16,
|
|
631
|
-
mono: MONO_ANSI16,
|
|
265
|
+
system: DARK_ANSI16,
|
|
632
266
|
};
|
|
633
267
|
/** Distance from a foreground SGR code to its background twin: 31 → 41, 39 → 49. */
|
|
634
268
|
const BG_CODE_OFFSET = 10;
|
|
@@ -645,18 +279,164 @@ function toBackgrounds(foreground) {
|
|
|
645
279
|
}
|
|
646
280
|
return result;
|
|
647
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Backgrounds for the three tool states, chosen so they stay distinct.
|
|
284
|
+
*
|
|
285
|
+
* This is the group form of the problem {@link nearestXterm256} solves for one
|
|
286
|
+
* colour: the states differ mostly in hue and only slightly in lightness, so
|
|
287
|
+
* quantising each swatch on its own collapses them. The assignment below picks a
|
|
288
|
+
* distinct entry per state and then orders the three by lightness, so pending,
|
|
289
|
+
* success, and error remain recognizable even on a terminal that offers only
|
|
290
|
+
* eight backgrounds.
|
|
291
|
+
*
|
|
292
|
+
* A 16-colour terminal simply has fewer backgrounds than the states that need
|
|
293
|
+
* one. Rather than paint three states the same black, the ones it cannot tint
|
|
294
|
+
* fall back to `toolSuccessBg` and the error state — the only one a reader must
|
|
295
|
+
* act on — is given the red it already has in `ansiBg` when the palette has a
|
|
296
|
+
* usable red background.
|
|
297
|
+
*/
|
|
298
|
+
function resolveToolFills(name, depth, terminal) {
|
|
299
|
+
const palette = name === 'system' ? systemPalette(terminal) : PALETTES[name];
|
|
300
|
+
const reported = depth === 'ansi16' && terminal?.palette !== undefined && terminal.palette.length === 16
|
|
301
|
+
? terminal.palette
|
|
302
|
+
: undefined;
|
|
303
|
+
const states = ['toolPendingBg', 'toolSuccessBg', 'toolErrorBg'];
|
|
304
|
+
const swatches = states.map(state => {
|
|
305
|
+
const swatch = palette[state];
|
|
306
|
+
if (typeof swatch !== 'string' || swatch === '')
|
|
307
|
+
return undefined;
|
|
308
|
+
const [r, g, b] = hexToRgb(swatch);
|
|
309
|
+
return { r, g, b };
|
|
310
|
+
});
|
|
311
|
+
const filled = swatches.every((swatch) => swatch !== undefined);
|
|
312
|
+
if (!filled)
|
|
313
|
+
return undefined;
|
|
314
|
+
if (depth === 'ansi256') {
|
|
315
|
+
// The neutral ramp is where the three near-black surfaces actually live; the
|
|
316
|
+
// colour cube would round them all to the same corner.
|
|
317
|
+
const indices = swatches.map(swatch => nearestXterm256Grey(swatch));
|
|
318
|
+
return assignDistinct(states, indices, index => `\x1b[48;5;${index}m`);
|
|
319
|
+
}
|
|
320
|
+
if (reported !== undefined) {
|
|
321
|
+
// Sixteen colours are not enough for three surfaces, so each state claims a
|
|
322
|
+
// *different* entry from the ones its own hue actually reaches, rather than
|
|
323
|
+
// nudging a number that has no meaning in this palette.
|
|
324
|
+
return pickDistinctPaletteEntries(name, states, swatches, reported, index => ansiIndexSgr(index, true));
|
|
325
|
+
}
|
|
326
|
+
// The terminal never answered our colour query, so the standard xterm ANSI
|
|
327
|
+
// meanings are assumed: three different backgrounds that carry text. Guessing
|
|
328
|
+
// per swatch here is exactly what collapsed the three states into one. Red is
|
|
329
|
+
// deliberately not one of them — a red fill would share its palette slot with
|
|
330
|
+
// the red `error` and `toolDiffRemoved` ink drawn on top of it.
|
|
331
|
+
// A light card is white paper under dark ink, and the eight backgrounds an
|
|
332
|
+
// ANSI-16 terminal offers contain exactly one that carries it. Pending and
|
|
333
|
+
// success therefore share it, and the failing card — the state a reader has to
|
|
334
|
+
// act on — is the one that gets a fill of its own.
|
|
335
|
+
return name === 'light'
|
|
336
|
+
? { toolPendingBg: '\x1b[47m', toolSuccessBg: '\x1b[47m', toolErrorBg: '\x1b[45m' }
|
|
337
|
+
: { toolPendingBg: '\x1b[44m', toolSuccessBg: '\x1b[40m', toolErrorBg: '\x1b[45m' };
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* Give each state the nearest palette entry no other state has taken.
|
|
341
|
+
*
|
|
342
|
+
* A 16-colour terminal cannot express a subtle tint, so the question stops being
|
|
343
|
+
* "which entry is nearest" and becomes "which entry can this state have". Each
|
|
344
|
+
* state is served in turn, by increasing distance, and only entries whose
|
|
345
|
+
* luminance is low enough to hold card text are considered — a bright palette
|
|
346
|
+
* entry would turn a tool card into a block of white.
|
|
347
|
+
*/
|
|
348
|
+
function pickDistinctPaletteEntries(name, states, swatches, palette, encode) {
|
|
349
|
+
// A palette slot already used as ink on a card is not available as a fill: a
|
|
350
|
+
// red background under red diff lines erases them. The check runs against the
|
|
351
|
+
// same fallback table the foreground codes come from, so the two can never
|
|
352
|
+
// disagree about which slot a colour occupies.
|
|
353
|
+
const takenByInk = new Set(CARD_INK_COLORS
|
|
354
|
+
.map(color => Number.parseInt(ANSI16[name][color], 10))
|
|
355
|
+
.filter(code => Number.isInteger(code) && code < 8));
|
|
356
|
+
const ceiling = SURFACE_MAX_LUMINANCE[name];
|
|
357
|
+
// A light card's ink is a black that owns slot 0, so its fills come from the
|
|
358
|
+
// bright end instead; the test is "far enough from the ink", not "dark".
|
|
359
|
+
const eligible = (entry, index) => entry !== undefined
|
|
360
|
+
&& !takenByInk.has(index)
|
|
361
|
+
&& (name === 'light' ? relativeLuminance(entry) >= ceiling : relativeLuminance(entry) < ceiling);
|
|
362
|
+
if (palette.filter(eligible).length < states.length)
|
|
363
|
+
return undefined;
|
|
364
|
+
const ranked = palette
|
|
365
|
+
.flatMap((entry, index) => entry === undefined ? [] : [{ index, entry: entry }])
|
|
366
|
+
.filter(({ entry, index }) => eligible(entry, index))
|
|
367
|
+
.map(({ index, entry }) => ({ index, lab: toOklab(entry) }));
|
|
368
|
+
if (ranked.length < states.length)
|
|
369
|
+
return undefined;
|
|
370
|
+
const used = new Set();
|
|
371
|
+
const result = {};
|
|
372
|
+
for (let at = 0; at < states.length; at += 1) {
|
|
373
|
+
const target = toOklab(swatches[at]);
|
|
374
|
+
let best;
|
|
375
|
+
for (const candidate of ranked) {
|
|
376
|
+
if (used.has(candidate.index))
|
|
377
|
+
continue;
|
|
378
|
+
const distance = labDistance(target, candidate.lab);
|
|
379
|
+
if (best === undefined || distance < best.distance)
|
|
380
|
+
best = { index: candidate.index, distance };
|
|
381
|
+
}
|
|
382
|
+
if (best === undefined)
|
|
383
|
+
return undefined;
|
|
384
|
+
used.add(best.index);
|
|
385
|
+
result[states[at]] = encode(best.index);
|
|
386
|
+
}
|
|
387
|
+
return result;
|
|
388
|
+
}
|
|
389
|
+
/** Ink drawn on a tool card, by theme token and ANSI-16 fallback code. */
|
|
390
|
+
const CARD_INK_COLORS = [
|
|
391
|
+
'toolTitle', 'toolOutput', 'toolDiffAdded', 'toolDiffRemoved', 'toolDiffContext',
|
|
392
|
+
'mdKeyword', 'syntaxString', 'syntaxNumber', 'syntaxFunction', 'syntaxType',
|
|
393
|
+
'mdCodeBlock', 'mdLink', 'mdHeading', 'mdQuote', 'dim', 'muted', 'accent',
|
|
394
|
+
'success', 'error', 'warning', 'thinkingText', 'mdListBullet', 'mdHr',
|
|
395
|
+
'mdQuoteBorder', 'mdCodeBlockBorder', 'mdCode', 'mdLinkUrl',
|
|
396
|
+
];
|
|
397
|
+
/**
|
|
398
|
+
* How light a card fill may be, per palette.
|
|
399
|
+
*
|
|
400
|
+
* The constraint is not "dark" — it is "far from the ink". A dark theme draws
|
|
401
|
+
* light text on a dark fill and a light theme draws dark text on a light fill, so
|
|
402
|
+
* each palette states its own ceiling instead of sharing a dark one that would
|
|
403
|
+
* make every card on a light terminal a black block.
|
|
404
|
+
*/
|
|
405
|
+
const SURFACE_MAX_LUMINANCE = {
|
|
406
|
+
dark: 0.5, system: 0.5,
|
|
407
|
+
// A light card's fill comes from the bright end; the ceiling is its floor.
|
|
408
|
+
light: 0.98,
|
|
409
|
+
};
|
|
410
|
+
/**
|
|
411
|
+
* Give every state its own encoding, nudging the ones that collided.
|
|
412
|
+
*
|
|
413
|
+
* `preferred` is the honest per-state choice; when two states land on it, the
|
|
414
|
+
* one further from the light end is pushed to a neighbouring entry. A terminal
|
|
415
|
+
* whose palette has no room left keeps the collision rather than inventing a
|
|
416
|
+
* colour it cannot draw, which is why this returns `undefined` in that case.
|
|
417
|
+
*/
|
|
418
|
+
function assignDistinct(states, preferred, encode) {
|
|
419
|
+
const used = new Set();
|
|
420
|
+
const result = {};
|
|
421
|
+
for (let at = 0; at < states.length; at += 1) {
|
|
422
|
+
const state = states[at];
|
|
423
|
+
let index = preferred[at] ?? 0;
|
|
424
|
+
for (let step = 1; used.has(index) && step < 8; step += 1) {
|
|
425
|
+
// Alternate lighter and darker so a nudge stays close to the intent.
|
|
426
|
+
index = preferred[at] + (step % 2 === 1 ? -step : step);
|
|
427
|
+
}
|
|
428
|
+
if (used.has(index))
|
|
429
|
+
return undefined;
|
|
430
|
+
used.add(index);
|
|
431
|
+
result[state] = encode(index);
|
|
432
|
+
}
|
|
433
|
+
return result;
|
|
434
|
+
}
|
|
648
435
|
/** 16-color background fallbacks when the terminal is not truecolor. */
|
|
649
436
|
const ANSI16_BG = {
|
|
650
437
|
dark: toBackgrounds(DARK_ANSI16),
|
|
651
438
|
light: toBackgrounds(LIGHT_ANSI16),
|
|
652
|
-
|
|
653
|
-
solarized: toBackgrounds(SOLARIZED_ANSI16),
|
|
654
|
-
catppuccin: toBackgrounds(CATPPUCCIN_ANSI16),
|
|
655
|
-
dracula: toBackgrounds(DRACULA_ANSI16),
|
|
656
|
-
nord: toBackgrounds(NORD_ANSI16),
|
|
657
|
-
gruvbox: toBackgrounds(GRUVBOX_ANSI16),
|
|
658
|
-
'rose-pine': toBackgrounds(ROSE_PINE_ANSI16),
|
|
659
|
-
mono: toBackgrounds(MONO_ANSI16),
|
|
439
|
+
system: toBackgrounds(DARK_ANSI16),
|
|
660
440
|
};
|
|
661
441
|
const FG_RESET = '\x1b[39m';
|
|
662
442
|
const BG_RESET = '\x1b[49m';
|
|
@@ -666,11 +446,15 @@ const UNDERLINE_RESET = '\x1b[24m';
|
|
|
666
446
|
const STRIKE_RESET = '\x1b[29m';
|
|
667
447
|
const INVERSE_RESET = '\x1b[27m';
|
|
668
448
|
/** Built-in palettes, including a few well-known oh-my-pi coding themes. */
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
449
|
+
/**
|
|
450
|
+
* Shipped palettes: one dark, one light, and the one that follows the terminal.
|
|
451
|
+
*
|
|
452
|
+
* A theme is a contract the whole TUI is read through — every card, diff, and
|
|
453
|
+
* syntax token has to stay legible on it — so the set stays small enough to hold
|
|
454
|
+
* to that standard. `system` is not a third design: it derives its surfaces from
|
|
455
|
+
* the colours the terminal reports and then follows the same rules.
|
|
456
|
+
*/
|
|
457
|
+
export const THEME_NAMES = ['dark', 'light', 'system'];
|
|
674
458
|
/** True when `value` is a shipped palette name. */
|
|
675
459
|
export function isThemeName(value) {
|
|
676
460
|
return THEME_NAMES.includes(value);
|
|
@@ -726,27 +510,282 @@ function hexToRgb(hex) {
|
|
|
726
510
|
Number.parseInt(body.slice(4, 6), 16),
|
|
727
511
|
];
|
|
728
512
|
}
|
|
729
|
-
/**
|
|
730
|
-
|
|
513
|
+
/**
|
|
514
|
+
* SGR for one swatch at a color depth; `fallback` is a code from
|
|
515
|
+
* {@link ANSI16} or {@link ANSI16_BG}. `layer` is 38 for ink and 48 for fill.
|
|
516
|
+
*/
|
|
517
|
+
function swatchCode(swatch, depth, fallback, layer) {
|
|
731
518
|
if (swatch === '')
|
|
732
|
-
return '\x1b[39m';
|
|
519
|
+
return layer === 38 ? '\x1b[39m' : '\x1b[49m';
|
|
733
520
|
if (typeof swatch === 'number')
|
|
734
|
-
return `\x1b[
|
|
735
|
-
if (
|
|
521
|
+
return `\x1b[${layer};5;${swatch}m`;
|
|
522
|
+
if (depth === 'ansi16')
|
|
736
523
|
return `\x1b[${fallback}m`;
|
|
737
524
|
const [r, g, b] = hexToRgb(swatch);
|
|
738
|
-
|
|
525
|
+
if (depth === 'ansi256')
|
|
526
|
+
return `\x1b[${layer};5;${nearestXterm256({ r, g, b })}m`;
|
|
527
|
+
return `\x1b[${layer};2;${r};${g};${b}m`;
|
|
739
528
|
}
|
|
740
|
-
/**
|
|
741
|
-
function
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
529
|
+
/** Mix two colors in linear light; `weight` is the share of `to`. */
|
|
530
|
+
function mixRgb(from, to, weight) {
|
|
531
|
+
const linear = (value) => {
|
|
532
|
+
const channelValue = value / 255;
|
|
533
|
+
return channelValue <= 0.04045 ? channelValue / 12.92 : ((channelValue + 0.055) / 1.055) ** 2.4;
|
|
534
|
+
};
|
|
535
|
+
const encode = (value) => {
|
|
536
|
+
const clamped = Math.max(0, Math.min(1, value));
|
|
537
|
+
return Math.round(255 * (clamped <= 0.003_130_8 ? clamped * 12.92 : 1.055 * clamped ** (1 / 2.4) - 0.055));
|
|
538
|
+
};
|
|
539
|
+
const mix = (left, right) => linear(left) * (1 - weight) + linear(right) * weight;
|
|
540
|
+
return { r: encode(mix(from.r, to.r)), g: encode(mix(from.g, to.g)), b: encode(mix(from.b, to.b)) };
|
|
541
|
+
}
|
|
542
|
+
function toHex(color) {
|
|
543
|
+
const part = (value) => Math.round(value).toString(16).padStart(2, '0');
|
|
544
|
+
return `#${part(color.r)}${part(color.g)}${part(color.b)}`;
|
|
545
|
+
}
|
|
546
|
+
/** The fixed red a `system` error surface mixes toward when the palette has none. */
|
|
547
|
+
const SYSTEM_ERROR_RED = { r: 0xff, g: 0x5f, b: 0x5f };
|
|
548
|
+
/** The fixed green a `system` success surface mixes toward when the palette has none. */
|
|
549
|
+
const SYSTEM_SUCCESS_GREEN = { r: 0x4a, g: 0xc8, b: 0x7a };
|
|
550
|
+
/** The fixed blue a `system` notice surface mixes toward when the palette has none. */
|
|
551
|
+
const SYSTEM_NOTICE_BLUE = { r: 0x6f, g: 0xa8, b: 0xff };
|
|
552
|
+
/**
|
|
553
|
+
* Relative luminance each card state is held to on a `system` palette.
|
|
554
|
+
*
|
|
555
|
+
* These are the shipped dark palette's own fills measured as luminance, so a
|
|
556
|
+
* `system` card sits where a `dark` one does no matter what green the terminal
|
|
557
|
+
* reports. They are the one part of the card that must not follow the terminal:
|
|
558
|
+
* a terminal's green is chosen for *ink* on its background, and spending that
|
|
559
|
+
* green on a large fill is what pushed the shipped value from 4.4% to 11.7%
|
|
560
|
+
* luminance and flattened dimmed text on a successful card to 3.3:1.
|
|
561
|
+
*/
|
|
562
|
+
const SYSTEM_CARD_LUMINANCE = {
|
|
563
|
+
pending: 0.0386,
|
|
564
|
+
success: 0.0440,
|
|
565
|
+
error: 0.0391,
|
|
566
|
+
};
|
|
567
|
+
/**
|
|
568
|
+
* How far a card may move from the terminal background toward a state hue.
|
|
569
|
+
*
|
|
570
|
+
* A card is a step off the reader's own background, not a swatch of the terminal
|
|
571
|
+
* palette. Scaling a tint to the target luminance directly keeps the hue but not
|
|
572
|
+
* the calm: a saturated green has to be dimmed so far to reach 4.4% that the
|
|
573
|
+
* scaled channels clip, and a light terminal turns it neon. Interpolating from the
|
|
574
|
+
* background toward the hue instead bounds how much of the hue a card can ever
|
|
575
|
+
* contain, so the fill stays a surface whatever the terminal reports.
|
|
576
|
+
*/
|
|
577
|
+
const SYSTEM_CARD_TINT = 0.34;
|
|
578
|
+
/**
|
|
579
|
+
* Where the alternate successful surface sits relative to the primary one.
|
|
580
|
+
*
|
|
581
|
+
* A slight step, not a second state: the pair exists so two adjacent successful
|
|
582
|
+
* calls do not merge into one block. Anything larger starts naming a difference
|
|
583
|
+
* that is not there.
|
|
584
|
+
*/
|
|
585
|
+
const SYSTEM_ALT_STEP = 1.065;
|
|
586
|
+
/**
|
|
587
|
+
* The background-to-hue step that lands closest to a target relative luminance.
|
|
588
|
+
*
|
|
589
|
+
* A fixed weight cannot hold a card at one lightness — the same weight applied to
|
|
590
|
+
* a bright terminal green and to a dim one lands almost three stops apart — so the
|
|
591
|
+
* weight is solved for instead, by bisection. Hue still comes from the terminal,
|
|
592
|
+
* which is what makes the fill say "success"; how *much* of it a card spends is
|
|
593
|
+
* the theme's decision, and the tint cap is what keeps the result a surface.
|
|
594
|
+
*/
|
|
595
|
+
function tintAtLuminance(background, tint, target) {
|
|
596
|
+
const at = (weight) => mixRgb(background, tint, weight);
|
|
597
|
+
const luminanceAt = (weight) => relativeLuminance(at(weight));
|
|
598
|
+
// Interpolating between two luminance endpoints is monotonic only while one
|
|
599
|
+
// side sits below the target; a background already brighter than the target
|
|
600
|
+
// reverses which direction helps, so solve outward from the background.
|
|
601
|
+
const towardsBrighter = relativeLuminance(background) < target;
|
|
602
|
+
let low = 0;
|
|
603
|
+
let high = SYSTEM_CARD_TINT;
|
|
604
|
+
if (towardsBrighter ? luminanceAt(high) < target : luminanceAt(high) > target) {
|
|
605
|
+
// The capped step already overshoots or undershoots the target: the cap is
|
|
606
|
+
// the answer, and the card keeps the calm it was given.
|
|
607
|
+
return at(high);
|
|
608
|
+
}
|
|
609
|
+
for (let step = 0; step < 40; step += 1) {
|
|
610
|
+
const middle = (low + high) / 2;
|
|
611
|
+
if (towardsBrighter ? luminanceAt(middle) < target : luminanceAt(middle) > target)
|
|
612
|
+
low = middle;
|
|
613
|
+
else
|
|
614
|
+
high = middle;
|
|
615
|
+
}
|
|
616
|
+
return at((low + high) / 2);
|
|
617
|
+
}
|
|
618
|
+
/**
|
|
619
|
+
* The background lifted toward white until it reaches a target luminance.
|
|
620
|
+
*
|
|
621
|
+
* Neutral surfaces need a lightness step, not a hue: interpolating toward the
|
|
622
|
+
* foreground flattens whatever faint tint the terminal's background carries, so
|
|
623
|
+
* the step is taken toward white and the background's own channel ratios survive.
|
|
624
|
+
*/
|
|
625
|
+
function liftedToLuminance(background, target) {
|
|
626
|
+
// On paper the background is already brighter than a card wants to be, so the
|
|
627
|
+
// step goes toward black instead; either way it keeps the background's ratios.
|
|
628
|
+
const pole = relativeLuminance(background) < target ? { r: 255, g: 255, b: 255 } : { r: 0, g: 0, b: 0 };
|
|
629
|
+
const towardsBrighter = pole.r === 255;
|
|
630
|
+
const at = (weight) => mixRgb(background, pole, weight);
|
|
631
|
+
const luminanceAt = (weight) => relativeLuminance(at(weight));
|
|
632
|
+
let low = 0;
|
|
633
|
+
let high = 1;
|
|
634
|
+
for (let step = 0; step < 40; step += 1) {
|
|
635
|
+
const middle = (low + high) / 2;
|
|
636
|
+
if (towardsBrighter ? luminanceAt(middle) < target : luminanceAt(middle) > target)
|
|
637
|
+
low = middle;
|
|
638
|
+
else
|
|
639
|
+
high = middle;
|
|
640
|
+
}
|
|
641
|
+
return at((low + high) / 2);
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* The contrast a swatch keeps against the surfaces it is drawn on.
|
|
645
|
+
*
|
|
646
|
+
* Taken against the *weakest* card rather than against the page, because a
|
|
647
|
+
* secondary ink is spent on card fills — a hint on a successful card is the case
|
|
648
|
+
* that has to stay readable, not the same hint on bare background.
|
|
649
|
+
*/
|
|
650
|
+
function worstContrast(ink, cards) {
|
|
651
|
+
return Math.min(...cards.map(card => contrastRatio(ink, card)));
|
|
652
|
+
}
|
|
653
|
+
/**
|
|
654
|
+
* A secondary ink held at the contrast the shipped palette achieved.
|
|
655
|
+
*
|
|
656
|
+
* A fixed mix weight made the result depend on which side was bright: on a dark
|
|
657
|
+
* background the step lightened the ink and stayed safe, but against paper it
|
|
658
|
+
* moved it toward the page, so a light terminal's `dim` ended at 1.75:1 — nearly
|
|
659
|
+
* invisible. The weight is solved from the contrast actually achieved instead, so
|
|
660
|
+
* the live cards keep the hierarchy the palette was drawn with: this ink is as
|
|
661
|
+
* far from the text as the shipped one was from its own cards, no further.
|
|
662
|
+
*/
|
|
663
|
+
function quietInk(foreground, background, cards, target) {
|
|
664
|
+
const at = (weight) => mixRgb(foreground, background, weight);
|
|
665
|
+
/**
|
|
666
|
+
* Which way the weight moves contrast is the host's answer, not a constant.
|
|
667
|
+
*
|
|
668
|
+
* Toward a dark background the ink lightens as it is mixed, so weight raises
|
|
669
|
+
* contrast; toward paper it fades toward the page, so weight lowers it. Solving
|
|
670
|
+
* one direction silently returned the palette's cap on every light host, which
|
|
671
|
+
* is how `dim` came to sit at 1.75:1 while looking correct in the code.
|
|
672
|
+
*/
|
|
673
|
+
const towardsBrighter = relativeLuminance(background) < relativeLuminance(foreground);
|
|
674
|
+
let low = 0;
|
|
675
|
+
let high = 1;
|
|
676
|
+
// A background that is itself close to the cards cannot reach the target at any
|
|
677
|
+
// weight; full foreground is the readable answer and the palette's floor.
|
|
678
|
+
if (worstContrast(at(0), cards) <= target)
|
|
679
|
+
return at(0);
|
|
680
|
+
for (let step = 0; step < 40; step += 1) {
|
|
681
|
+
const middle = (low + high) / 2;
|
|
682
|
+
const contrast = worstContrast(at(middle), cards);
|
|
683
|
+
if (towardsBrighter ? contrast > target : contrast >= target)
|
|
684
|
+
low = middle;
|
|
685
|
+
else
|
|
686
|
+
high = middle;
|
|
687
|
+
}
|
|
688
|
+
return at((low + high) / 2);
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Rebuild the surface and ink swatches from what the terminal reported, so the
|
|
692
|
+
* cards sit on the user's own background instead of a guessed one. The two
|
|
693
|
+
* successful-call surfaces are separated by design: alternation only reads if
|
|
694
|
+
* the pair is perceptibly different.
|
|
695
|
+
*/
|
|
696
|
+
function systemPalette(terminal) {
|
|
697
|
+
const background = terminal?.background;
|
|
698
|
+
const light = terminal?.scheme === 'light';
|
|
699
|
+
const base = light ? LIGHT_PALETTE : DARK_PALETTE;
|
|
700
|
+
if (background === undefined)
|
|
701
|
+
return base;
|
|
702
|
+
const foreground = terminal?.foreground ?? (light ? { r: 0x24, g: 0x24, b: 0x24 } : { r: 0xe0, g: 0xe0, b: 0xe0 });
|
|
703
|
+
const accent = terminal?.palette?.[4] ?? SYSTEM_NOTICE_BLUE;
|
|
704
|
+
const error = terminal?.palette?.[1] ?? SYSTEM_ERROR_RED;
|
|
705
|
+
const successInk = terminal?.palette?.[2] ?? SYSTEM_SUCCESS_GREEN;
|
|
706
|
+
/**
|
|
707
|
+
* A state fill carries its own hue, the way the shipped palettes do.
|
|
708
|
+
*
|
|
709
|
+
* Mixing toward the terminal's *foreground* produced three greys: the states
|
|
710
|
+
* were separated only by lightness, which on a dark background is the one axis
|
|
711
|
+
* a reader cannot see. Mixing toward the state colour keeps the card on the
|
|
712
|
+
* user's own background and gives it the meaning at the same time — but the
|
|
713
|
+
* *amount* of that colour is fixed by the target luminance, not by a weight.
|
|
714
|
+
* A weight made the fill follow the terminal's swatch, and a bright terminal
|
|
715
|
+
* green turned every successful card into a pale one.
|
|
716
|
+
*/
|
|
717
|
+
const stateCard = (tint, target) => toHex(tintAtLuminance(background, tint, target));
|
|
718
|
+
/** Relative luminance of a shipped swatch, keeping the light palette's own paper tone. */
|
|
719
|
+
const swatchLuminance = (color) => {
|
|
720
|
+
const [r, g, b] = hexToRgb(base[color]);
|
|
721
|
+
return relativeLuminance({ r, g, b });
|
|
722
|
+
};
|
|
723
|
+
// A light terminal wants the same three states as tinted paper, so its cards
|
|
724
|
+
// are held at the light palette's own lightness rather than inverted.
|
|
725
|
+
const target = (state) => light
|
|
726
|
+
? swatchLuminance(`tool${state[0].toUpperCase()}${state.slice(1)}Bg`)
|
|
727
|
+
: SYSTEM_CARD_LUMINANCE[state];
|
|
728
|
+
// Pending is neutral, so it keeps the background's own hue and is only lifted in
|
|
729
|
+
// lightness: it must stay quieter than a settled card, and it must still read as
|
|
730
|
+
// belonging to this terminal rather than as a generic grey. Lifting the
|
|
731
|
+
// background keeps whatever faint tint it has; mixing toward the foreground
|
|
732
|
+
// instead traded that tint for the foreground's flat grey.
|
|
733
|
+
const pending = toHex(liftedToLuminance(background, target('pending')));
|
|
734
|
+
const successRgb = tintAtLuminance(background, successInk, target('success'));
|
|
735
|
+
const success = toHex(successRgb);
|
|
736
|
+
/**
|
|
737
|
+
* The alternating pair is a plain lightness step, and it has to stay one: two
|
|
738
|
+
* successive successful cards must be told apart, not two different states. It
|
|
739
|
+
* is derived from the success fill rather than from the background, because a
|
|
740
|
+
* success fill held at a fixed luminance no longer sits where the old background
|
|
741
|
+
* step assumed — measuring against the background made the pair collide and
|
|
742
|
+
* pushed the alternate surface to a brightness no card should reach.
|
|
743
|
+
*/
|
|
744
|
+
const alt = toHex(tintAtLuminance(background, successInk, target('success') * SYSTEM_ALT_STEP));
|
|
745
|
+
const errorCard = stateCard(error, target('error'));
|
|
746
|
+
const cardFills = [pending, success, alt, errorCard].map(hexToRgb).map(([r, g, b]) => ({ r, g, b }));
|
|
747
|
+
/**
|
|
748
|
+
* The contrast the shipped `dim` reached against the shipped cards.
|
|
749
|
+
*
|
|
750
|
+
* That is the hierarchy to reproduce: the live cards are derived here, so a
|
|
751
|
+
* fixed weight cannot know how far it may weaken the ink before it disappears.
|
|
752
|
+
* `dim` is the only secondary ink that needs solving — it sits closest to the
|
|
753
|
+
* surface, so it is the first to become unreadable there.
|
|
754
|
+
*/
|
|
755
|
+
const shippedDimContrast = () => {
|
|
756
|
+
const [r, g, b] = hexToRgb(base.dim);
|
|
757
|
+
const shippedCards = ['toolPendingBg', 'toolSuccessBg', 'toolAltBg', 'toolErrorBg']
|
|
758
|
+
.map(color => {
|
|
759
|
+
const [cr, cg, cb] = hexToRgb(base[color]);
|
|
760
|
+
return { r: cr, g: cg, b: cb };
|
|
761
|
+
});
|
|
762
|
+
return worstContrast({ r, g, b }, shippedCards);
|
|
763
|
+
};
|
|
764
|
+
return {
|
|
765
|
+
...base,
|
|
766
|
+
text: toHex(foreground),
|
|
767
|
+
/**
|
|
768
|
+
* `muted` is not derived. Both shipped palettes set it to the same swatch as
|
|
769
|
+
* `toolOutput`, and that swatch already clears its cards on either host;
|
|
770
|
+
* deriving it separately only produced a second, near-identical grey that
|
|
771
|
+
* sometimes landed on the wrong side of the ink it was meant to match.
|
|
772
|
+
*/
|
|
773
|
+
muted: base.muted,
|
|
774
|
+
dim: toHex(quietInk(foreground, background, cardFills, shippedDimContrast())),
|
|
775
|
+
userMessageBg: toHex(mixRgb(background, accent, 0.14)),
|
|
776
|
+
toolPendingBg: pending,
|
|
777
|
+
toolSuccessBg: success,
|
|
778
|
+
toolAltBg: alt,
|
|
779
|
+
toolErrorBg: errorCard,
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
/** 16-color SGR for one ANSI palette index, as ink or as a fill. */
|
|
783
|
+
function ansiIndexSgr(index, asBackground) {
|
|
784
|
+
const base = asBackground ? 4 : 3;
|
|
785
|
+
if (index < 8)
|
|
786
|
+
return `\x1b[${base}${index}m`;
|
|
787
|
+
const bright = asBackground ? 10 : 9;
|
|
788
|
+
return `\x1b[${bright}${index - 8}m`;
|
|
750
789
|
}
|
|
751
790
|
/**
|
|
752
791
|
* True when the environment asks for no color at all: a non-empty `NO_COLOR`
|
|
@@ -757,41 +796,99 @@ export function colorDisabledByEnv(env = process.env) {
|
|
|
757
796
|
return (env.NO_COLOR !== undefined && env.NO_COLOR !== '') || env.FORCE_COLOR === '0';
|
|
758
797
|
}
|
|
759
798
|
/**
|
|
760
|
-
* Detect
|
|
761
|
-
*
|
|
762
|
-
*
|
|
799
|
+
* Detect the color depth. `NO_COLOR` (any non-empty value) and `FORCE_COLOR=0`
|
|
800
|
+
* outrank every capability hint and leave 16 colors; `COLORTERM` and Windows
|
|
801
|
+
* Terminal prove 24-bit color. Apple Terminal advertises `xterm-256color` but
|
|
802
|
+
* misdraws 24-bit sequences on the releases still in use, so it gets the
|
|
803
|
+
* 256-color cube. Known 16-color hosts stay at 16, and every other terminal is
|
|
804
|
+
* assumed to be a modern 24-bit one.
|
|
763
805
|
*/
|
|
764
|
-
export function
|
|
806
|
+
export function detectColorDepth(env = process.env) {
|
|
765
807
|
if (colorDisabledByEnv(env))
|
|
766
|
-
return
|
|
767
|
-
const colorterm = env.COLORTERM ?? '';
|
|
808
|
+
return 'ansi16';
|
|
809
|
+
const colorterm = (env.COLORTERM ?? '').toLowerCase();
|
|
768
810
|
if (colorterm === 'truecolor' || colorterm === '24bit')
|
|
769
|
-
return
|
|
811
|
+
return 'truecolor';
|
|
770
812
|
if (env.WT_SESSION)
|
|
771
|
-
return
|
|
813
|
+
return 'truecolor';
|
|
772
814
|
const term = env.TERM ?? '';
|
|
773
815
|
if (term === 'dumb' || term === '' || term === 'linux')
|
|
774
|
-
return
|
|
775
|
-
|
|
816
|
+
return 'ansi16';
|
|
817
|
+
if (env.TERM_PROGRAM === 'Apple_Terminal') {
|
|
818
|
+
const build = Number.parseInt((env.TERM_PROGRAM_VERSION ?? '').split('.')[0] ?? '', 10);
|
|
819
|
+
return Number.isInteger(build) && build >= FIRST_TRUECOLOR_APPLE_TERMINAL_BUILD ? 'truecolor' : 'ansi256';
|
|
820
|
+
}
|
|
821
|
+
return 'truecolor';
|
|
776
822
|
}
|
|
823
|
+
/** Terminal 2.15, macOS 26 Tahoe, is the first Apple Terminal with real 24-bit color. */
|
|
824
|
+
const FIRST_TRUECOLOR_APPLE_TERMINAL_BUILD = 464;
|
|
777
825
|
/**
|
|
778
826
|
* Build a theme.
|
|
779
827
|
* @param colors - emit SGR when true.
|
|
780
|
-
* @param
|
|
828
|
+
* @param depth - color depth, or a boolean for 24-bit versus 16 colors; ignored when colors is false.
|
|
781
829
|
* @param name - shipped palette (`dark` default).
|
|
830
|
+
* @param terminal - what the terminal reported about itself, when a probe answered.
|
|
782
831
|
*/
|
|
783
|
-
export function createTheme(colors,
|
|
784
|
-
const
|
|
785
|
-
const
|
|
832
|
+
export function createTheme(colors, depth = detectColorDepth(), name = 'dark', terminal) {
|
|
833
|
+
const resolvedDepth = depth === true ? 'truecolor' : depth === false ? 'ansi16' : depth;
|
|
834
|
+
const tc = colors && resolvedDepth === 'truecolor';
|
|
835
|
+
const palette = name === 'system' ? systemPalette(terminal) : PALETTES[name];
|
|
786
836
|
const ansi = ANSI16[name];
|
|
787
837
|
const ansiBg = ANSI16_BG[name];
|
|
788
|
-
|
|
789
|
-
|
|
838
|
+
// The reported sixteen colors decide the encoding only on a 16-color
|
|
839
|
+
// terminal; 256- and 24-bit terminals encode the exact swatch themselves.
|
|
840
|
+
const reported = resolvedDepth === 'ansi16' && terminal?.palette !== undefined && terminal.palette.length === 16 ? terminal.palette : undefined;
|
|
841
|
+
/**
|
|
842
|
+
* Without 24-bit color a hex swatch is quantized to the entry the terminal
|
|
843
|
+
* actually has, which is perceptual nearest rather than a fixed code shift.
|
|
844
|
+
*/
|
|
845
|
+
const quantized = (swatch, asBackground) => {
|
|
846
|
+
if (reported === undefined || typeof swatch !== 'string' || swatch === '')
|
|
847
|
+
return undefined;
|
|
848
|
+
const [r, g, b] = hexToRgb(swatch);
|
|
849
|
+
const index = nearestPaletteIndex({ r, g, b }, reported);
|
|
850
|
+
return index === undefined ? undefined : ansiIndexSgr(index, asBackground);
|
|
851
|
+
};
|
|
852
|
+
const getFgAnsi = (color) => {
|
|
853
|
+
if (!colors)
|
|
854
|
+
return '';
|
|
855
|
+
return quantized(palette[color], false) ?? swatchCode(palette[color], resolvedDepth, ansi[color], 38);
|
|
856
|
+
};
|
|
857
|
+
const rawBgAnsi = (color) => {
|
|
858
|
+
if (!colors)
|
|
859
|
+
return '';
|
|
860
|
+
return quantized(palette[color], true) ?? swatchCode(palette[color], resolvedDepth, ansiBg[color], 48);
|
|
861
|
+
};
|
|
862
|
+
/**
|
|
863
|
+
* A tool card must keep telling pending from done from failed on every
|
|
864
|
+
* terminal, so the three fills are resolved together rather than one at a
|
|
865
|
+
* time.
|
|
866
|
+
*
|
|
867
|
+
* A palette with only sixteen backgrounds, or a 256-color cube with six
|
|
868
|
+
* levels per channel, has no entries that differ by the handful of units the
|
|
869
|
+
* truecolor swatches do: nearest-neighbour quantisation sent pending,
|
|
870
|
+
* success, and error to one and the same black. Every icon that says which
|
|
871
|
+
* state a card is in was therefore invisible on any terminal that was not
|
|
872
|
+
* truecolor.
|
|
873
|
+
*/
|
|
874
|
+
const toolFills = colors && !tc ? resolveToolFills(name, resolvedDepth, terminal) : undefined;
|
|
875
|
+
const fillsFor = (color) => toolFills !== undefined && Object.hasOwn(toolFills, color)
|
|
876
|
+
? toolFills[color]
|
|
877
|
+
: rawBgAnsi(color);
|
|
878
|
+
/**
|
|
879
|
+
* A 16- or 256-color terminal can still collapse the two *alternating* card
|
|
880
|
+
* surfaces onto one entry. The alternating card then gives up its fill instead
|
|
881
|
+
* of pretending to have a second one, which keeps consecutive calls readable.
|
|
882
|
+
*/
|
|
883
|
+
const altBg = colors && !tc && fillsFor('toolSuccessBg') === fillsFor('toolAltBg') ? BG_RESET : undefined;
|
|
884
|
+
const getBgAnsi = (color) => color === 'toolAltBg' && altBg !== undefined ? altBg : fillsFor(color);
|
|
790
885
|
const paint = (open, text, close) => colors && open !== '' ? open + text + close : text;
|
|
791
886
|
return {
|
|
792
887
|
name,
|
|
793
888
|
colors,
|
|
794
889
|
trueColor: tc,
|
|
890
|
+
depth: resolvedDepth,
|
|
891
|
+
key: [name, colors ? 1 : 0, resolvedDepth, terminalColorsKey(terminal)].join('|'),
|
|
795
892
|
getFgAnsi,
|
|
796
893
|
getBgAnsi,
|
|
797
894
|
fg: (color, text) => paint(getFgAnsi(color), text, FG_RESET),
|
|
@@ -814,13 +911,12 @@ export const DEEPSEEK_LOGO = [
|
|
|
814
911
|
' ⠉⠉⠉⠉⠉⠁ ',
|
|
815
912
|
];
|
|
816
913
|
const GRADIENT_STOPS = [
|
|
817
|
-
[
|
|
818
|
-
[
|
|
819
|
-
[
|
|
820
|
-
[
|
|
821
|
-
[120, 255, 220],
|
|
914
|
+
[47, 73, 224],
|
|
915
|
+
[77, 107, 254],
|
|
916
|
+
[53, 200, 222],
|
|
917
|
+
[124, 231, 242],
|
|
822
918
|
];
|
|
823
|
-
const GRADIENT_RAMP_256 = [
|
|
919
|
+
const GRADIENT_RAMP_256 = [63, 69, 75, 81, 45, 51, 87];
|
|
824
920
|
function gradientEscape(t, trueColor) {
|
|
825
921
|
if (trueColor) {
|
|
826
922
|
const seg = t * (GRADIENT_STOPS.length - 1);
|