@hncode/hncode 0.5.0 → 0.5.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 +1 -1
- package/src/agent.js +4 -2
- package/src/colors.js +478 -387
- package/src/config.js +38 -11
- package/src/doctor.js +209 -11
- package/src/memory-profile.js +247 -0
- package/src/row-cache.js +147 -142
- package/src/session-hub.js +1 -0
- package/src/settings-schema.js +301 -0
- package/src/tool-result.js +264 -236
- package/src/tools/edit.js +1 -1
- package/src/tui.js +544 -80
- package/src/web.js +3 -1
package/src/colors.js
CHANGED
|
@@ -1,387 +1,478 @@
|
|
|
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-colour xterm index.
|
|
14
|
-
//
|
|
15
|
-
// Two different palettes live in xterm-256 and they must not be confused:
|
|
16
|
-
// * the 6x6x6 COLOUR CUBE (16..231): each channel has 6 levels — 0, then
|
|
17
|
-
// 95,135,175,215,255 — so the step is 40 above a floor of 35, not 51;
|
|
18
|
-
// * the GREY RAMP (232..255): 24 levels from rgb(8) to rgb(238), steps of 10.
|
|
19
|
-
//
|
|
20
|
-
// The grey case used to be `232 + Math.round(v / 51)`, which collapses a whole range of
|
|
21
|
-
// greys onto a few ramp entries: rgb(46,46,46) — the panel surface — quantised to 233,
|
|
22
|
-
// i.e. rgb(18,18,18), three steps darker than asked for. The panel then read as a
|
|
23
|
-
// near-black hole instead of a raised surface. Quantising against the ramp's own step
|
|
24
|
-
// fixes that.
|
|
25
|
-
function ansi256(r, g, b) {
|
|
26
|
-
const cube = (v) => (v < 48 ? 0 : v < 115 ? 1 : Math.min(5, Math.round((v - 35) / 40)));
|
|
27
|
-
// A triple that is (nearly) neutral belongs on the grey ramp, which has finer steps
|
|
28
|
-
// than the cube's 6 levels: rgb(46,46,46) is not representable in the cube at all.
|
|
29
|
-
const max = Math.max(r, g, b);
|
|
30
|
-
const min = Math.min(r, g, b);
|
|
31
|
-
if (max - min <= 8) {
|
|
32
|
-
const v = (r + g + b) / 3;
|
|
33
|
-
const n = Math.max(0, Math.min(23, Math.round((v - 8) / 10)));
|
|
34
|
-
return 232 + n;
|
|
35
|
-
}
|
|
36
|
-
return 16 + 36 * cube(r) + 6 * cube(g) + cube(b);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Brand accent: cyan-blue.
|
|
40
|
-
// A BACKGROUND escape at the terminal's own colour depth: the exact triple under
|
|
41
|
-
// truecolor, the nearest palette index otherwise.
|
|
42
|
-
//
|
|
43
|
-
// Exported because a caller computing a NEW colour at paint time — the backdrop dimming
|
|
44
|
-
// does, when it pulls a fill toward the page — has to state its result in the same notation
|
|
45
|
-
// the untouched fills use. Emitting `48;2;…` unconditionally loses the fill entirely on a
|
|
46
|
-
// 256-colour terminal, which silently ignores what it does not implement.
|
|
47
|
-
export const bgRgb = brgb;
|
|
48
|
-
|
|
49
|
-
export const TEAL = rgb(0, 184, 219);
|
|
50
|
-
export const CYAN = rgb(0, 215, 255);
|
|
51
|
-
export const BLUE = rgb(66, 135, 245);
|
|
52
|
-
export const FG = TEAL; // primary fg
|
|
53
|
-
export const BG = rgb(0, 18, 26);
|
|
54
|
-
export const BORDER = rgb(70, 130, 180);
|
|
55
|
-
|
|
56
|
-
// Named accent colours per theme. `auto` keeps the brand cyan-blue and lets the
|
|
57
|
-
// terminal's own palette show through for everything else; `dark`/`light` pick
|
|
58
|
-
// accent triples that stay readable on that background.
|
|
59
|
-
const THEMES = {
|
|
60
|
-
auto:
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
//
|
|
223
|
-
//
|
|
224
|
-
|
|
225
|
-
//
|
|
226
|
-
//
|
|
227
|
-
//
|
|
228
|
-
//
|
|
229
|
-
//
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
//
|
|
244
|
-
//
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
//
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
//
|
|
263
|
-
|
|
264
|
-
//
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
//
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
//
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
//
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
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-colour xterm index.
|
|
14
|
+
//
|
|
15
|
+
// Two different palettes live in xterm-256 and they must not be confused:
|
|
16
|
+
// * the 6x6x6 COLOUR CUBE (16..231): each channel has 6 levels — 0, then
|
|
17
|
+
// 95,135,175,215,255 — so the step is 40 above a floor of 35, not 51;
|
|
18
|
+
// * the GREY RAMP (232..255): 24 levels from rgb(8) to rgb(238), steps of 10.
|
|
19
|
+
//
|
|
20
|
+
// The grey case used to be `232 + Math.round(v / 51)`, which collapses a whole range of
|
|
21
|
+
// greys onto a few ramp entries: rgb(46,46,46) — the panel surface — quantised to 233,
|
|
22
|
+
// i.e. rgb(18,18,18), three steps darker than asked for. The panel then read as a
|
|
23
|
+
// near-black hole instead of a raised surface. Quantising against the ramp's own step
|
|
24
|
+
// fixes that.
|
|
25
|
+
function ansi256(r, g, b) {
|
|
26
|
+
const cube = (v) => (v < 48 ? 0 : v < 115 ? 1 : Math.min(5, Math.round((v - 35) / 40)));
|
|
27
|
+
// A triple that is (nearly) neutral belongs on the grey ramp, which has finer steps
|
|
28
|
+
// than the cube's 6 levels: rgb(46,46,46) is not representable in the cube at all.
|
|
29
|
+
const max = Math.max(r, g, b);
|
|
30
|
+
const min = Math.min(r, g, b);
|
|
31
|
+
if (max - min <= 8) {
|
|
32
|
+
const v = (r + g + b) / 3;
|
|
33
|
+
const n = Math.max(0, Math.min(23, Math.round((v - 8) / 10)));
|
|
34
|
+
return 232 + n;
|
|
35
|
+
}
|
|
36
|
+
return 16 + 36 * cube(r) + 6 * cube(g) + cube(b);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Brand accent: cyan-blue.
|
|
40
|
+
// A BACKGROUND escape at the terminal's own colour depth: the exact triple under
|
|
41
|
+
// truecolor, the nearest palette index otherwise.
|
|
42
|
+
//
|
|
43
|
+
// Exported because a caller computing a NEW colour at paint time — the backdrop dimming
|
|
44
|
+
// does, when it pulls a fill toward the page — has to state its result in the same notation
|
|
45
|
+
// the untouched fills use. Emitting `48;2;…` unconditionally loses the fill entirely on a
|
|
46
|
+
// 256-colour terminal, which silently ignores what it does not implement.
|
|
47
|
+
export const bgRgb = brgb;
|
|
48
|
+
|
|
49
|
+
export const TEAL = rgb(0, 184, 219);
|
|
50
|
+
export const CYAN = rgb(0, 215, 255);
|
|
51
|
+
export const BLUE = rgb(66, 135, 245);
|
|
52
|
+
export const FG = TEAL; // primary fg
|
|
53
|
+
export const BG = rgb(0, 18, 26);
|
|
54
|
+
export const BORDER = rgb(70, 130, 180);
|
|
55
|
+
|
|
56
|
+
// Named accent colours per theme. `auto` keeps the brand cyan-blue and lets the
|
|
57
|
+
// terminal's own palette show through for everything else; `dark`/`light` pick
|
|
58
|
+
// accent triples that stay readable on that background.
|
|
59
|
+
const THEMES = {
|
|
60
|
+
// `auto` is deliberately NOT a table here: it is a CHOICE, resolved by `setTheme` into
|
|
61
|
+
// `light` or `dark` from the terminal's background. A table named `auto` shadows that,
|
|
62
|
+
// and did — it was a fixed dark palette with brand-cyan text, while its own label claimed
|
|
63
|
+
// it followed the terminal.
|
|
64
|
+
dark: {
|
|
65
|
+
fg: [255, 255, 255], bg: [0, 18, 26], // White foreground for normal text
|
|
66
|
+
fg: [255, 255, 255], bg: [0, 18, 26], // White foreground for normal text
|
|
67
|
+
teal: [0, 184, 219], cyan: [0, 215, 255], blue: [66, 135, 245], border: [70, 130, 180],
|
|
68
|
+
// The semantic colours. They were missing, and `setTheme` guards each assignment, so
|
|
69
|
+
// they kept the previous theme's values — a theme that only looks right after a
|
|
70
|
+
// particular one was applied.
|
|
71
|
+
red: [230, 80, 80], green: [120, 200, 120], yellow: [230, 200, 100], orange: [240, 160, 80],
|
|
72
|
+
white: [255, 255, 255], gray: [130, 140, 150],
|
|
73
|
+
hover: [190, 245, 255], selBg: [0, 70, 84],
|
|
74
|
+
surface: [46, 46, 46],
|
|
75
|
+
},
|
|
76
|
+
light: {
|
|
77
|
+
fg: [26, 26, 26], bg: [255, 255, 255],
|
|
78
|
+
teal: [0, 102, 128], cyan: [0, 122, 153], blue: [10, 77, 158], border: [138, 155, 176],
|
|
79
|
+
hover: [0, 78, 102], selBg: [200, 230, 239],
|
|
80
|
+
red: [179, 38, 30], green: [30, 122, 60], yellow: [160, 120, 0], orange: [178, 90, 0],
|
|
81
|
+
white: [26, 26, 26], gray: [85, 85, 85],
|
|
82
|
+
// A LIGHT surface is a grey that is darker than the white page but far lighter than
|
|
83
|
+
// the dark themes' — a panel has to read as raised in whichever direction the theme
|
|
84
|
+
// goes, and reusing #2e2e2e on white would be a black box.
|
|
85
|
+
surface: [232, 232, 232],
|
|
86
|
+
},
|
|
87
|
+
// The four below are the same SHAPE as `dark` with the accent hue moved, so every
|
|
88
|
+
// derived colour in `setTheme` (the scrollbar shades, hover, and the light/dark branch
|
|
89
|
+
// test) keeps working with no special case. Each keeps ONE accent family varying only in
|
|
90
|
+
// lightness, because the context bar's five fills read as a gradient and a second hue in
|
|
91
|
+
// that ramp makes the bar look broken.
|
|
92
|
+
nord: {
|
|
93
|
+
fg: [216, 222, 233], bg: [46, 52, 64],
|
|
94
|
+
teal: [136, 192, 208], cyan: [143, 188, 187], blue: [129, 161, 193], border: [94, 129, 172],
|
|
95
|
+
hover: [236, 239, 244], selBg: [59, 66, 82],
|
|
96
|
+
red: [191, 97, 106], green: [163, 190, 140], yellow: [235, 203, 139], orange: [208, 135, 112],
|
|
97
|
+
white: [236, 239, 244], gray: [129, 135, 148],
|
|
98
|
+
surface: [59, 66, 82],
|
|
99
|
+
},
|
|
100
|
+
dracula: {
|
|
101
|
+
fg: [248, 248, 242], bg: [40, 42, 54],
|
|
102
|
+
teal: [139, 233, 253], cyan: [139, 233, 253], blue: [98, 114, 164], border: [98, 114, 164],
|
|
103
|
+
hover: [255, 121, 198], selBg: [68, 71, 90],
|
|
104
|
+
red: [255, 85, 85], green: [80, 250, 123], yellow: [241, 250, 140], orange: [255, 184, 108],
|
|
105
|
+
white: [248, 248, 242], gray: [98, 114, 164],
|
|
106
|
+
surface: [68, 71, 90],
|
|
107
|
+
},
|
|
108
|
+
gruvbox: {
|
|
109
|
+
fg: [235, 219, 178], bg: [40, 40, 40],
|
|
110
|
+
teal: [142, 192, 124], cyan: [131, 165, 152], blue: [69, 133, 136], border: [146, 131, 116],
|
|
111
|
+
hover: [250, 189, 47], selBg: [60, 56, 54],
|
|
112
|
+
red: [251, 73, 52], green: [184, 187, 38], yellow: [250, 189, 47], orange: [254, 128, 25],
|
|
113
|
+
white: [235, 219, 178], gray: [146, 131, 116],
|
|
114
|
+
surface: [60, 56, 54],
|
|
115
|
+
},
|
|
116
|
+
mono: {
|
|
117
|
+
// No hue at all: every accent is a grey, so the UI is readable on a monochrome
|
|
118
|
+
// terminal or for anyone who needs colour off without losing the structure that
|
|
119
|
+
// colour was carrying. The lightness steps are what keep the context bar legible.
|
|
120
|
+
fg: [230, 230, 230], bg: [18, 18, 18],
|
|
121
|
+
teal: [190, 190, 190], cyan: [210, 210, 210], blue: [150, 150, 150], border: [110, 110, 110],
|
|
122
|
+
hover: [255, 255, 255], selBg: [60, 60, 60],
|
|
123
|
+
red: [200, 90, 90], green: [140, 190, 140], yellow: [200, 190, 120], orange: [210, 160, 110],
|
|
124
|
+
white: [240, 240, 240], gray: [130, 130, 130],
|
|
125
|
+
surface: [42, 42, 42],
|
|
126
|
+
},
|
|
127
|
+
catppuccin: {
|
|
128
|
+
// Mocha: a warm-tinted dark with pastel accents. Every accent is desaturated toward
|
|
129
|
+
// the background so long replies do not glare, which is the whole point of the palette.
|
|
130
|
+
fg: [205, 214, 244], bg: [30, 30, 46],
|
|
131
|
+
teal: [137, 180, 250], cyan: [137, 220, 235], blue: [137, 180, 250], border: [108, 112, 134],
|
|
132
|
+
hover: [203, 166, 247], selBg: [49, 50, 68],
|
|
133
|
+
red: [243, 139, 168], green: [166, 227, 161], yellow: [249, 226, 175], orange: [250, 179, 135],
|
|
134
|
+
white: [205, 214, 244], gray: [127, 132, 156],
|
|
135
|
+
surface: [49, 50, 68],
|
|
136
|
+
},
|
|
137
|
+
tokyo: {
|
|
138
|
+
// Night: a blue-black with high-contrast accents. Distinct from nord, which is greyed
|
|
139
|
+
// out; this keeps the blue in the background and lets the cyan carry the structure.
|
|
140
|
+
fg: [192, 202, 245], bg: [26, 27, 38],
|
|
141
|
+
teal: [125, 207, 255], cyan: [125, 207, 255], blue: [122, 162, 247], border: [86, 95, 137],
|
|
142
|
+
hover: [187, 154, 247], selBg: [41, 46, 66],
|
|
143
|
+
red: [247, 118, 142], green: [158, 206, 106], yellow: [224, 175, 104], orange: [255, 158, 100],
|
|
144
|
+
white: [192, 202, 245], gray: [86, 95, 137],
|
|
145
|
+
surface: [41, 46, 66],
|
|
146
|
+
},
|
|
147
|
+
solar: {
|
|
148
|
+
// Solarized LIGHT. Included because `light` is a plain neutral and solarized's even
|
|
149
|
+
// hue ramp is the best-tested light palette in the terminal world. The dark variant is
|
|
150
|
+
// `solar-dark`, so both exist; the light one is the one worth having.
|
|
151
|
+
fg: [101, 123, 131], bg: [253, 246, 227],
|
|
152
|
+
teal: [38, 139, 210], cyan: [38, 139, 210], blue: [108, 113, 196], border: [147, 161, 161],
|
|
153
|
+
hover: [211, 54, 130], selBg: [238, 232, 213],
|
|
154
|
+
red: [220, 50, 47], green: [133, 153, 0], yellow: [181, 137, 0], orange: [203, 75, 22],
|
|
155
|
+
white: [253, 246, 227], gray: [147, 161, 161],
|
|
156
|
+
surface: [238, 232, 213],
|
|
157
|
+
},
|
|
158
|
+
rose: {
|
|
159
|
+
// Pine: a muted mauve dark, between dracula's saturation and nord's restraint.
|
|
160
|
+
fg: [224, 222, 244], bg: [25, 23, 36],
|
|
161
|
+
teal: [196, 167, 231], cyan: [137, 180, 250], blue: [137, 180, 250], border: [110, 106, 134],
|
|
162
|
+
hover: [196, 167, 231], selBg: [31, 29, 46],
|
|
163
|
+
red: [215, 130, 126], green: [156, 207, 216], yellow: [234, 157, 173], orange: [235, 188, 186],
|
|
164
|
+
white: [224, 222, 244], gray: [110, 106, 134],
|
|
165
|
+
surface: [31, 29, 46],
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
/** Theme name -> one-line description, for the /theme picker. */
|
|
169
|
+
export const THEME_LABELS = {
|
|
170
|
+
auto: 'follow the terminal: light theme on a light background, dark otherwise',
|
|
171
|
+
dark: 'dark teal background, white text',
|
|
172
|
+
light: 'light background, darkened accents',
|
|
173
|
+
nord: 'cool blue-grey',
|
|
174
|
+
dracula: 'high-contrast purple and pink',
|
|
175
|
+
gruvbox: 'warm retro earth tones',
|
|
176
|
+
catppuccin: 'soft pastel, warm-tinted dark',
|
|
177
|
+
tokyo: 'blue-black, high-contrast accents',
|
|
178
|
+
solar: 'even hue ramp, light background',
|
|
179
|
+
rose: 'muted mauve dark',
|
|
180
|
+
mono: 'greyscale, no hue',
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/** `auto` is a name you can set without being a palette, so it is listed separately from
|
|
184
|
+
* the tables — see `setTheme` for how it resolves. */
|
|
185
|
+
export const AUTO_THEME = 'auto';
|
|
186
|
+
|
|
187
|
+
/** Whether a theme name is one we know. */
|
|
188
|
+
export function hasTheme(name) {
|
|
189
|
+
return name === AUTO_THEME || Object.prototype.hasOwnProperty.call(THEMES, name);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The names a user may choose, `auto` first because it is the default. */
|
|
193
|
+
export const THEME_NAMES = [AUTO_THEME, ...Object.keys(THEMES)];
|
|
194
|
+
|
|
195
|
+
/** True when a theme paints on a light background. Resolves `auto` first, so a caller
|
|
196
|
+
* asking about the CURRENT theme gets the answer for what is actually on screen. */
|
|
197
|
+
export function isLightTheme(name) {
|
|
198
|
+
const resolved = name === AUTO_THEME ? (autoIsLight ? 'light' : 'dark') : name;
|
|
199
|
+
const t = THEMES[resolved];
|
|
200
|
+
return !!(t && Array.isArray(t.bg) && t.bg[0] > 128);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export const C = {
|
|
204
|
+
reset: '\x1b[0m',
|
|
205
|
+
bold: '\x1b[1m',
|
|
206
|
+
dim: '\x1b[2m',
|
|
207
|
+
// Italic (SGR 3). Used for the BODY of a reasoning block — the prose reads as
|
|
208
|
+
// an aside rather than as the answer. Must be closed with SGR 23 (`endItalic`),
|
|
209
|
+
// NOT with `reset`, because resetting also drops the surrounding dim/gray that
|
|
210
|
+
// the same row depends on. Terminals without italic fall back to normal text,
|
|
211
|
+
// so this is a decoration, never load-bearing.
|
|
212
|
+
italic: '\x1b[3m',
|
|
213
|
+
endItalic: '\x1b[23m',
|
|
214
|
+
muted: '\x1b[90m',
|
|
215
|
+
teal: TEAL,
|
|
216
|
+
cyan: CYAN,
|
|
217
|
+
blue: BLUE,
|
|
218
|
+
fg: FG,
|
|
219
|
+
bg: BG,
|
|
220
|
+
bgTeal: brgb(0, 184, 219),
|
|
221
|
+
bgDim: brgb(12, 24, 30),
|
|
222
|
+
// Fills for the segmented context bar: five blues in one family, deepening towards
|
|
223
|
+
// black, plus a neutral free segment. Background colours, because the bar is read by
|
|
224
|
+
// FILL rather than by any text — a segment is usually only a few columns wide, so a
|
|
225
|
+
// letter inside it would be unreadable noise.
|
|
226
|
+
//
|
|
227
|
+
// The steps are deliberately narrow. Widening them makes the bar easier to tell apart
|
|
228
|
+
// at a glance but harder to keep a dark terminal readable, and the darkest member has
|
|
229
|
+
// to stay distinguishable from the page background it sits on.
|
|
230
|
+
ctxSystem: brgb(35, 48, 95), // deepest navy
|
|
231
|
+
ctxPrompt: brgb(43, 61, 120), // navy
|
|
232
|
+
ctxAssistant: brgb(52, 74, 146), // indigo
|
|
233
|
+
ctxThinking: brgb(77, 107, 254), // brand blue, the brightest used segment
|
|
234
|
+
ctxTools: brgb(90, 124, 255), // light blue
|
|
235
|
+
ctxFree: brgb(46, 46, 46), // neutral grey: "nothing here yet"
|
|
236
|
+
// The SURFACE colour for a floating panel (picker menus, dialogs). A NEUTRAL GREY,
|
|
237
|
+
// deliberately separate from the theme's `bg`:
|
|
238
|
+
//
|
|
239
|
+
// * the theme background is `[0, 18, 26]` — a dark TEAL. A raised surface should be
|
|
240
|
+
// neutral in every theme; only the ACCENTS carry the brand hue. Reusing `bg` for
|
|
241
|
+
// the panel made every popup read blue, which is not what a panel is.
|
|
242
|
+
// * `applyTheme` used to overwrite this with `t.bg`, so the value here only applied
|
|
243
|
+
// before a theme was set. It is no longer touched by the theme.
|
|
244
|
+
//
|
|
245
|
+
// xterm-256's grey ramp runs 232..255 from rgb(8) to rgb(238) in steps of 10, so 236
|
|
246
|
+
// is rgb(48,48,48) = #303030 — clearly lifted off a near-black terminal without
|
|
247
|
+
// washing the text out. Truecolor gets `#2e2e2e`, the same shade written exactly.
|
|
248
|
+
bgPanel: TRUECOLOR ? '\x1b[48;2;46;46;46m' : '\x1b[48;5;236m',
|
|
249
|
+
border: BORDER,
|
|
250
|
+
red: '\x1b[91m',
|
|
251
|
+
green: '\x1b[92m',
|
|
252
|
+
yellow: '\x1b[93m',
|
|
253
|
+
magenta: '\x1b[95m',
|
|
254
|
+
gray: '\x1b[90m',
|
|
255
|
+
white: '\x1b[97m',
|
|
256
|
+
orange: TRUECOLOR ? rgb(255, 140, 0) : '\x1b[38;5;208m',
|
|
257
|
+
// Cyan pushed slightly toward green — used for the Swarm mode badge so it reads
|
|
258
|
+
// as its own mode next to Plan (pure cyan) and Focus (blue) while staying in the
|
|
259
|
+
// theme's blue-green family. 38;5;43 is rgb(0,215,175): red at zero and green
|
|
260
|
+
// only 40 above blue, i.e. still clearly cyan with a green cast. 38;5;85 was the
|
|
261
|
+
// first attempt and read as plain GREEN — its red channel is lifted to 95 and
|
|
262
|
+
// green pins at 255, which is a spring green, not a green-tinted cyan.
|
|
263
|
+
spring: TRUECOLOR ? rgb(0, 215, 175) : '\x1b[38;5;43m',
|
|
264
|
+
// Git branch in the status line: yellow pulled about half way toward white, so
|
|
265
|
+
// it stays clearly "yellow" (not cream/ivory) while reading brighter than the
|
|
266
|
+
// plain 93 yellow on a dark background. rgb(255,219,110) keeps red at max,
|
|
267
|
+
// green near it, and blue around 43% — that ratio is what keeps the hue
|
|
268
|
+
// yellow rather than dropping into orange.
|
|
269
|
+
branch: TRUECOLOR ? rgb(255, 219, 110) : '\x1b[38;5;222m',
|
|
270
|
+
// Shell-mode (`!`) accent — violet, matching kimi-code's shellMode token so the
|
|
271
|
+
// bash-mode editor border and the `!` read as "shell", distinct from the cyan
|
|
272
|
+
// prompt and the yellow git badge. #BD93F9 dark / #7C3AED light.
|
|
273
|
+
shellMode: TRUECOLOR ? rgb(189, 147, 249) : '\x1b[38;5;141m',
|
|
274
|
+
bgRed: '\x1b[41m',
|
|
275
|
+
bgGreen: '\x1b[42m',
|
|
276
|
+
// Selection highlight (mouse text selection) and the scrollbar gutter.
|
|
277
|
+
// Selection uses a dark teal background so white text stays readable.
|
|
278
|
+
selBg: TRUECOLOR ? brgb(0, 70, 84) : '\x1b[48;5;23m', // Original dark cyan background
|
|
279
|
+
// Reset ONLY the background colour, keeping the current foreground. Used to end
|
|
280
|
+
// a selection highlight so text after it keeps its own colour (a bare ESC[0m
|
|
281
|
+
// would also reset the foreground, turning a cyan/white run grey).
|
|
282
|
+
bgReset: '\x1b[49m',
|
|
283
|
+
// Scrollbar, styled after OpenTUI (what cline uses): a solid block thumb on a
|
|
284
|
+
// near-black track. Only the FOREGROUND colour is stateful — the track is always the
|
|
285
|
+
// background — because that is how OpenTUI's SliderRenderable paints a cell:
|
|
286
|
+
// foreground = thumb, background = track. A `█` covers both columns with the
|
|
287
|
+
// foreground, while `▀`/`▄` expose one half to the track, so the two colours must
|
|
288
|
+
// stay DISTINCT; giving a half-cell the thumb colour as its background is what made
|
|
289
|
+
// it read as a second square, and it also survived the modal backdrop's faint
|
|
290
|
+
// attribute, which fades the foreground only.
|
|
291
|
+
// track #252527 thumb #9a9ea3 (OpenTUI's defaults, inlined)
|
|
292
|
+
// hover #d2d6da active #ffffff (brighter thumb, not a new hue)
|
|
293
|
+
scrollTrackBg: TRUECOLOR ? brgb(0x25, 0x25, 0x27) : '\x1b[48;5;235m',
|
|
294
|
+
scrollTrackFg: TRUECOLOR ? rgb(0x25, 0x25, 0x27) : '\x1b[38;5;235m',
|
|
295
|
+
scrollThumbBg: TRUECOLOR ? brgb(0x9a, 0x9e, 0xa3) : '\x1b[48;5;246m',
|
|
296
|
+
scrollThumbFg: TRUECOLOR ? rgb(0x9a, 0x9e, 0xa3) : '\x1b[38;5;246m',
|
|
297
|
+
scrollThumbHoverBg: TRUECOLOR ? brgb(0xd2, 0xd6, 0xda) : '\x1b[48;5;252m',
|
|
298
|
+
scrollThumbHoverFg: TRUECOLOR ? rgb(0xd2, 0xd6, 0xda) : '\x1b[38;5;252m',
|
|
299
|
+
scrollThumbActiveBg: TRUECOLOR ? brgb(0xff, 0xff, 0xff) : '\x1b[48;5;231m',
|
|
300
|
+
scrollThumbActiveFg: TRUECOLOR ? rgb(0xff, 0xff, 0xff) : '\x1b[38;5;231m',
|
|
301
|
+
// FOREGROUND aliases kept for the todo/queue resize rule, which draws a `─` line
|
|
302
|
+
// and therefore needs a foreground, not the block's background pair.
|
|
303
|
+
scrollThumbActive: TRUECOLOR ? rgb(0xff, 0xff, 0xff) : '\x1b[38;5;231m',
|
|
304
|
+
scrollThumbHover: TRUECOLOR ? rgb(0xd2, 0xd6, 0xda) : '\x1b[38;5;252m',
|
|
305
|
+
// Legacy foreground forms, still referenced by the gutter-hover path.
|
|
306
|
+
scrollTrack: '\x1b[90m',
|
|
307
|
+
scrollThumb: TRUECOLOR ? rgb(0, 184, 219) : '\x1b[38;5;37m',
|
|
308
|
+
// Hover highlight for clickable rows (menu items, picker options, form
|
|
309
|
+
// fields, composer rows). An explicit bright foreground + BOLD rather than a
|
|
310
|
+
// bare BOLD: BOLD only brightens the 16-colour palette (it does nothing to a
|
|
311
|
+
// true-color row), which is why hover used to be invisible on most rows.
|
|
312
|
+
// tintRange() re-applies this so the row's own SGR codes cannot cancel it.
|
|
313
|
+
hover: '\x1b[1m' + rgb(190, 245, 255),
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
export function style(text, ...codes) {
|
|
317
|
+
return codes.join('') + text + '\x1b[0m';
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Linearly interpolate between two RGB triples and return an ANSI colour string.
|
|
321
|
+
// Used for the pulsing Working indicator (orange pulses to a brighter/darker
|
|
322
|
+
// shade over a 2s cycle).
|
|
323
|
+
export function lerpColor(r1, g1, b1, r2, g2, b2, t) {
|
|
324
|
+
const ti = Math.max(0, Math.min(1, t));
|
|
325
|
+
const r = Math.round(r1 + (r2 - r1) * ti);
|
|
326
|
+
const g = Math.round(g1 + (g2 - g1) * ti);
|
|
327
|
+
const b = Math.round(b1 + (b2 - b1) * ti);
|
|
328
|
+
return rgb(r, g, b);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Which concrete theme `auto` resolves to right now. `setTheme` keeps it so a repaint or a
|
|
332
|
+
// `/theme auto` after a terminal probe lands on the same answer.
|
|
333
|
+
let autoIsLight = false;
|
|
334
|
+
|
|
335
|
+
// The theme `setTheme` last applied, resolved (so `auto` reports the concrete name it
|
|
336
|
+
// became). Read by the render cache: a cached row holds finished escapes, so a palette
|
|
337
|
+
// change has to invalidate them or the old colours survive the switch.
|
|
338
|
+
let appliedTheme = '';
|
|
339
|
+
|
|
340
|
+
/** The concrete theme in force, or `''` before the first `setTheme`. */
|
|
341
|
+
export function currentTheme() { return appliedTheme; }
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Tell `setTheme` whether the terminal has a light background.
|
|
345
|
+
*
|
|
346
|
+
* The probe lives in term-caps.js and this module is a leaf that imports nothing, so the
|
|
347
|
+
* answer has to be handed in. Called once after the probe and again if the terminal is
|
|
348
|
+
* resized on a different display.
|
|
349
|
+
*/
|
|
350
|
+
export function setLightBackground(light) {
|
|
351
|
+
autoIsLight = !!light;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Apply a named theme IN PLACE. `C` is a module-level object and ESM imports are
|
|
355
|
+
// live bindings, so mutating its properties here changes the colours everywhere
|
|
356
|
+
// without re-importing. Returns true when the name was recognised.
|
|
357
|
+
//
|
|
358
|
+
// `auto` is a CHOICE, not a palette: it resolves to `light` on a light terminal and to
|
|
359
|
+
// `dark` otherwise, and then applies that theme's colours. It used to be a fixed table —
|
|
360
|
+
// a dark background with brand-cyan text — which is not what "auto" means and not what its
|
|
361
|
+
// own label claimed ("on the terminal's own palette"). The user asking for auto is asking
|
|
362
|
+
// not to have to think about it.
|
|
363
|
+
export function setTheme(name) {
|
|
364
|
+
const resolved = name === 'auto' ? (autoIsLight ? 'light' : 'dark') : name;
|
|
365
|
+
const t = THEMES[resolved];
|
|
366
|
+
if (!t) return false;
|
|
367
|
+
appliedTheme = resolved;
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
// Update foreground colors
|
|
372
|
+
C.teal = rgb(...t.teal);
|
|
373
|
+
C.fg = rgb(...t.fg);
|
|
374
|
+
C.cyan = rgb(...t.cyan);
|
|
375
|
+
C.blue = rgb(...t.blue);
|
|
376
|
+
C.border = rgb(...t.border);
|
|
377
|
+
// Update background colors
|
|
378
|
+
C.bg = brgb(...t.bg);
|
|
379
|
+
C.bgTeal = brgb(...t.teal);
|
|
380
|
+
// The panel surface is NEUTRAL GREY and comes from the theme's own `surface` triple,
|
|
381
|
+
// NOT from `bg`. It used to be set to `t.bg` (a dark teal), which made every popup
|
|
382
|
+
// carry a blue cast; a raised surface must be neutral, with only the accents following
|
|
383
|
+
// the brand hue. A light theme gets a light grey so the panel still reads as raised.
|
|
384
|
+
if (t.surface) C.bgPanel = brgb(...t.surface);
|
|
385
|
+
C.selBg = brgb(...t.selBg);
|
|
386
|
+
|
|
387
|
+
// Scrollbar colours. The thumb is a BLOCK, so its colour goes on BOTH the
|
|
388
|
+
// foreground and the background (a half-cell `▀`/`▄` shows the foreground, a full
|
|
389
|
+
// `█` shows the background — they must match). Hover / drag are brighter shades
|
|
390
|
+
// of the same hue, applied to the same pair, so the whole thumb changes together.
|
|
391
|
+
const active = [
|
|
392
|
+
Math.min(255, Math.round(t.teal[0] * 0.6 + 255 * 0.4)),
|
|
393
|
+
Math.min(255, Math.round(t.teal[1] * 0.6 + 255 * 0.4)),
|
|
394
|
+
Math.min(255, Math.round(t.teal[2] * 0.6 + 255 * 0.4)),
|
|
395
|
+
];
|
|
396
|
+
const hover = t.hover || [190, 245, 255];
|
|
397
|
+
C.scrollTrackFg = rgb(0x25, 0x25, 0x27);
|
|
398
|
+
C.scrollTrackBg = brgb(0x25, 0x25, 0x27);
|
|
399
|
+
C.scrollThumbFg = rgb(...t.teal);
|
|
400
|
+
C.scrollThumbBg = brgb(...t.teal);
|
|
401
|
+
C.scrollThumbHoverFg = rgb(...hover);
|
|
402
|
+
C.scrollThumbHoverBg = brgb(...hover);
|
|
403
|
+
C.scrollThumbActiveFg = rgb(...active);
|
|
404
|
+
C.scrollThumbActiveBg = brgb(...active);
|
|
405
|
+
// Foreground aliases for the todo/queue resize rule.
|
|
406
|
+
C.scrollTrack = '\x1b[90m';
|
|
407
|
+
C.scrollThumb = rgb(...t.teal);
|
|
408
|
+
C.scrollThumbHover = rgb(...hover);
|
|
409
|
+
C.scrollThumbActive = rgb(...active);
|
|
410
|
+
|
|
411
|
+
// Hover stays visible on this theme's background (bright on dark, dark on light).
|
|
412
|
+
C.hover = '\x1b[1m' + rgb(...(t.hover || [190, 245, 255]));
|
|
413
|
+
|
|
414
|
+
// Semantic colors — swap to deep versions on light backgrounds.
|
|
415
|
+
if (t.white) C.white = rgb(...t.white);
|
|
416
|
+
if (t.gray) C.gray = rgb(...t.gray);
|
|
417
|
+
if (t.red) C.red = rgb(...t.red);
|
|
418
|
+
if (t.green) C.green = rgb(...t.green);
|
|
419
|
+
if (t.yellow) C.yellow = rgb(...t.yellow);
|
|
420
|
+
if (t.orange) C.orange = rgb(...t.orange);
|
|
421
|
+
// The git branch colour is derived per theme: the pale yellow that reads well
|
|
422
|
+
// on the dark background is nearly invisible on white, so on a light theme it
|
|
423
|
+
// is darkened (keeping the same hue) instead of being left as-is.
|
|
424
|
+
if (t.bg && t.bg[0] > 128) {
|
|
425
|
+
C.branch = rgb(146, 106, 0); // dark amber for light backgrounds
|
|
426
|
+
} else {
|
|
427
|
+
C.branch = TRUECOLOR ? rgb(255, 219, 110) : '\x1b[38;5;222m';
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
return true;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
export function clearScreen() {
|
|
434
|
+
return '\x1b[3J\x1b[H\x1b[2J';
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// Clear screen and set background color based on current theme
|
|
438
|
+
export function clearAndSetBg() {
|
|
439
|
+
// Reset all attributes first
|
|
440
|
+
const reset = '\x1b[0m';
|
|
441
|
+
// Clear screen
|
|
442
|
+
const clear = '\x1b[3J\x1b[H\x1b[2J';
|
|
443
|
+
// Set background color using current C.bg value
|
|
444
|
+
const bg = C.bg || '\x1b[48;2;0;18;26m'; // Default dark background
|
|
445
|
+
return reset + clear + bg;
|
|
446
|
+
}
|
|
447
|
+
export function eraseLine() {
|
|
448
|
+
return '\x1b[2K';
|
|
449
|
+
}
|
|
450
|
+
export function eraseScreen() {
|
|
451
|
+
return '\x1b[2J';
|
|
452
|
+
}
|
|
453
|
+
export function moveTo(r, c) {
|
|
454
|
+
return `\x1b[${r + 1};${c + 1}H`;
|
|
455
|
+
}
|
|
456
|
+
export function cursorUp(n) { return `\x1b[${n}A`; }
|
|
457
|
+
export function cursorDown(n) { return `\x1b[${n}B`; }
|
|
458
|
+
export function cursorLeft(n) { return `\x1b[${n}D`; }
|
|
459
|
+
export function cursorForward(n) { return `\x1b[${n}C`; }
|
|
460
|
+
export function hideCursor() { return '\x1b[?25l'; }
|
|
461
|
+
export function showCursor() { return '\x1b[?25h'; }
|
|
462
|
+
// Cursor shape: terminals accept DECSCUSR — 2 = block, 4 = underline, 6 = bar(I-beam);
|
|
463
|
+
// 1/3/5 = blinking variants. Force a solid block cursor so positioning is always exact
|
|
464
|
+
// and unaffected by I-beam alignment.
|
|
465
|
+
export function blockCursor() { return '\x1b[2 q'; }
|
|
466
|
+
export function underlineCursor() { return '\x1b[4 q'; }
|
|
467
|
+
export function barCursor() { return '\x1b[6 q'; }
|
|
468
|
+
export function alternateScreen(on) { return on ? '\x1b[?1049h' : '\x1b[?1049l'; }
|
|
469
|
+
export function setScrollRegion(top, bottom) {
|
|
470
|
+
if (process.platform === 'win32') return ''; // win TTY scroll regions are unreliable
|
|
471
|
+
return `\x1b[${top + 1};${bottom + 1}r`;
|
|
472
|
+
}
|
|
473
|
+
export function resetScrollRegion() {
|
|
474
|
+
return '\x1b[r';
|
|
475
|
+
}
|
|
476
|
+
export function setBracketed(on) {
|
|
477
|
+
return `\x1b[?2004h${on ? '' : ''}`; // placeholder; real impl toggles bracketed paste
|
|
478
|
+
}
|