@hncode/hncode 0.3.6 → 0.4.1
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/package.json +5 -3
- package/src/agent-task.js +154 -84
- package/src/agent.js +533 -145
- package/src/clipboard.js +5 -2
- package/src/colors.js +242 -238
- package/src/config.js +217 -17
- package/src/file-history.js +177 -0
- package/src/hooks.js +11 -1
- package/src/i18n.js +389 -0
- package/src/index.js +46 -3
- package/src/llm.js +139 -16
- package/src/permissions.js +120 -0
- package/src/session-hub.js +407 -0
- package/src/session.js +31 -0
- package/src/term.js +19 -3
- package/src/tool-result.js +236 -0
- package/src/tools/agent.js +11 -0
- package/src/tools/bash.js +84 -17
- package/src/tools/edit.js +192 -32
- package/src/tools/file-lines.js +5 -0
- package/src/tools/index.js +4 -0
- package/src/tools/memory.js +88 -0
- package/src/tools/read-media-file.js +85 -68
- package/src/tools/read.js +15 -1
- package/src/tools/utils.js +14 -0
- package/src/tools/write.js +35 -5
- package/src/tui.js +11665 -9179
- package/src/web-auth.js +98 -0
- package/src/web-daemon-cli.js +63 -0
- package/src/web-daemon-client.js +203 -0
- package/src/web-daemon.js +871 -0
- package/src/web-public/app.css +1509 -0
- package/src/web-public/app.js +2913 -0
- package/src/web-public/complete.js +154 -0
- package/src/web-public/fonts/JetBrainsMono-Bold.woff2 +0 -0
- package/src/web-public/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-100-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-101-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-102-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-103-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-104-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-105-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-106-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-107-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-108-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-109-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-110-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-111-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-112-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-113-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-114-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-115-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-116-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-117-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-118-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-119-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-21-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-22-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-23-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-24-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-25-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-26-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-27-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-28-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-29-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-30-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-31-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-32-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-33-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-34-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-35-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-36-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-37-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-38-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-39-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-4-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-40-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-41-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-42-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-43-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-44-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-45-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-46-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-47-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-48-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-49-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-5-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-50-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-51-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-52-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-53-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-54-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-55-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-56-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-57-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-58-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-59-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-6-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-60-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-61-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-62-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-63-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-64-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-65-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-66-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-67-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-68-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-69-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-70-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-71-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-72-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-73-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-74-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-75-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-76-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-77-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-78-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-79-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-80-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-81-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-82-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-83-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-84-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-85-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-86-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-87-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-88-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-89-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-90-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-91-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-97-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-98-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc-99-400-normal.woff2 +0 -0
- package/src/web-public/fonts/noto-sans-sc.css +775 -0
- package/src/web-public/fonts.css +31 -0
- package/src/web-public/home.css +222 -0
- package/src/web-public/home.html +33 -0
- package/src/web-public/home.js +160 -0
- package/src/web-public/i18n.js +983 -0
- package/src/web-public/index.html +196 -0
- package/src/web-public/login.css +123 -0
- package/src/web-public/login.html +25 -0
- package/src/web-public/login.js +46 -0
- package/src/web-public/settings.js +290 -0
- package/src/web-ws.js +226 -0
- package/src/web.js +380 -0
- /package/bin/{install.js → install.cjs} +0 -0
- /package/bin/{preinstall.js → preinstall.cjs} +0 -0
package/src/clipboard.js
CHANGED
|
@@ -27,8 +27,11 @@ export function copyText(text) {
|
|
|
27
27
|
if (!s) return false;
|
|
28
28
|
if (win()) {
|
|
29
29
|
// clip.exe takes UTF-16LE. (`cmd /c echo "…" | clip` mangled quotes, '%'
|
|
30
|
-
// and newlines; plain UTF-8 input turned non-ASCII into mojibake.)
|
|
31
|
-
|
|
30
|
+
// and newlines; plain UTF-8 input turned non-ASCII into mojibake.) Prefix the
|
|
31
|
+
// UTF-16 byte order mark (FF FE) so clip.exe is never left to guess the endian
|
|
32
|
+
// or treat input as ANSI — without it, non-ASCII on some Windows versions /
|
|
33
|
+
// code pages came out mojibake.
|
|
34
|
+
cp.execSync('clip', { input: Buffer.concat([Buffer.from([0xff, 0xfe]), Buffer.from(s, 'utf16le')]), windowsHide: true, timeout: 5000 });
|
|
32
35
|
return true;
|
|
33
36
|
}
|
|
34
37
|
const cmd = process.platform === 'darwin' ? 'pbcopy' : 'xclip -selection clipboard';
|
package/src/colors.js
CHANGED
|
@@ -1,238 +1,242 @@
|
|
|
1
|
-
// Cyan-blue visual theme for hncode. Adapts to the terminal's color depth:
|
|
2
|
-
// uses true-color when available, 256/16-color fallbacks otherwise.
|
|
3
|
-
|
|
4
|
-
function colorDepth() {
|
|
5
|
-
try { return process.stdout.getColorDepth?.() || 0; } catch { return 0; }
|
|
6
|
-
}
|
|
7
|
-
const DEPTH = colorDepth();
|
|
8
|
-
export const TRUECOLOR = DEPTH >= 24 || process.env.COLORTERM === 'truecolor' || /^xterm.*-256color$|.*-truecolor$/.test(process.env.TERM || '');
|
|
9
|
-
|
|
10
|
-
function rgb(r, g, b) { return TRUECOLOR ? `\x1b[38;2;${r};${g};${b}m` : `\x1b[38;5;${ansi256(r, g, b)}m`; }
|
|
11
|
-
function brgb(r, g, b) { return TRUECOLOR ? `\x1b[48;2;${r};${g};${b}m` : `\x1b[48;5;${ansi256(r, g, b)}m`; }
|
|
12
|
-
|
|
13
|
-
// Quantize a truecolor triple to the nearest 256-color xterm index.
|
|
14
|
-
function ansi256(r, g, b) {
|
|
15
|
-
const ri = Math.round(r / 51), gi = Math.round(g / 51), bi = Math.round(b / 51);
|
|
16
|
-
if (ri === gi && gi === bi) {
|
|
17
|
-
if (ri === 0) return 16;
|
|
18
|
-
if (ri === 255 / 51) return 232 + ri;
|
|
19
|
-
return 232 + ri; // grayscale ramp
|
|
20
|
-
}
|
|
21
|
-
return 16 + 36 * ri + 6 * gi + bi;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Brand: cyan-blue
|
|
25
|
-
export const TEAL = rgb(0, 184, 219);
|
|
26
|
-
export const CYAN = rgb(0, 215, 255);
|
|
27
|
-
export const BLUE = rgb(66, 135, 245);
|
|
28
|
-
export const FG = TEAL; // primary fg
|
|
29
|
-
export const BG = rgb(0, 18, 26);
|
|
30
|
-
export const BORDER = rgb(70, 130, 180);
|
|
31
|
-
|
|
32
|
-
// Named accent colours per theme. `auto` keeps the brand cyan-blue and lets the
|
|
33
|
-
// terminal's own palette show through for everything else; `dark`/`light` pick
|
|
34
|
-
// accent triples that stay readable on that background.
|
|
35
|
-
const THEMES = {
|
|
36
|
-
auto: {
|
|
37
|
-
fg: [0, 184, 219], bg: [0, 18, 26],
|
|
38
|
-
teal: [0, 184, 219], cyan: [0, 215, 255], blue: [66, 135, 245], border: [70, 130, 180],
|
|
39
|
-
hover: [190, 245, 255], selBg: [0, 70, 84]
|
|
40
|
-
},
|
|
41
|
-
dark: {
|
|
42
|
-
fg: [255, 255, 255], bg: [0, 18, 26], // White foreground for normal text
|
|
43
|
-
teal: [0, 184, 219], cyan: [0, 215, 255], blue: [66, 135, 245], border: [70, 130, 180],
|
|
44
|
-
hover: [190, 245, 255], selBg: [0, 70, 84]
|
|
45
|
-
},
|
|
46
|
-
light: {
|
|
47
|
-
fg: [26, 26, 26], bg: [255, 255, 255],
|
|
48
|
-
teal: [0, 102, 128], cyan: [0, 122, 153], blue: [10, 77, 158], border: [138, 155, 176],
|
|
49
|
-
hover: [0, 78, 102], selBg: [200, 230, 239],
|
|
50
|
-
red: [179, 38, 30], green: [30, 122, 60], yellow: [160, 120, 0], orange: [178, 90, 0],
|
|
51
|
-
white: [26, 26, 26], gray: [85, 85, 85],
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
export const THEME_NAMES = Object.keys(THEMES);
|
|
55
|
-
|
|
56
|
-
export const C = {
|
|
57
|
-
reset: '\x1b[0m',
|
|
58
|
-
bold: '\x1b[1m',
|
|
59
|
-
dim: '\x1b[2m',
|
|
60
|
-
// Italic (SGR 3). Used for the BODY of a reasoning block — the prose reads as
|
|
61
|
-
// an aside rather than as the answer. Must be closed with SGR 23 (`endItalic`),
|
|
62
|
-
// NOT with `reset`, because resetting also drops the surrounding dim/gray that
|
|
63
|
-
// the same row depends on. Terminals without italic fall back to normal text,
|
|
64
|
-
// so this is a decoration, never load-bearing.
|
|
65
|
-
italic: '\x1b[3m',
|
|
66
|
-
endItalic: '\x1b[23m',
|
|
67
|
-
muted: '\x1b[90m',
|
|
68
|
-
teal: TEAL,
|
|
69
|
-
cyan: CYAN,
|
|
70
|
-
blue: BLUE,
|
|
71
|
-
fg: FG,
|
|
72
|
-
bg: BG,
|
|
73
|
-
bgTeal: brgb(0, 184, 219),
|
|
74
|
-
bgDim: brgb(12, 24, 30),
|
|
75
|
-
bgPanel: TRUECOLOR ? '\x1b[48;5;236m' : '\x1b[48;5;236m',
|
|
76
|
-
border: BORDER,
|
|
77
|
-
red: '\x1b[91m',
|
|
78
|
-
green: '\x1b[92m',
|
|
79
|
-
yellow: '\x1b[93m',
|
|
80
|
-
magenta: '\x1b[95m',
|
|
81
|
-
gray: '\x1b[90m',
|
|
82
|
-
white: '\x1b[97m',
|
|
83
|
-
orange: TRUECOLOR ? rgb(255, 140, 0) : '\x1b[38;5;208m',
|
|
84
|
-
// Cyan pushed slightly toward green — used for the Swarm mode badge so it reads
|
|
85
|
-
// as its own mode next to Plan (pure cyan) and Focus (blue) while staying in the
|
|
86
|
-
// theme's blue-green family. 38;5;43 is rgb(0,215,175): red at zero and green
|
|
87
|
-
// only 40 above blue, i.e. still clearly cyan with a green cast. 38;5;85 was the
|
|
88
|
-
// first attempt and read as plain GREEN — its red channel is lifted to 95 and
|
|
89
|
-
// green pins at 255, which is a spring green, not a green-tinted cyan.
|
|
90
|
-
spring: TRUECOLOR ? rgb(0, 215, 175) : '\x1b[38;5;43m',
|
|
91
|
-
// Git branch in the status line: yellow pulled about half way toward white, so
|
|
92
|
-
// it stays clearly "yellow" (not cream/ivory) while reading brighter than the
|
|
93
|
-
// plain 93 yellow on a dark background. rgb(255,219,110) keeps red at max,
|
|
94
|
-
// green near it, and blue around 43% — that ratio is what keeps the hue
|
|
95
|
-
// yellow rather than dropping into orange.
|
|
96
|
-
branch: TRUECOLOR ? rgb(255, 219, 110) : '\x1b[38;5;222m',
|
|
97
|
-
// Shell-mode (`!`) accent — violet, matching kimi-code's shellMode token so the
|
|
98
|
-
// bash-mode editor border and the `!` read as "shell", distinct from the cyan
|
|
99
|
-
// prompt and the yellow git badge. #BD93F9 dark / #7C3AED light.
|
|
100
|
-
shellMode: TRUECOLOR ? rgb(189, 147, 249) : '\x1b[38;5;141m',
|
|
101
|
-
bgRed: '\x1b[41m',
|
|
102
|
-
bgGreen: '\x1b[42m',
|
|
103
|
-
// Selection highlight (mouse text selection) and the scrollbar gutter.
|
|
104
|
-
// Selection uses a dark teal background so white text stays readable.
|
|
105
|
-
selBg: TRUECOLOR ? brgb(0, 70, 84) : '\x1b[48;5;23m', // Original dark cyan background
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
//
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
//
|
|
117
|
-
//
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
//
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
C.
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
C.
|
|
155
|
-
C.
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (t.
|
|
180
|
-
if (t.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if (t.
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
export function
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
export function
|
|
221
|
-
export function
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
export function
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
export function
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
export function
|
|
234
|
-
return '
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
1
|
+
// Cyan-blue visual theme for hncode. Adapts to the terminal's color depth:
|
|
2
|
+
// uses true-color when available, 256/16-color fallbacks otherwise.
|
|
3
|
+
|
|
4
|
+
function colorDepth() {
|
|
5
|
+
try { return process.stdout.getColorDepth?.() || 0; } catch { return 0; }
|
|
6
|
+
}
|
|
7
|
+
const DEPTH = colorDepth();
|
|
8
|
+
export const TRUECOLOR = DEPTH >= 24 || process.env.COLORTERM === 'truecolor' || /^xterm.*-256color$|.*-truecolor$/.test(process.env.TERM || '');
|
|
9
|
+
|
|
10
|
+
function rgb(r, g, b) { return TRUECOLOR ? `\x1b[38;2;${r};${g};${b}m` : `\x1b[38;5;${ansi256(r, g, b)}m`; }
|
|
11
|
+
function brgb(r, g, b) { return TRUECOLOR ? `\x1b[48;2;${r};${g};${b}m` : `\x1b[48;5;${ansi256(r, g, b)}m`; }
|
|
12
|
+
|
|
13
|
+
// Quantize a truecolor triple to the nearest 256-color xterm index.
|
|
14
|
+
function ansi256(r, g, b) {
|
|
15
|
+
const ri = Math.round(r / 51), gi = Math.round(g / 51), bi = Math.round(b / 51);
|
|
16
|
+
if (ri === gi && gi === bi) {
|
|
17
|
+
if (ri === 0) return 16;
|
|
18
|
+
if (ri === 255 / 51) return 232 + ri;
|
|
19
|
+
return 232 + ri; // grayscale ramp
|
|
20
|
+
}
|
|
21
|
+
return 16 + 36 * ri + 6 * gi + bi;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Brand accent: cyan-blue.
|
|
25
|
+
export const TEAL = rgb(0, 184, 219);
|
|
26
|
+
export const CYAN = rgb(0, 215, 255);
|
|
27
|
+
export const BLUE = rgb(66, 135, 245);
|
|
28
|
+
export const FG = TEAL; // primary fg
|
|
29
|
+
export const BG = rgb(0, 18, 26);
|
|
30
|
+
export const BORDER = rgb(70, 130, 180);
|
|
31
|
+
|
|
32
|
+
// Named accent colours per theme. `auto` keeps the brand cyan-blue and lets the
|
|
33
|
+
// terminal's own palette show through for everything else; `dark`/`light` pick
|
|
34
|
+
// accent triples that stay readable on that background.
|
|
35
|
+
const THEMES = {
|
|
36
|
+
auto: {
|
|
37
|
+
fg: [0, 184, 219], bg: [0, 18, 26],
|
|
38
|
+
teal: [0, 184, 219], cyan: [0, 215, 255], blue: [66, 135, 245], border: [70, 130, 180],
|
|
39
|
+
hover: [190, 245, 255], selBg: [0, 70, 84]
|
|
40
|
+
},
|
|
41
|
+
dark: {
|
|
42
|
+
fg: [255, 255, 255], bg: [0, 18, 26], // White foreground for normal text
|
|
43
|
+
teal: [0, 184, 219], cyan: [0, 215, 255], blue: [66, 135, 245], border: [70, 130, 180],
|
|
44
|
+
hover: [190, 245, 255], selBg: [0, 70, 84]
|
|
45
|
+
},
|
|
46
|
+
light: {
|
|
47
|
+
fg: [26, 26, 26], bg: [255, 255, 255],
|
|
48
|
+
teal: [0, 102, 128], cyan: [0, 122, 153], blue: [10, 77, 158], border: [138, 155, 176],
|
|
49
|
+
hover: [0, 78, 102], selBg: [200, 230, 239],
|
|
50
|
+
red: [179, 38, 30], green: [30, 122, 60], yellow: [160, 120, 0], orange: [178, 90, 0],
|
|
51
|
+
white: [26, 26, 26], gray: [85, 85, 85],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
export const THEME_NAMES = Object.keys(THEMES);
|
|
55
|
+
|
|
56
|
+
export const C = {
|
|
57
|
+
reset: '\x1b[0m',
|
|
58
|
+
bold: '\x1b[1m',
|
|
59
|
+
dim: '\x1b[2m',
|
|
60
|
+
// Italic (SGR 3). Used for the BODY of a reasoning block — the prose reads as
|
|
61
|
+
// an aside rather than as the answer. Must be closed with SGR 23 (`endItalic`),
|
|
62
|
+
// NOT with `reset`, because resetting also drops the surrounding dim/gray that
|
|
63
|
+
// the same row depends on. Terminals without italic fall back to normal text,
|
|
64
|
+
// so this is a decoration, never load-bearing.
|
|
65
|
+
italic: '\x1b[3m',
|
|
66
|
+
endItalic: '\x1b[23m',
|
|
67
|
+
muted: '\x1b[90m',
|
|
68
|
+
teal: TEAL,
|
|
69
|
+
cyan: CYAN,
|
|
70
|
+
blue: BLUE,
|
|
71
|
+
fg: FG,
|
|
72
|
+
bg: BG,
|
|
73
|
+
bgTeal: brgb(0, 184, 219),
|
|
74
|
+
bgDim: brgb(12, 24, 30),
|
|
75
|
+
bgPanel: TRUECOLOR ? '\x1b[48;5;236m' : '\x1b[48;5;236m',
|
|
76
|
+
border: BORDER,
|
|
77
|
+
red: '\x1b[91m',
|
|
78
|
+
green: '\x1b[92m',
|
|
79
|
+
yellow: '\x1b[93m',
|
|
80
|
+
magenta: '\x1b[95m',
|
|
81
|
+
gray: '\x1b[90m',
|
|
82
|
+
white: '\x1b[97m',
|
|
83
|
+
orange: TRUECOLOR ? rgb(255, 140, 0) : '\x1b[38;5;208m',
|
|
84
|
+
// Cyan pushed slightly toward green — used for the Swarm mode badge so it reads
|
|
85
|
+
// as its own mode next to Plan (pure cyan) and Focus (blue) while staying in the
|
|
86
|
+
// theme's blue-green family. 38;5;43 is rgb(0,215,175): red at zero and green
|
|
87
|
+
// only 40 above blue, i.e. still clearly cyan with a green cast. 38;5;85 was the
|
|
88
|
+
// first attempt and read as plain GREEN — its red channel is lifted to 95 and
|
|
89
|
+
// green pins at 255, which is a spring green, not a green-tinted cyan.
|
|
90
|
+
spring: TRUECOLOR ? rgb(0, 215, 175) : '\x1b[38;5;43m',
|
|
91
|
+
// Git branch in the status line: yellow pulled about half way toward white, so
|
|
92
|
+
// it stays clearly "yellow" (not cream/ivory) while reading brighter than the
|
|
93
|
+
// plain 93 yellow on a dark background. rgb(255,219,110) keeps red at max,
|
|
94
|
+
// green near it, and blue around 43% — that ratio is what keeps the hue
|
|
95
|
+
// yellow rather than dropping into orange.
|
|
96
|
+
branch: TRUECOLOR ? rgb(255, 219, 110) : '\x1b[38;5;222m',
|
|
97
|
+
// Shell-mode (`!`) accent — violet, matching kimi-code's shellMode token so the
|
|
98
|
+
// bash-mode editor border and the `!` read as "shell", distinct from the cyan
|
|
99
|
+
// prompt and the yellow git badge. #BD93F9 dark / #7C3AED light.
|
|
100
|
+
shellMode: TRUECOLOR ? rgb(189, 147, 249) : '\x1b[38;5;141m',
|
|
101
|
+
bgRed: '\x1b[41m',
|
|
102
|
+
bgGreen: '\x1b[42m',
|
|
103
|
+
// Selection highlight (mouse text selection) and the scrollbar gutter.
|
|
104
|
+
// Selection uses a dark teal background so white text stays readable.
|
|
105
|
+
selBg: TRUECOLOR ? brgb(0, 70, 84) : '\x1b[48;5;23m', // Original dark cyan background
|
|
106
|
+
// Reset ONLY the background colour, keeping the current foreground. Used to end
|
|
107
|
+
// a selection highlight so text after it keeps its own colour (a bare ESC[0m
|
|
108
|
+
// would also reset the foreground, turning a cyan/white run grey).
|
|
109
|
+
bgReset: '\x1b[49m',
|
|
110
|
+
scrollTrack: '\x1b[90m', // dim gutter
|
|
111
|
+
scrollThumb: TRUECOLOR ? rgb(0, 184, 219) : '\x1b[38;5;37m', // brand cyan-blue
|
|
112
|
+
// Scrollbar interaction states:
|
|
113
|
+
// hover -> a touch whiter/brighter than the brand colour
|
|
114
|
+
// active -> BOLD + the brand colour (pressed/dragging)
|
|
115
|
+
// The pressed state used to be a DARKER colour, which on a dark background
|
|
116
|
+
// read as "the thumb disappeared". Making it bold-and-bright means a press is
|
|
117
|
+
// always unmistakably visible. Idle swaps back to scrollThumb automatically.
|
|
118
|
+
scrollThumbHover: TRUECOLOR ? rgb(120, 226, 245) : '\x1b[38;5;123m',
|
|
119
|
+
scrollThumbActive: '\x1b[1m' + (TRUECOLOR ? rgb(0, 220, 255) : '\x1b[38;5;51m'),
|
|
120
|
+
// Hover highlight for clickable rows (menu items, picker options, form
|
|
121
|
+
// fields, composer rows). An explicit bright foreground + BOLD rather than a
|
|
122
|
+
// bare BOLD: BOLD only brightens the 16-colour palette (it does nothing to a
|
|
123
|
+
// true-color row), which is why hover used to be invisible on most rows.
|
|
124
|
+
// tintRange() re-applies this so the row's own SGR codes cannot cancel it.
|
|
125
|
+
hover: '\x1b[1m' + rgb(190, 245, 255),
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
export function style(text, ...codes) {
|
|
129
|
+
return codes.join('') + text + '\x1b[0m';
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Linearly interpolate between two RGB triples and return an ANSI colour string.
|
|
133
|
+
// Used for the pulsing Working indicator (orange pulses to a brighter/darker
|
|
134
|
+
// shade over a 2s cycle).
|
|
135
|
+
export function lerpColor(r1, g1, b1, r2, g2, b2, t) {
|
|
136
|
+
const ti = Math.max(0, Math.min(1, t));
|
|
137
|
+
const r = Math.round(r1 + (r2 - r1) * ti);
|
|
138
|
+
const g = Math.round(g1 + (g2 - g1) * ti);
|
|
139
|
+
const b = Math.round(b1 + (b2 - b1) * ti);
|
|
140
|
+
return rgb(r, g, b);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Apply a named theme IN PLACE. `C` is a module-level object and ESM imports are
|
|
144
|
+
// live bindings, so mutating its properties here changes the colours everywhere
|
|
145
|
+
// without re-importing. Returns true when the name was recognised.
|
|
146
|
+
export function setTheme(name) {
|
|
147
|
+
const t = THEMES[name];
|
|
148
|
+
if (!t) return false;
|
|
149
|
+
|
|
150
|
+
// Update foreground colors
|
|
151
|
+
C.teal = rgb(...t.teal);
|
|
152
|
+
C.fg = rgb(...t.fg);
|
|
153
|
+
C.cyan = rgb(...t.cyan);
|
|
154
|
+
C.blue = rgb(...t.blue);
|
|
155
|
+
C.border = rgb(...t.border);
|
|
156
|
+
|
|
157
|
+
// Update background colors
|
|
158
|
+
C.bg = brgb(...t.bg);
|
|
159
|
+
C.bgTeal = brgb(...t.teal);
|
|
160
|
+
C.bgPanel = brgb(...t.bg);
|
|
161
|
+
C.selBg = brgb(...t.selBg);
|
|
162
|
+
|
|
163
|
+
// Scrollbar colours. The active (pressed/dragging) thumb is BOLD + a brighter
|
|
164
|
+
// shade of the theme colour. It used to be a DARKER shade, which on a dark
|
|
165
|
+
// background read as "the thumb vanished" mid-drag.
|
|
166
|
+
C.scrollTrack = '\x1b[90m'; // dim gutter
|
|
167
|
+
C.scrollThumb = rgb(...t.teal);
|
|
168
|
+
C.scrollThumbHover = rgb(...t.hover);
|
|
169
|
+
C.scrollThumbActive = '\x1b[1m' + rgb(
|
|
170
|
+
Math.min(255, Math.round(t.teal[0] * 0.6 + 255 * 0.4)),
|
|
171
|
+
Math.min(255, Math.round(t.teal[1] * 0.6 + 255 * 0.4)),
|
|
172
|
+
Math.min(255, Math.round(t.teal[2] * 0.6 + 255 * 0.4)),
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Hover stays visible on this theme's background (bright on dark, dark on light).
|
|
176
|
+
C.hover = '\x1b[1m' + rgb(...(t.hover || [190, 245, 255]));
|
|
177
|
+
|
|
178
|
+
// Semantic colors — swap to deep versions on light backgrounds.
|
|
179
|
+
if (t.white) C.white = rgb(...t.white);
|
|
180
|
+
if (t.gray) C.gray = rgb(...t.gray);
|
|
181
|
+
if (t.red) C.red = rgb(...t.red);
|
|
182
|
+
if (t.green) C.green = rgb(...t.green);
|
|
183
|
+
if (t.yellow) C.yellow = rgb(...t.yellow);
|
|
184
|
+
if (t.orange) C.orange = rgb(...t.orange);
|
|
185
|
+
// The git branch colour is derived per theme: the pale yellow that reads well
|
|
186
|
+
// on the dark background is nearly invisible on white, so on a light theme it
|
|
187
|
+
// is darkened (keeping the same hue) instead of being left as-is.
|
|
188
|
+
if (t.bg && t.bg[0] > 128) {
|
|
189
|
+
C.branch = rgb(146, 106, 0); // dark amber for light backgrounds
|
|
190
|
+
} else {
|
|
191
|
+
C.branch = TRUECOLOR ? rgb(255, 219, 110) : '\x1b[38;5;222m';
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function clearScreen() {
|
|
198
|
+
return '\x1b[3J\x1b[H\x1b[2J';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Clear screen and set background color based on current theme
|
|
202
|
+
export function clearAndSetBg() {
|
|
203
|
+
// Reset all attributes first
|
|
204
|
+
const reset = '\x1b[0m';
|
|
205
|
+
// Clear screen
|
|
206
|
+
const clear = '\x1b[3J\x1b[H\x1b[2J';
|
|
207
|
+
// Set background color using current C.bg value
|
|
208
|
+
const bg = C.bg || '\x1b[48;2;0;18;26m'; // Default dark background
|
|
209
|
+
return reset + clear + bg;
|
|
210
|
+
}
|
|
211
|
+
export function eraseLine() {
|
|
212
|
+
return '\x1b[2K';
|
|
213
|
+
}
|
|
214
|
+
export function eraseScreen() {
|
|
215
|
+
return '\x1b[2J';
|
|
216
|
+
}
|
|
217
|
+
export function moveTo(r, c) {
|
|
218
|
+
return `\x1b[${r + 1};${c + 1}H`;
|
|
219
|
+
}
|
|
220
|
+
export function cursorUp(n) { return `\x1b[${n}A`; }
|
|
221
|
+
export function cursorDown(n) { return `\x1b[${n}B`; }
|
|
222
|
+
export function cursorLeft(n) { return `\x1b[${n}D`; }
|
|
223
|
+
export function cursorForward(n) { return `\x1b[${n}C`; }
|
|
224
|
+
export function hideCursor() { return '\x1b[?25l'; }
|
|
225
|
+
export function showCursor() { return '\x1b[?25h'; }
|
|
226
|
+
// Cursor shape: terminals accept DECSCUSR — 2 = block, 4 = underline, 6 = bar(I-beam);
|
|
227
|
+
// 1/3/5 = blinking variants. Force a solid block cursor so positioning is always exact
|
|
228
|
+
// and unaffected by I-beam alignment.
|
|
229
|
+
export function blockCursor() { return '\x1b[2 q'; }
|
|
230
|
+
export function underlineCursor() { return '\x1b[4 q'; }
|
|
231
|
+
export function barCursor() { return '\x1b[6 q'; }
|
|
232
|
+
export function alternateScreen(on) { return on ? '\x1b[?1049h' : '\x1b[?1049l'; }
|
|
233
|
+
export function setScrollRegion(top, bottom) {
|
|
234
|
+
if (process.platform === 'win32') return ''; // win TTY scroll regions are unreliable
|
|
235
|
+
return `\x1b[${top + 1};${bottom + 1}r`;
|
|
236
|
+
}
|
|
237
|
+
export function resetScrollRegion() {
|
|
238
|
+
return '\x1b[r';
|
|
239
|
+
}
|
|
240
|
+
export function setBracketed(on) {
|
|
241
|
+
return `\x1b[?2004h${on ? '' : ''}`; // placeholder; real impl toggles bracketed paste
|
|
242
|
+
}
|