@asm-agent/tui 0.8.2
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/README.md +795 -0
- package/dist/autocomplete.d.ts +61 -0
- package/dist/autocomplete.d.ts.map +1 -0
- package/dist/autocomplete.js +601 -0
- package/dist/autocomplete.js.map +1 -0
- package/dist/components/box.d.ts +24 -0
- package/dist/components/box.d.ts.map +1 -0
- package/dist/components/box.js +114 -0
- package/dist/components/box.js.map +1 -0
- package/dist/components/cancellable-loader.d.ts +22 -0
- package/dist/components/cancellable-loader.d.ts.map +1 -0
- package/dist/components/cancellable-loader.js +35 -0
- package/dist/components/cancellable-loader.js.map +1 -0
- package/dist/components/editor.d.ts +275 -0
- package/dist/components/editor.d.ts.map +1 -0
- package/dist/components/editor.js +1968 -0
- package/dist/components/editor.js.map +1 -0
- package/dist/components/image.d.ts +34 -0
- package/dist/components/image.d.ts.map +1 -0
- package/dist/components/image.js +96 -0
- package/dist/components/image.js.map +1 -0
- package/dist/components/input.d.ts +37 -0
- package/dist/components/input.d.ts.map +1 -0
- package/dist/components/input.js +385 -0
- package/dist/components/input.js.map +1 -0
- package/dist/components/loader.d.ts +29 -0
- package/dist/components/loader.d.ts.map +1 -0
- package/dist/components/loader.js +69 -0
- package/dist/components/loader.js.map +1 -0
- package/dist/components/markdown.d.ts +100 -0
- package/dist/components/markdown.d.ts.map +1 -0
- package/dist/components/markdown.js +773 -0
- package/dist/components/markdown.js.map +1 -0
- package/dist/components/select-list.d.ts +61 -0
- package/dist/components/select-list.d.ts.map +1 -0
- package/dist/components/select-list.js +206 -0
- package/dist/components/select-list.js.map +1 -0
- package/dist/components/settings-list.d.ts +49 -0
- package/dist/components/settings-list.d.ts.map +1 -0
- package/dist/components/settings-list.js +170 -0
- package/dist/components/settings-list.js.map +1 -0
- package/dist/components/spacer.d.ts +12 -0
- package/dist/components/spacer.d.ts.map +1 -0
- package/dist/components/spacer.js +21 -0
- package/dist/components/spacer.js.map +1 -0
- package/dist/components/text.d.ts +19 -0
- package/dist/components/text.d.ts.map +1 -0
- package/dist/components/text.js +77 -0
- package/dist/components/text.js.map +1 -0
- package/dist/components/truncated-text.d.ts +14 -0
- package/dist/components/truncated-text.d.ts.map +1 -0
- package/dist/components/truncated-text.js +44 -0
- package/dist/components/truncated-text.js.map +1 -0
- package/dist/editor-component.d.ts +51 -0
- package/dist/editor-component.d.ts.map +1 -0
- package/dist/editor-component.js +2 -0
- package/dist/editor-component.js.map +1 -0
- package/dist/fullscreen.d.ts +105 -0
- package/dist/fullscreen.d.ts.map +1 -0
- package/dist/fullscreen.js +597 -0
- package/dist/fullscreen.js.map +1 -0
- package/dist/fuzzy.d.ts +26 -0
- package/dist/fuzzy.d.ts.map +1 -0
- package/dist/fuzzy.js +114 -0
- package/dist/fuzzy.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/keybindings.d.ts +239 -0
- package/dist/keybindings.d.ts.map +1 -0
- package/dist/keybindings.js +222 -0
- package/dist/keybindings.js.map +1 -0
- package/dist/keys.d.ts +184 -0
- package/dist/keys.d.ts.map +1 -0
- package/dist/keys.js +1163 -0
- package/dist/keys.js.map +1 -0
- package/dist/kill-ring.d.ts +26 -0
- package/dist/kill-ring.d.ts.map +1 -0
- package/dist/kill-ring.js +42 -0
- package/dist/kill-ring.js.map +1 -0
- package/dist/latex.d.ts +21 -0
- package/dist/latex.d.ts.map +1 -0
- package/dist/latex.js +797 -0
- package/dist/latex.js.map +1 -0
- package/dist/mouse.d.ts +24 -0
- package/dist/mouse.d.ts.map +1 -0
- package/dist/mouse.js +34 -0
- package/dist/mouse.js.map +1 -0
- package/dist/render-cache.d.ts +9 -0
- package/dist/render-cache.d.ts.map +1 -0
- package/dist/render-cache.js +23 -0
- package/dist/render-cache.js.map +1 -0
- package/dist/selection-metadata.d.ts +22 -0
- package/dist/selection-metadata.d.ts.map +1 -0
- package/dist/selection-metadata.js +141 -0
- package/dist/selection-metadata.js.map +1 -0
- package/dist/slash-command-context.d.ts +12 -0
- package/dist/slash-command-context.d.ts.map +1 -0
- package/dist/slash-command-context.js +32 -0
- package/dist/slash-command-context.js.map +1 -0
- package/dist/stdin-buffer.d.ts +50 -0
- package/dist/stdin-buffer.d.ts.map +1 -0
- package/dist/stdin-buffer.js +313 -0
- package/dist/stdin-buffer.js.map +1 -0
- package/dist/terminal-colors.d.ts +31 -0
- package/dist/terminal-colors.d.ts.map +1 -0
- package/dist/terminal-colors.js +161 -0
- package/dist/terminal-colors.js.map +1 -0
- package/dist/terminal-image.d.ts +85 -0
- package/dist/terminal-image.d.ts.map +1 -0
- package/dist/terminal-image.js +324 -0
- package/dist/terminal-image.js.map +1 -0
- package/dist/terminal.d.ts +113 -0
- package/dist/terminal.d.ts.map +1 -0
- package/dist/terminal.js +486 -0
- package/dist/terminal.js.map +1 -0
- package/dist/tui.d.ts +310 -0
- package/dist/tui.d.ts.map +1 -0
- package/dist/tui.js +1673 -0
- package/dist/tui.js.map +1 -0
- package/dist/undo-stack.d.ts +16 -0
- package/dist/undo-stack.d.ts.map +1 -0
- package/dist/undo-stack.js +26 -0
- package/dist/undo-stack.js.map +1 -0
- package/dist/utils.d.ts +98 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +1233 -0
- package/dist/utils.js.map +1 -0
- package/package.json +52 -0
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* StdinBuffer buffers input and emits complete sequences.
|
|
3
|
+
*
|
|
4
|
+
* This is necessary because stdin data events can arrive in partial chunks,
|
|
5
|
+
* especially for escape sequences like mouse events. Without buffering,
|
|
6
|
+
* partial sequences can be misinterpreted as regular keypresses.
|
|
7
|
+
*
|
|
8
|
+
* For example, the mouse SGR sequence `\x1b[<35;20;5m` might arrive as:
|
|
9
|
+
* - Event 1: `\x1b`
|
|
10
|
+
* - Event 2: `[<35`
|
|
11
|
+
* - Event 3: `;20;5m`
|
|
12
|
+
*
|
|
13
|
+
* The buffer accumulates these until a complete sequence is detected.
|
|
14
|
+
* Call the `process()` method to feed input data.
|
|
15
|
+
*
|
|
16
|
+
* Based on code from OpenTUI (https://github.com/anomalyco/opentui)
|
|
17
|
+
* MIT License - Copyright (c) 2025 opentui
|
|
18
|
+
*/
|
|
19
|
+
import { EventEmitter } from "events";
|
|
20
|
+
const ESC = "\x1b";
|
|
21
|
+
const BRACKETED_PASTE_START = "\x1b[200~";
|
|
22
|
+
const BRACKETED_PASTE_END = "\x1b[201~";
|
|
23
|
+
/**
|
|
24
|
+
* Check if a string is a complete escape sequence or needs more data
|
|
25
|
+
*/
|
|
26
|
+
function isCompleteSequence(data) {
|
|
27
|
+
if (!data.startsWith(ESC)) {
|
|
28
|
+
return "not-escape";
|
|
29
|
+
}
|
|
30
|
+
if (data.length === 1) {
|
|
31
|
+
return "incomplete";
|
|
32
|
+
}
|
|
33
|
+
const afterEsc = data.slice(1);
|
|
34
|
+
if (afterEsc.startsWith("[")) {
|
|
35
|
+
if (afterEsc.startsWith("[M")) {
|
|
36
|
+
return data.length >= 6 ? "complete" : "incomplete";
|
|
37
|
+
}
|
|
38
|
+
return isCompleteCsiSequence(data);
|
|
39
|
+
}
|
|
40
|
+
if (afterEsc.startsWith("]")) {
|
|
41
|
+
return isCompleteOscSequence(data);
|
|
42
|
+
}
|
|
43
|
+
if (afterEsc.startsWith("P")) {
|
|
44
|
+
return isCompleteDcsSequence(data);
|
|
45
|
+
}
|
|
46
|
+
if (afterEsc.startsWith("_")) {
|
|
47
|
+
return isCompleteApcSequence(data);
|
|
48
|
+
}
|
|
49
|
+
if (afterEsc.startsWith("O")) {
|
|
50
|
+
return afterEsc.length >= 2 ? "complete" : "incomplete";
|
|
51
|
+
}
|
|
52
|
+
if (afterEsc.length === 1) {
|
|
53
|
+
return "complete";
|
|
54
|
+
}
|
|
55
|
+
return "complete";
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Check if CSI sequence is complete
|
|
59
|
+
* CSI sequences: ESC [ ... followed by a final byte (0x40-0x7E)
|
|
60
|
+
*/
|
|
61
|
+
function isCompleteCsiSequence(data) {
|
|
62
|
+
if (!data.startsWith(`${ESC}[`)) {
|
|
63
|
+
return "complete";
|
|
64
|
+
}
|
|
65
|
+
if (data.length < 3) {
|
|
66
|
+
return "incomplete";
|
|
67
|
+
}
|
|
68
|
+
const payload = data.slice(2);
|
|
69
|
+
const lastChar = payload[payload.length - 1];
|
|
70
|
+
const lastCharCode = lastChar.charCodeAt(0);
|
|
71
|
+
if (lastCharCode >= 0x40 && lastCharCode <= 0x7e) {
|
|
72
|
+
if (payload.startsWith("<")) {
|
|
73
|
+
const mouseMatch = /^<\d+;\d+;\d+[Mm]$/.test(payload);
|
|
74
|
+
if (mouseMatch) {
|
|
75
|
+
return "complete";
|
|
76
|
+
}
|
|
77
|
+
if (lastChar === "M" || lastChar === "m") {
|
|
78
|
+
const parts = payload.slice(1, -1).split(";");
|
|
79
|
+
if (parts.length === 3 && parts.every((p) => /^\d+$/.test(p))) {
|
|
80
|
+
return "complete";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return "incomplete";
|
|
84
|
+
}
|
|
85
|
+
return "complete";
|
|
86
|
+
}
|
|
87
|
+
return "incomplete";
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Check if OSC sequence is complete
|
|
91
|
+
* OSC sequences: ESC ] ... ST (where ST is ESC \ or BEL)
|
|
92
|
+
*/
|
|
93
|
+
function isCompleteOscSequence(data) {
|
|
94
|
+
if (!data.startsWith(`${ESC}]`)) {
|
|
95
|
+
return "complete";
|
|
96
|
+
}
|
|
97
|
+
if (data.endsWith(`${ESC}\\`) || data.endsWith("\x07")) {
|
|
98
|
+
return "complete";
|
|
99
|
+
}
|
|
100
|
+
return "incomplete";
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if DCS (Device Control String) sequence is complete
|
|
104
|
+
* DCS sequences: ESC P ... ST (where ST is ESC \)
|
|
105
|
+
* Used for XTVersion responses like ESC P >| ... ESC \
|
|
106
|
+
*/
|
|
107
|
+
function isCompleteDcsSequence(data) {
|
|
108
|
+
if (!data.startsWith(`${ESC}P`)) {
|
|
109
|
+
return "complete";
|
|
110
|
+
}
|
|
111
|
+
if (data.endsWith(`${ESC}\\`)) {
|
|
112
|
+
return "complete";
|
|
113
|
+
}
|
|
114
|
+
return "incomplete";
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if APC (Application Program Command) sequence is complete
|
|
118
|
+
* APC sequences: ESC _ ... ST (where ST is ESC \)
|
|
119
|
+
* Used for Kitty graphics responses like ESC _ G ... ESC \
|
|
120
|
+
*/
|
|
121
|
+
function isCompleteApcSequence(data) {
|
|
122
|
+
if (!data.startsWith(`${ESC}_`)) {
|
|
123
|
+
return "complete";
|
|
124
|
+
}
|
|
125
|
+
if (data.endsWith(`${ESC}\\`)) {
|
|
126
|
+
return "complete";
|
|
127
|
+
}
|
|
128
|
+
return "incomplete";
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Split accumulated buffer into complete sequences
|
|
132
|
+
*/
|
|
133
|
+
function parseUnmodifiedKittyPrintableCodepoint(sequence) {
|
|
134
|
+
const match = sequence.match(/^\x1b\[(\d+)(?::\d*)?(?::\d+)?u$/);
|
|
135
|
+
if (!match)
|
|
136
|
+
return undefined;
|
|
137
|
+
const codepoint = parseInt(match[1], 10);
|
|
138
|
+
return codepoint >= 32 ? codepoint : undefined;
|
|
139
|
+
}
|
|
140
|
+
function extractCompleteSequences(buffer) {
|
|
141
|
+
const sequences = [];
|
|
142
|
+
let pos = 0;
|
|
143
|
+
while (pos < buffer.length) {
|
|
144
|
+
const remaining = buffer.slice(pos);
|
|
145
|
+
if (remaining.startsWith(ESC)) {
|
|
146
|
+
let seqEnd = 1;
|
|
147
|
+
while (seqEnd <= remaining.length) {
|
|
148
|
+
const candidate = remaining.slice(0, seqEnd);
|
|
149
|
+
const status = isCompleteSequence(candidate);
|
|
150
|
+
if (status === "complete") {
|
|
151
|
+
sequences.push(candidate);
|
|
152
|
+
pos += seqEnd;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
else if (status === "incomplete") {
|
|
156
|
+
seqEnd++;
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
sequences.push(candidate);
|
|
160
|
+
pos += seqEnd;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
if (seqEnd > remaining.length) {
|
|
165
|
+
return { sequences, remainder: remaining };
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
sequences.push(remaining[0]);
|
|
170
|
+
pos++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
return { sequences, remainder: "" };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Buffers stdin input and emits complete sequences via the 'data' event.
|
|
177
|
+
* Handles partial escape sequences that arrive across multiple chunks.
|
|
178
|
+
*/
|
|
179
|
+
export class StdinBuffer extends EventEmitter {
|
|
180
|
+
buffer = "";
|
|
181
|
+
timeout = null;
|
|
182
|
+
timeoutMs;
|
|
183
|
+
pasteMode = false;
|
|
184
|
+
pasteBuffer = "";
|
|
185
|
+
pendingKittyPrintableCodepoint;
|
|
186
|
+
constructor(options = {}) {
|
|
187
|
+
super();
|
|
188
|
+
this.timeoutMs = options.timeout ?? 10;
|
|
189
|
+
}
|
|
190
|
+
process(data) {
|
|
191
|
+
if (this.timeout) {
|
|
192
|
+
clearTimeout(this.timeout);
|
|
193
|
+
this.timeout = null;
|
|
194
|
+
}
|
|
195
|
+
// Handle high-byte conversion (for compatibility with parseKeypress)
|
|
196
|
+
// If buffer has single byte > 127, convert to ESC + (byte - 128)
|
|
197
|
+
let str;
|
|
198
|
+
if (Buffer.isBuffer(data)) {
|
|
199
|
+
if (data.length === 1 && data[0] > 127) {
|
|
200
|
+
const byte = data[0] - 128;
|
|
201
|
+
str = `\x1b${String.fromCharCode(byte)}`;
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
str = data.toString();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
str = data;
|
|
209
|
+
}
|
|
210
|
+
if (str.length === 0 && this.buffer.length === 0) {
|
|
211
|
+
this.emitDataSequence("");
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
this.buffer += str;
|
|
215
|
+
if (this.pasteMode) {
|
|
216
|
+
this.pasteBuffer += this.buffer;
|
|
217
|
+
this.buffer = "";
|
|
218
|
+
const endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);
|
|
219
|
+
if (endIndex !== -1) {
|
|
220
|
+
const pastedContent = this.pasteBuffer.slice(0, endIndex);
|
|
221
|
+
const remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);
|
|
222
|
+
this.pasteMode = false;
|
|
223
|
+
this.pasteBuffer = "";
|
|
224
|
+
this.pendingKittyPrintableCodepoint = undefined;
|
|
225
|
+
this.emit("paste", pastedContent);
|
|
226
|
+
if (remaining.length > 0) {
|
|
227
|
+
this.process(remaining);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
const startIndex = this.buffer.indexOf(BRACKETED_PASTE_START);
|
|
233
|
+
if (startIndex !== -1) {
|
|
234
|
+
if (startIndex > 0) {
|
|
235
|
+
const beforePaste = this.buffer.slice(0, startIndex);
|
|
236
|
+
const result = extractCompleteSequences(beforePaste);
|
|
237
|
+
for (const sequence of result.sequences) {
|
|
238
|
+
this.emitDataSequence(sequence);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.pendingKittyPrintableCodepoint = undefined;
|
|
242
|
+
this.buffer = this.buffer.slice(startIndex + BRACKETED_PASTE_START.length);
|
|
243
|
+
this.pasteMode = true;
|
|
244
|
+
this.pasteBuffer = this.buffer;
|
|
245
|
+
this.buffer = "";
|
|
246
|
+
const endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);
|
|
247
|
+
if (endIndex !== -1) {
|
|
248
|
+
const pastedContent = this.pasteBuffer.slice(0, endIndex);
|
|
249
|
+
const remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);
|
|
250
|
+
this.pasteMode = false;
|
|
251
|
+
this.pasteBuffer = "";
|
|
252
|
+
this.pendingKittyPrintableCodepoint = undefined;
|
|
253
|
+
this.emit("paste", pastedContent);
|
|
254
|
+
if (remaining.length > 0) {
|
|
255
|
+
this.process(remaining);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const result = extractCompleteSequences(this.buffer);
|
|
261
|
+
this.buffer = result.remainder;
|
|
262
|
+
for (const sequence of result.sequences) {
|
|
263
|
+
this.emitDataSequence(sequence);
|
|
264
|
+
}
|
|
265
|
+
if (this.buffer.length > 0) {
|
|
266
|
+
this.timeout = setTimeout(() => {
|
|
267
|
+
const flushed = this.flush();
|
|
268
|
+
for (const sequence of flushed) {
|
|
269
|
+
this.emitDataSequence(sequence);
|
|
270
|
+
}
|
|
271
|
+
}, this.timeoutMs);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
emitDataSequence(sequence) {
|
|
275
|
+
const rawCodepoint = sequence.length === 1 ? sequence.codePointAt(0) : undefined;
|
|
276
|
+
if (rawCodepoint !== undefined && rawCodepoint === this.pendingKittyPrintableCodepoint) {
|
|
277
|
+
this.pendingKittyPrintableCodepoint = undefined;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
this.pendingKittyPrintableCodepoint = parseUnmodifiedKittyPrintableCodepoint(sequence);
|
|
281
|
+
this.emit("data", sequence);
|
|
282
|
+
}
|
|
283
|
+
flush() {
|
|
284
|
+
if (this.timeout) {
|
|
285
|
+
clearTimeout(this.timeout);
|
|
286
|
+
this.timeout = null;
|
|
287
|
+
}
|
|
288
|
+
if (this.buffer.length === 0) {
|
|
289
|
+
return [];
|
|
290
|
+
}
|
|
291
|
+
const sequences = [this.buffer];
|
|
292
|
+
this.buffer = "";
|
|
293
|
+
this.pendingKittyPrintableCodepoint = undefined;
|
|
294
|
+
return sequences;
|
|
295
|
+
}
|
|
296
|
+
clear() {
|
|
297
|
+
if (this.timeout) {
|
|
298
|
+
clearTimeout(this.timeout);
|
|
299
|
+
this.timeout = null;
|
|
300
|
+
}
|
|
301
|
+
this.buffer = "";
|
|
302
|
+
this.pasteMode = false;
|
|
303
|
+
this.pasteBuffer = "";
|
|
304
|
+
this.pendingKittyPrintableCodepoint = undefined;
|
|
305
|
+
}
|
|
306
|
+
getBuffer() {
|
|
307
|
+
return this.buffer;
|
|
308
|
+
}
|
|
309
|
+
destroy() {
|
|
310
|
+
this.clear();
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=stdin-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdin-buffer.js","sourceRoot":"","sources":["../src/stdin-buffer.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * StdinBuffer buffers input and emits complete sequences.\n *\n * This is necessary because stdin data events can arrive in partial chunks,\n * especially for escape sequences like mouse events. Without buffering,\n * partial sequences can be misinterpreted as regular keypresses.\n *\n * For example, the mouse SGR sequence `\\x1b[<35;20;5m` might arrive as:\n * - Event 1: `\\x1b`\n * - Event 2: `[<35`\n * - Event 3: `;20;5m`\n *\n * The buffer accumulates these until a complete sequence is detected.\n * Call the `process()` method to feed input data.\n *\n * Based on code from OpenTUI (https://github.com/anomalyco/opentui)\n * MIT License - Copyright (c) 2025 opentui\n */\n\nimport { EventEmitter } from \"events\";\n\nconst ESC = \"\\x1b\";\nconst BRACKETED_PASTE_START = \"\\x1b[200~\";\nconst BRACKETED_PASTE_END = \"\\x1b[201~\";\n\n/**\n * Check if a string is a complete escape sequence or needs more data\n */\nfunction isCompleteSequence(data: string): \"complete\" | \"incomplete\" | \"not-escape\" {\n\tif (!data.startsWith(ESC)) {\n\t\treturn \"not-escape\";\n\t}\n\n\tif (data.length === 1) {\n\t\treturn \"incomplete\";\n\t}\n\n\tconst afterEsc = data.slice(1);\n\n\tif (afterEsc.startsWith(\"[\")) {\n\t\tif (afterEsc.startsWith(\"[M\")) {\n\t\t\treturn data.length >= 6 ? \"complete\" : \"incomplete\";\n\t\t}\n\t\treturn isCompleteCsiSequence(data);\n\t}\n\n\tif (afterEsc.startsWith(\"]\")) {\n\t\treturn isCompleteOscSequence(data);\n\t}\n\n\tif (afterEsc.startsWith(\"P\")) {\n\t\treturn isCompleteDcsSequence(data);\n\t}\n\n\tif (afterEsc.startsWith(\"_\")) {\n\t\treturn isCompleteApcSequence(data);\n\t}\n\n\tif (afterEsc.startsWith(\"O\")) {\n\t\treturn afterEsc.length >= 2 ? \"complete\" : \"incomplete\";\n\t}\n\n\tif (afterEsc.length === 1) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"complete\";\n}\n\n/**\n * Check if CSI sequence is complete\n * CSI sequences: ESC [ ... followed by a final byte (0x40-0x7E)\n */\nfunction isCompleteCsiSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}[`)) {\n\t\treturn \"complete\";\n\t}\n\n\tif (data.length < 3) {\n\t\treturn \"incomplete\";\n\t}\n\n\tconst payload = data.slice(2);\n\n\tconst lastChar = payload[payload.length - 1];\n\tconst lastCharCode = lastChar.charCodeAt(0);\n\n\tif (lastCharCode >= 0x40 && lastCharCode <= 0x7e) {\n\t\tif (payload.startsWith(\"<\")) {\n\t\t\tconst mouseMatch = /^<\\d+;\\d+;\\d+[Mm]$/.test(payload);\n\t\t\tif (mouseMatch) {\n\t\t\t\treturn \"complete\";\n\t\t\t}\n\t\t\tif (lastChar === \"M\" || lastChar === \"m\") {\n\t\t\t\tconst parts = payload.slice(1, -1).split(\";\");\n\t\t\t\tif (parts.length === 3 && parts.every((p) => /^\\d+$/.test(p))) {\n\t\t\t\t\treturn \"complete\";\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn \"incomplete\";\n\t\t}\n\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if OSC sequence is complete\n * OSC sequences: ESC ] ... ST (where ST is ESC \\ or BEL)\n */\nfunction isCompleteOscSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}]`)) {\n\t\treturn \"complete\";\n\t}\n\n\tif (data.endsWith(`${ESC}\\\\`) || data.endsWith(\"\\x07\")) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if DCS (Device Control String) sequence is complete\n * DCS sequences: ESC P ... ST (where ST is ESC \\)\n * Used for XTVersion responses like ESC P >| ... ESC \\\n */\nfunction isCompleteDcsSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}P`)) {\n\t\treturn \"complete\";\n\t}\n\n\tif (data.endsWith(`${ESC}\\\\`)) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Check if APC (Application Program Command) sequence is complete\n * APC sequences: ESC _ ... ST (where ST is ESC \\)\n * Used for Kitty graphics responses like ESC _ G ... ESC \\\n */\nfunction isCompleteApcSequence(data: string): \"complete\" | \"incomplete\" {\n\tif (!data.startsWith(`${ESC}_`)) {\n\t\treturn \"complete\";\n\t}\n\n\tif (data.endsWith(`${ESC}\\\\`)) {\n\t\treturn \"complete\";\n\t}\n\n\treturn \"incomplete\";\n}\n\n/**\n * Split accumulated buffer into complete sequences\n */\nfunction parseUnmodifiedKittyPrintableCodepoint(sequence: string): number | undefined {\n\tconst match = sequence.match(/^\\x1b\\[(\\d+)(?::\\d*)?(?::\\d+)?u$/);\n\tif (!match) return undefined;\n\n\tconst codepoint = parseInt(match[1]!, 10);\n\treturn codepoint >= 32 ? codepoint : undefined;\n}\n\nfunction extractCompleteSequences(buffer: string): { sequences: string[]; remainder: string } {\n\tconst sequences: string[] = [];\n\tlet pos = 0;\n\n\twhile (pos < buffer.length) {\n\t\tconst remaining = buffer.slice(pos);\n\n\t\tif (remaining.startsWith(ESC)) {\n\t\t\tlet seqEnd = 1;\n\t\t\twhile (seqEnd <= remaining.length) {\n\t\t\t\tconst candidate = remaining.slice(0, seqEnd);\n\t\t\t\tconst status = isCompleteSequence(candidate);\n\n\t\t\t\tif (status === \"complete\") {\n\t\t\t\t\tsequences.push(candidate);\n\t\t\t\t\tpos += seqEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t} else if (status === \"incomplete\") {\n\t\t\t\t\tseqEnd++;\n\t\t\t\t} else {\n\t\t\t\t\tsequences.push(candidate);\n\t\t\t\t\tpos += seqEnd;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (seqEnd > remaining.length) {\n\t\t\t\treturn { sequences, remainder: remaining };\n\t\t\t}\n\t\t} else {\n\t\t\tsequences.push(remaining[0]!);\n\t\t\tpos++;\n\t\t}\n\t}\n\n\treturn { sequences, remainder: \"\" };\n}\n\nexport type StdinBufferOptions = {\n\t/**\n\t * Maximum time to wait for sequence completion (default: 10ms)\n\t * After this time, the buffer is flushed even if incomplete\n\t */\n\ttimeout?: number;\n};\n\nexport type StdinBufferEventMap = {\n\tdata: [string];\n\tpaste: [string];\n};\n\n/**\n * Buffers stdin input and emits complete sequences via the 'data' event.\n * Handles partial escape sequences that arrive across multiple chunks.\n */\nexport class StdinBuffer extends EventEmitter<StdinBufferEventMap> {\n\tprivate buffer: string = \"\";\n\tprivate timeout: ReturnType<typeof setTimeout> | null = null;\n\tprivate readonly timeoutMs: number;\n\tprivate pasteMode: boolean = false;\n\tprivate pasteBuffer: string = \"\";\n\tprivate pendingKittyPrintableCodepoint: number | undefined;\n\n\tconstructor(options: StdinBufferOptions = {}) {\n\t\tsuper();\n\t\tthis.timeoutMs = options.timeout ?? 10;\n\t}\n\n\tpublic process(data: string | Buffer): void {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\n\t\t// Handle high-byte conversion (for compatibility with parseKeypress)\n\t\t// If buffer has single byte > 127, convert to ESC + (byte - 128)\n\t\tlet str: string;\n\t\tif (Buffer.isBuffer(data)) {\n\t\t\tif (data.length === 1 && data[0]! > 127) {\n\t\t\t\tconst byte = data[0]! - 128;\n\t\t\t\tstr = `\\x1b${String.fromCharCode(byte)}`;\n\t\t\t} else {\n\t\t\t\tstr = data.toString();\n\t\t\t}\n\t\t} else {\n\t\t\tstr = data;\n\t\t}\n\n\t\tif (str.length === 0 && this.buffer.length === 0) {\n\t\t\tthis.emitDataSequence(\"\");\n\t\t\treturn;\n\t\t}\n\n\t\tthis.buffer += str;\n\n\t\tif (this.pasteMode) {\n\t\t\tthis.pasteBuffer += this.buffer;\n\t\t\tthis.buffer = \"\";\n\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pastedContent = this.pasteBuffer.slice(0, endIndex);\n\t\t\t\tconst remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);\n\n\t\t\t\tthis.pasteMode = false;\n\t\t\t\tthis.pasteBuffer = \"\";\n\t\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\n\t\t\t\tthis.emit(\"paste\", pastedContent);\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.process(remaining);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst startIndex = this.buffer.indexOf(BRACKETED_PASTE_START);\n\t\tif (startIndex !== -1) {\n\t\t\tif (startIndex > 0) {\n\t\t\t\tconst beforePaste = this.buffer.slice(0, startIndex);\n\t\t\t\tconst result = extractCompleteSequences(beforePaste);\n\t\t\t\tfor (const sequence of result.sequences) {\n\t\t\t\t\tthis.emitDataSequence(sequence);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t\t\tthis.buffer = this.buffer.slice(startIndex + BRACKETED_PASTE_START.length);\n\t\t\tthis.pasteMode = true;\n\t\t\tthis.pasteBuffer = this.buffer;\n\t\t\tthis.buffer = \"\";\n\n\t\t\tconst endIndex = this.pasteBuffer.indexOf(BRACKETED_PASTE_END);\n\t\t\tif (endIndex !== -1) {\n\t\t\t\tconst pastedContent = this.pasteBuffer.slice(0, endIndex);\n\t\t\t\tconst remaining = this.pasteBuffer.slice(endIndex + BRACKETED_PASTE_END.length);\n\n\t\t\t\tthis.pasteMode = false;\n\t\t\t\tthis.pasteBuffer = \"\";\n\t\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\n\t\t\t\tthis.emit(\"paste\", pastedContent);\n\n\t\t\t\tif (remaining.length > 0) {\n\t\t\t\t\tthis.process(remaining);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst result = extractCompleteSequences(this.buffer);\n\t\tthis.buffer = result.remainder;\n\n\t\tfor (const sequence of result.sequences) {\n\t\t\tthis.emitDataSequence(sequence);\n\t\t}\n\n\t\tif (this.buffer.length > 0) {\n\t\t\tthis.timeout = setTimeout(() => {\n\t\t\t\tconst flushed = this.flush();\n\n\t\t\t\tfor (const sequence of flushed) {\n\t\t\t\t\tthis.emitDataSequence(sequence);\n\t\t\t\t}\n\t\t\t}, this.timeoutMs);\n\t\t}\n\t}\n\n\tprivate emitDataSequence(sequence: string): void {\n\t\tconst rawCodepoint = sequence.length === 1 ? sequence.codePointAt(0) : undefined;\n\t\tif (rawCodepoint !== undefined && rawCodepoint === this.pendingKittyPrintableCodepoint) {\n\t\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t\t\treturn;\n\t\t}\n\n\t\tthis.pendingKittyPrintableCodepoint = parseUnmodifiedKittyPrintableCodepoint(sequence);\n\t\tthis.emit(\"data\", sequence);\n\t}\n\n\tflush(): string[] {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\n\t\tif (this.buffer.length === 0) {\n\t\t\treturn [];\n\t\t}\n\n\t\tconst sequences = [this.buffer];\n\t\tthis.buffer = \"\";\n\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t\treturn sequences;\n\t}\n\n\tclear(): void {\n\t\tif (this.timeout) {\n\t\t\tclearTimeout(this.timeout);\n\t\t\tthis.timeout = null;\n\t\t}\n\t\tthis.buffer = \"\";\n\t\tthis.pasteMode = false;\n\t\tthis.pasteBuffer = \"\";\n\t\tthis.pendingKittyPrintableCodepoint = undefined;\n\t}\n\n\tgetBuffer(): string {\n\t\treturn this.buffer;\n\t}\n\n\tdestroy(): void {\n\t\tthis.clear();\n\t}\n}\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export interface Rgb {
|
|
2
|
+
r: number;
|
|
3
|
+
g: number;
|
|
4
|
+
b: number;
|
|
5
|
+
}
|
|
6
|
+
export interface DefaultTerminalColors {
|
|
7
|
+
foreground: Rgb;
|
|
8
|
+
background: Rgb;
|
|
9
|
+
}
|
|
10
|
+
export type TerminalBackgroundKind = "dark" | "light";
|
|
11
|
+
export type TerminalColorMode = "truecolor" | "256color" | "ansi16" | "unknown";
|
|
12
|
+
export type OscColorKind = "foreground" | "background";
|
|
13
|
+
export interface OscColorResponse {
|
|
14
|
+
kind: OscColorKind;
|
|
15
|
+
rgb: Rgb;
|
|
16
|
+
}
|
|
17
|
+
export declare const QUERY_DEFAULT_FOREGROUND = "\u001B]10;?\u001B\\";
|
|
18
|
+
export declare const QUERY_DEFAULT_BACKGROUND = "\u001B]11;?\u001B\\";
|
|
19
|
+
export declare function rgbToHex(rgb: Rgb): string;
|
|
20
|
+
export declare function isLightColor(rgb: Rgb): boolean;
|
|
21
|
+
export declare function blendColor(top: Rgb, bottom: Rgb, alpha: number): Rgb;
|
|
22
|
+
export declare function rgbTo256(rgb: Rgb): number;
|
|
23
|
+
export declare function bestAnsiColor(rgb: Rgb, mode: TerminalColorMode): string | number;
|
|
24
|
+
export declare function parseOscColorResponse(sequence: string): OscColorResponse | undefined;
|
|
25
|
+
export declare function detectBackgroundFromColorFgBg(value?: string | undefined): TerminalBackgroundKind | undefined;
|
|
26
|
+
export declare function getDefaultTerminalColors(): DefaultTerminalColors | undefined;
|
|
27
|
+
export declare function setDefaultTerminalColors(colors: DefaultTerminalColors | undefined): void;
|
|
28
|
+
export declare function clearDefaultTerminalColors(): void;
|
|
29
|
+
export declare function getTerminalBackgroundKind(): TerminalBackgroundKind | undefined;
|
|
30
|
+
export declare function onDefaultTerminalColorsChange(listener: () => void): () => void;
|
|
31
|
+
//# sourceMappingURL=terminal-colors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-colors.d.ts","sourceRoot":"","sources":["../src/terminal-colors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,GAAG;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED,MAAM,WAAW,qBAAqB;IACrC,UAAU,EAAE,GAAG,CAAC;IAChB,UAAU,EAAE,GAAG,CAAC;CAChB;AAED,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,OAAO,CAAC;AACtD,MAAM,MAAM,iBAAiB,GAAG,WAAW,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,CAAC;AAChF,MAAM,MAAM,YAAY,GAAG,YAAY,GAAG,YAAY,CAAC;AAEvD,MAAM,WAAW,gBAAgB;IAChC,IAAI,EAAE,YAAY,CAAC;IACnB,GAAG,EAAE,GAAG,CAAC;CACT;AAED,eAAO,MAAM,wBAAwB,wBAAoB,CAAC;AAC1D,eAAO,MAAM,wBAAwB,wBAAoB,CAAC;AA0E1D,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAGzC;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,GAAG,OAAO,CAE9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,GAAG,GAAG,CAOpE;AAED,wBAAgB,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CAsBzC;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,MAAM,CAQhF;AAED,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAapF;AAED,wBAAgB,6BAA6B,CAC5C,KAAK,GAAE,MAAM,GAAG,SAAiC,GAC/C,sBAAsB,GAAG,SAAS,CAapC;AAED,wBAAgB,wBAAwB,IAAI,qBAAqB,GAAG,SAAS,CAE5E;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,GAAG,IAAI,CAGxF;AAED,wBAAgB,0BAA0B,IAAI,IAAI,CAEjD;AAED,wBAAgB,yBAAyB,IAAI,sBAAsB,GAAG,SAAS,CAM9E;AAED,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAK9E","sourcesContent":["export interface Rgb {\n\tr: number;\n\tg: number;\n\tb: number;\n}\n\nexport interface DefaultTerminalColors {\n\tforeground: Rgb;\n\tbackground: Rgb;\n}\n\nexport type TerminalBackgroundKind = \"dark\" | \"light\";\nexport type TerminalColorMode = \"truecolor\" | \"256color\" | \"ansi16\" | \"unknown\";\nexport type OscColorKind = \"foreground\" | \"background\";\n\nexport interface OscColorResponse {\n\tkind: OscColorKind;\n\trgb: Rgb;\n}\n\nexport const QUERY_DEFAULT_FOREGROUND = \"\\x1b]10;?\\x1b\\\\\";\nexport const QUERY_DEFAULT_BACKGROUND = \"\\x1b]11;?\\x1b\\\\\";\n\nconst CUBE_VALUES = [0, 95, 135, 175, 215, 255];\nconst GRAY_VALUES = Array.from({ length: 24 }, (_, i) => 8 + i * 10);\nconst defaultColorListeners = new Set<() => void>();\n\nlet defaultTerminalColors: DefaultTerminalColors | undefined;\n\nfunction clampChannel(value: number): number {\n\treturn Math.max(0, Math.min(255, Math.round(value)));\n}\n\nfunction normalizeHexChannel(value: string): number | undefined {\n\tif (!/^[0-9a-fA-F]{2,4}$/.test(value)) {\n\t\treturn undefined;\n\t}\n\tconst parsed = parseInt(value, 16);\n\tif (Number.isNaN(parsed)) {\n\t\treturn undefined;\n\t}\n\tif (value.length <= 2) {\n\t\treturn parsed;\n\t}\n\tconst max = 16 ** value.length - 1;\n\treturn Math.round((parsed / max) * 255);\n}\n\nfunction parseRgbPayload(payload: string): Rgb | undefined {\n\tconst rgbMatch = payload.match(/^rgb:([0-9a-fA-F]{2,4})\\/([0-9a-fA-F]{2,4})\\/([0-9a-fA-F]{2,4})$/);\n\tif (rgbMatch) {\n\t\tconst r = normalizeHexChannel(rgbMatch[1]!);\n\t\tconst g = normalizeHexChannel(rgbMatch[2]!);\n\t\tconst b = normalizeHexChannel(rgbMatch[3]!);\n\t\treturn r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };\n\t}\n\n\tconst hexMatch = payload.match(/^#?([0-9a-fA-F]{6})$/);\n\tif (!hexMatch) {\n\t\treturn undefined;\n\t}\n\tconst hex = hexMatch[1]!;\n\treturn {\n\t\tr: parseInt(hex.slice(0, 2), 16),\n\t\tg: parseInt(hex.slice(2, 4), 16),\n\t\tb: parseInt(hex.slice(4, 6), 16),\n\t};\n}\n\nfunction colorDistance(a: Rgb, b: Rgb): number {\n\tconst dr = a.r - b.r;\n\tconst dg = a.g - b.g;\n\tconst db = a.b - b.b;\n\treturn dr * dr * 0.299 + dg * dg * 0.587 + db * db * 0.114;\n}\n\nfunction findClosestIndex(value: number, values: readonly number[]): number {\n\tlet minDist = Infinity;\n\tlet minIdx = 0;\n\tfor (let i = 0; i < values.length; i++) {\n\t\tconst dist = Math.abs(value - values[i]!);\n\t\tif (dist < minDist) {\n\t\t\tminDist = dist;\n\t\t\tminIdx = i;\n\t\t}\n\t}\n\treturn minIdx;\n}\n\nfunction notifyDefaultColorListeners(): void {\n\tfor (const listener of defaultColorListeners) {\n\t\tlistener();\n\t}\n}\n\nexport function rgbToHex(rgb: Rgb): string {\n\tconst toHex = (value: number) => clampChannel(value).toString(16).padStart(2, \"0\");\n\treturn `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;\n}\n\nexport function isLightColor(rgb: Rgb): boolean {\n\treturn 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b > 128;\n}\n\nexport function blendColor(top: Rgb, bottom: Rgb, alpha: number): Rgb {\n\tconst clampedAlpha = Math.max(0, Math.min(1, alpha));\n\treturn {\n\t\tr: clampChannel(top.r * clampedAlpha + bottom.r * (1 - clampedAlpha)),\n\t\tg: clampChannel(top.g * clampedAlpha + bottom.g * (1 - clampedAlpha)),\n\t\tb: clampChannel(top.b * clampedAlpha + bottom.b * (1 - clampedAlpha)),\n\t};\n}\n\nexport function rgbTo256(rgb: Rgb): number {\n\tconst rIdx = findClosestIndex(rgb.r, CUBE_VALUES);\n\tconst gIdx = findClosestIndex(rgb.g, CUBE_VALUES);\n\tconst bIdx = findClosestIndex(rgb.b, CUBE_VALUES);\n\tconst cubeRgb = { r: CUBE_VALUES[rIdx]!, g: CUBE_VALUES[gIdx]!, b: CUBE_VALUES[bIdx]! };\n\tconst cubeIndex = 16 + 36 * rIdx + 6 * gIdx + bIdx;\n\tconst cubeDist = colorDistance(rgb, cubeRgb);\n\n\tconst gray = Math.round(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);\n\tconst grayIdx = findClosestIndex(gray, GRAY_VALUES);\n\tconst grayValue = GRAY_VALUES[grayIdx]!;\n\tconst grayRgb = { r: grayValue, g: grayValue, b: grayValue };\n\tconst grayIndex = 232 + grayIdx;\n\tconst grayDist = colorDistance(rgb, grayRgb);\n\n\tconst maxChannel = Math.max(rgb.r, rgb.g, rgb.b);\n\tconst minChannel = Math.min(rgb.r, rgb.g, rgb.b);\n\tif (maxChannel - minChannel < 10 && grayDist < cubeDist) {\n\t\treturn grayIndex;\n\t}\n\n\treturn cubeIndex;\n}\n\nexport function bestAnsiColor(rgb: Rgb, mode: TerminalColorMode): string | number {\n\tif (mode === \"truecolor\") {\n\t\treturn rgbToHex(rgb);\n\t}\n\tif (mode === \"256color\") {\n\t\treturn rgbTo256(rgb);\n\t}\n\treturn \"\";\n}\n\nexport function parseOscColorResponse(sequence: string): OscColorResponse | undefined {\n\tconst match = sequence.match(/^\\x1b\\](10|11);([^\\x07\\x1b]+)(?:\\x07|\\x1b\\\\)$/);\n\tif (!match) {\n\t\treturn undefined;\n\t}\n\tconst rgb = parseRgbPayload(match[2]!);\n\tif (!rgb) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tkind: match[1] === \"10\" ? \"foreground\" : \"background\",\n\t\trgb,\n\t};\n}\n\nexport function detectBackgroundFromColorFgBg(\n\tvalue: string | undefined = process.env.COLORFGBG,\n): TerminalBackgroundKind | undefined {\n\tif (!value) {\n\t\treturn undefined;\n\t}\n\tconst parts = value.split(\";\");\n\tif (parts.length < 2) {\n\t\treturn undefined;\n\t}\n\tconst bg = parseInt(parts[1]!, 10);\n\tif (Number.isNaN(bg)) {\n\t\treturn undefined;\n\t}\n\treturn bg < 8 ? \"dark\" : \"light\";\n}\n\nexport function getDefaultTerminalColors(): DefaultTerminalColors | undefined {\n\treturn defaultTerminalColors;\n}\n\nexport function setDefaultTerminalColors(colors: DefaultTerminalColors | undefined): void {\n\tdefaultTerminalColors = colors;\n\tnotifyDefaultColorListeners();\n}\n\nexport function clearDefaultTerminalColors(): void {\n\tsetDefaultTerminalColors(undefined);\n}\n\nexport function getTerminalBackgroundKind(): TerminalBackgroundKind | undefined {\n\tconst bg = defaultTerminalColors?.background;\n\tif (bg) {\n\t\treturn isLightColor(bg) ? \"light\" : \"dark\";\n\t}\n\treturn detectBackgroundFromColorFgBg();\n}\n\nexport function onDefaultTerminalColorsChange(listener: () => void): () => void {\n\tdefaultColorListeners.add(listener);\n\treturn () => {\n\t\tdefaultColorListeners.delete(listener);\n\t};\n}\n"]}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
export const QUERY_DEFAULT_FOREGROUND = "\x1b]10;?\x1b\\";
|
|
2
|
+
export const QUERY_DEFAULT_BACKGROUND = "\x1b]11;?\x1b\\";
|
|
3
|
+
const CUBE_VALUES = [0, 95, 135, 175, 215, 255];
|
|
4
|
+
const GRAY_VALUES = Array.from({ length: 24 }, (_, i) => 8 + i * 10);
|
|
5
|
+
const defaultColorListeners = new Set();
|
|
6
|
+
let defaultTerminalColors;
|
|
7
|
+
function clampChannel(value) {
|
|
8
|
+
return Math.max(0, Math.min(255, Math.round(value)));
|
|
9
|
+
}
|
|
10
|
+
function normalizeHexChannel(value) {
|
|
11
|
+
if (!/^[0-9a-fA-F]{2,4}$/.test(value)) {
|
|
12
|
+
return undefined;
|
|
13
|
+
}
|
|
14
|
+
const parsed = parseInt(value, 16);
|
|
15
|
+
if (Number.isNaN(parsed)) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
if (value.length <= 2) {
|
|
19
|
+
return parsed;
|
|
20
|
+
}
|
|
21
|
+
const max = 16 ** value.length - 1;
|
|
22
|
+
return Math.round((parsed / max) * 255);
|
|
23
|
+
}
|
|
24
|
+
function parseRgbPayload(payload) {
|
|
25
|
+
const rgbMatch = payload.match(/^rgb:([0-9a-fA-F]{2,4})\/([0-9a-fA-F]{2,4})\/([0-9a-fA-F]{2,4})$/);
|
|
26
|
+
if (rgbMatch) {
|
|
27
|
+
const r = normalizeHexChannel(rgbMatch[1]);
|
|
28
|
+
const g = normalizeHexChannel(rgbMatch[2]);
|
|
29
|
+
const b = normalizeHexChannel(rgbMatch[3]);
|
|
30
|
+
return r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };
|
|
31
|
+
}
|
|
32
|
+
const hexMatch = payload.match(/^#?([0-9a-fA-F]{6})$/);
|
|
33
|
+
if (!hexMatch) {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const hex = hexMatch[1];
|
|
37
|
+
return {
|
|
38
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
39
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
40
|
+
b: parseInt(hex.slice(4, 6), 16),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function colorDistance(a, b) {
|
|
44
|
+
const dr = a.r - b.r;
|
|
45
|
+
const dg = a.g - b.g;
|
|
46
|
+
const db = a.b - b.b;
|
|
47
|
+
return dr * dr * 0.299 + dg * dg * 0.587 + db * db * 0.114;
|
|
48
|
+
}
|
|
49
|
+
function findClosestIndex(value, values) {
|
|
50
|
+
let minDist = Infinity;
|
|
51
|
+
let minIdx = 0;
|
|
52
|
+
for (let i = 0; i < values.length; i++) {
|
|
53
|
+
const dist = Math.abs(value - values[i]);
|
|
54
|
+
if (dist < minDist) {
|
|
55
|
+
minDist = dist;
|
|
56
|
+
minIdx = i;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return minIdx;
|
|
60
|
+
}
|
|
61
|
+
function notifyDefaultColorListeners() {
|
|
62
|
+
for (const listener of defaultColorListeners) {
|
|
63
|
+
listener();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export function rgbToHex(rgb) {
|
|
67
|
+
const toHex = (value) => clampChannel(value).toString(16).padStart(2, "0");
|
|
68
|
+
return `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;
|
|
69
|
+
}
|
|
70
|
+
export function isLightColor(rgb) {
|
|
71
|
+
return 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b > 128;
|
|
72
|
+
}
|
|
73
|
+
export function blendColor(top, bottom, alpha) {
|
|
74
|
+
const clampedAlpha = Math.max(0, Math.min(1, alpha));
|
|
75
|
+
return {
|
|
76
|
+
r: clampChannel(top.r * clampedAlpha + bottom.r * (1 - clampedAlpha)),
|
|
77
|
+
g: clampChannel(top.g * clampedAlpha + bottom.g * (1 - clampedAlpha)),
|
|
78
|
+
b: clampChannel(top.b * clampedAlpha + bottom.b * (1 - clampedAlpha)),
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
export function rgbTo256(rgb) {
|
|
82
|
+
const rIdx = findClosestIndex(rgb.r, CUBE_VALUES);
|
|
83
|
+
const gIdx = findClosestIndex(rgb.g, CUBE_VALUES);
|
|
84
|
+
const bIdx = findClosestIndex(rgb.b, CUBE_VALUES);
|
|
85
|
+
const cubeRgb = { r: CUBE_VALUES[rIdx], g: CUBE_VALUES[gIdx], b: CUBE_VALUES[bIdx] };
|
|
86
|
+
const cubeIndex = 16 + 36 * rIdx + 6 * gIdx + bIdx;
|
|
87
|
+
const cubeDist = colorDistance(rgb, cubeRgb);
|
|
88
|
+
const gray = Math.round(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);
|
|
89
|
+
const grayIdx = findClosestIndex(gray, GRAY_VALUES);
|
|
90
|
+
const grayValue = GRAY_VALUES[grayIdx];
|
|
91
|
+
const grayRgb = { r: grayValue, g: grayValue, b: grayValue };
|
|
92
|
+
const grayIndex = 232 + grayIdx;
|
|
93
|
+
const grayDist = colorDistance(rgb, grayRgb);
|
|
94
|
+
const maxChannel = Math.max(rgb.r, rgb.g, rgb.b);
|
|
95
|
+
const minChannel = Math.min(rgb.r, rgb.g, rgb.b);
|
|
96
|
+
if (maxChannel - minChannel < 10 && grayDist < cubeDist) {
|
|
97
|
+
return grayIndex;
|
|
98
|
+
}
|
|
99
|
+
return cubeIndex;
|
|
100
|
+
}
|
|
101
|
+
export function bestAnsiColor(rgb, mode) {
|
|
102
|
+
if (mode === "truecolor") {
|
|
103
|
+
return rgbToHex(rgb);
|
|
104
|
+
}
|
|
105
|
+
if (mode === "256color") {
|
|
106
|
+
return rgbTo256(rgb);
|
|
107
|
+
}
|
|
108
|
+
return "";
|
|
109
|
+
}
|
|
110
|
+
export function parseOscColorResponse(sequence) {
|
|
111
|
+
const match = sequence.match(/^\x1b\](10|11);([^\x07\x1b]+)(?:\x07|\x1b\\)$/);
|
|
112
|
+
if (!match) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const rgb = parseRgbPayload(match[2]);
|
|
116
|
+
if (!rgb) {
|
|
117
|
+
return undefined;
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
kind: match[1] === "10" ? "foreground" : "background",
|
|
121
|
+
rgb,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function detectBackgroundFromColorFgBg(value = process.env.COLORFGBG) {
|
|
125
|
+
if (!value) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
const parts = value.split(";");
|
|
129
|
+
if (parts.length < 2) {
|
|
130
|
+
return undefined;
|
|
131
|
+
}
|
|
132
|
+
const bg = parseInt(parts[1], 10);
|
|
133
|
+
if (Number.isNaN(bg)) {
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
return bg < 8 ? "dark" : "light";
|
|
137
|
+
}
|
|
138
|
+
export function getDefaultTerminalColors() {
|
|
139
|
+
return defaultTerminalColors;
|
|
140
|
+
}
|
|
141
|
+
export function setDefaultTerminalColors(colors) {
|
|
142
|
+
defaultTerminalColors = colors;
|
|
143
|
+
notifyDefaultColorListeners();
|
|
144
|
+
}
|
|
145
|
+
export function clearDefaultTerminalColors() {
|
|
146
|
+
setDefaultTerminalColors(undefined);
|
|
147
|
+
}
|
|
148
|
+
export function getTerminalBackgroundKind() {
|
|
149
|
+
const bg = defaultTerminalColors?.background;
|
|
150
|
+
if (bg) {
|
|
151
|
+
return isLightColor(bg) ? "light" : "dark";
|
|
152
|
+
}
|
|
153
|
+
return detectBackgroundFromColorFgBg();
|
|
154
|
+
}
|
|
155
|
+
export function onDefaultTerminalColorsChange(listener) {
|
|
156
|
+
defaultColorListeners.add(listener);
|
|
157
|
+
return () => {
|
|
158
|
+
defaultColorListeners.delete(listener);
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=terminal-colors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"terminal-colors.js","sourceRoot":"","sources":["../src/terminal-colors.ts"],"names":[],"mappings":"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","sourcesContent":["export interface Rgb {\n\tr: number;\n\tg: number;\n\tb: number;\n}\n\nexport interface DefaultTerminalColors {\n\tforeground: Rgb;\n\tbackground: Rgb;\n}\n\nexport type TerminalBackgroundKind = \"dark\" | \"light\";\nexport type TerminalColorMode = \"truecolor\" | \"256color\" | \"ansi16\" | \"unknown\";\nexport type OscColorKind = \"foreground\" | \"background\";\n\nexport interface OscColorResponse {\n\tkind: OscColorKind;\n\trgb: Rgb;\n}\n\nexport const QUERY_DEFAULT_FOREGROUND = \"\\x1b]10;?\\x1b\\\\\";\nexport const QUERY_DEFAULT_BACKGROUND = \"\\x1b]11;?\\x1b\\\\\";\n\nconst CUBE_VALUES = [0, 95, 135, 175, 215, 255];\nconst GRAY_VALUES = Array.from({ length: 24 }, (_, i) => 8 + i * 10);\nconst defaultColorListeners = new Set<() => void>();\n\nlet defaultTerminalColors: DefaultTerminalColors | undefined;\n\nfunction clampChannel(value: number): number {\n\treturn Math.max(0, Math.min(255, Math.round(value)));\n}\n\nfunction normalizeHexChannel(value: string): number | undefined {\n\tif (!/^[0-9a-fA-F]{2,4}$/.test(value)) {\n\t\treturn undefined;\n\t}\n\tconst parsed = parseInt(value, 16);\n\tif (Number.isNaN(parsed)) {\n\t\treturn undefined;\n\t}\n\tif (value.length <= 2) {\n\t\treturn parsed;\n\t}\n\tconst max = 16 ** value.length - 1;\n\treturn Math.round((parsed / max) * 255);\n}\n\nfunction parseRgbPayload(payload: string): Rgb | undefined {\n\tconst rgbMatch = payload.match(/^rgb:([0-9a-fA-F]{2,4})\\/([0-9a-fA-F]{2,4})\\/([0-9a-fA-F]{2,4})$/);\n\tif (rgbMatch) {\n\t\tconst r = normalizeHexChannel(rgbMatch[1]!);\n\t\tconst g = normalizeHexChannel(rgbMatch[2]!);\n\t\tconst b = normalizeHexChannel(rgbMatch[3]!);\n\t\treturn r === undefined || g === undefined || b === undefined ? undefined : { r, g, b };\n\t}\n\n\tconst hexMatch = payload.match(/^#?([0-9a-fA-F]{6})$/);\n\tif (!hexMatch) {\n\t\treturn undefined;\n\t}\n\tconst hex = hexMatch[1]!;\n\treturn {\n\t\tr: parseInt(hex.slice(0, 2), 16),\n\t\tg: parseInt(hex.slice(2, 4), 16),\n\t\tb: parseInt(hex.slice(4, 6), 16),\n\t};\n}\n\nfunction colorDistance(a: Rgb, b: Rgb): number {\n\tconst dr = a.r - b.r;\n\tconst dg = a.g - b.g;\n\tconst db = a.b - b.b;\n\treturn dr * dr * 0.299 + dg * dg * 0.587 + db * db * 0.114;\n}\n\nfunction findClosestIndex(value: number, values: readonly number[]): number {\n\tlet minDist = Infinity;\n\tlet minIdx = 0;\n\tfor (let i = 0; i < values.length; i++) {\n\t\tconst dist = Math.abs(value - values[i]!);\n\t\tif (dist < minDist) {\n\t\t\tminDist = dist;\n\t\t\tminIdx = i;\n\t\t}\n\t}\n\treturn minIdx;\n}\n\nfunction notifyDefaultColorListeners(): void {\n\tfor (const listener of defaultColorListeners) {\n\t\tlistener();\n\t}\n}\n\nexport function rgbToHex(rgb: Rgb): string {\n\tconst toHex = (value: number) => clampChannel(value).toString(16).padStart(2, \"0\");\n\treturn `#${toHex(rgb.r)}${toHex(rgb.g)}${toHex(rgb.b)}`;\n}\n\nexport function isLightColor(rgb: Rgb): boolean {\n\treturn 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b > 128;\n}\n\nexport function blendColor(top: Rgb, bottom: Rgb, alpha: number): Rgb {\n\tconst clampedAlpha = Math.max(0, Math.min(1, alpha));\n\treturn {\n\t\tr: clampChannel(top.r * clampedAlpha + bottom.r * (1 - clampedAlpha)),\n\t\tg: clampChannel(top.g * clampedAlpha + bottom.g * (1 - clampedAlpha)),\n\t\tb: clampChannel(top.b * clampedAlpha + bottom.b * (1 - clampedAlpha)),\n\t};\n}\n\nexport function rgbTo256(rgb: Rgb): number {\n\tconst rIdx = findClosestIndex(rgb.r, CUBE_VALUES);\n\tconst gIdx = findClosestIndex(rgb.g, CUBE_VALUES);\n\tconst bIdx = findClosestIndex(rgb.b, CUBE_VALUES);\n\tconst cubeRgb = { r: CUBE_VALUES[rIdx]!, g: CUBE_VALUES[gIdx]!, b: CUBE_VALUES[bIdx]! };\n\tconst cubeIndex = 16 + 36 * rIdx + 6 * gIdx + bIdx;\n\tconst cubeDist = colorDistance(rgb, cubeRgb);\n\n\tconst gray = Math.round(0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b);\n\tconst grayIdx = findClosestIndex(gray, GRAY_VALUES);\n\tconst grayValue = GRAY_VALUES[grayIdx]!;\n\tconst grayRgb = { r: grayValue, g: grayValue, b: grayValue };\n\tconst grayIndex = 232 + grayIdx;\n\tconst grayDist = colorDistance(rgb, grayRgb);\n\n\tconst maxChannel = Math.max(rgb.r, rgb.g, rgb.b);\n\tconst minChannel = Math.min(rgb.r, rgb.g, rgb.b);\n\tif (maxChannel - minChannel < 10 && grayDist < cubeDist) {\n\t\treturn grayIndex;\n\t}\n\n\treturn cubeIndex;\n}\n\nexport function bestAnsiColor(rgb: Rgb, mode: TerminalColorMode): string | number {\n\tif (mode === \"truecolor\") {\n\t\treturn rgbToHex(rgb);\n\t}\n\tif (mode === \"256color\") {\n\t\treturn rgbTo256(rgb);\n\t}\n\treturn \"\";\n}\n\nexport function parseOscColorResponse(sequence: string): OscColorResponse | undefined {\n\tconst match = sequence.match(/^\\x1b\\](10|11);([^\\x07\\x1b]+)(?:\\x07|\\x1b\\\\)$/);\n\tif (!match) {\n\t\treturn undefined;\n\t}\n\tconst rgb = parseRgbPayload(match[2]!);\n\tif (!rgb) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tkind: match[1] === \"10\" ? \"foreground\" : \"background\",\n\t\trgb,\n\t};\n}\n\nexport function detectBackgroundFromColorFgBg(\n\tvalue: string | undefined = process.env.COLORFGBG,\n): TerminalBackgroundKind | undefined {\n\tif (!value) {\n\t\treturn undefined;\n\t}\n\tconst parts = value.split(\";\");\n\tif (parts.length < 2) {\n\t\treturn undefined;\n\t}\n\tconst bg = parseInt(parts[1]!, 10);\n\tif (Number.isNaN(bg)) {\n\t\treturn undefined;\n\t}\n\treturn bg < 8 ? \"dark\" : \"light\";\n}\n\nexport function getDefaultTerminalColors(): DefaultTerminalColors | undefined {\n\treturn defaultTerminalColors;\n}\n\nexport function setDefaultTerminalColors(colors: DefaultTerminalColors | undefined): void {\n\tdefaultTerminalColors = colors;\n\tnotifyDefaultColorListeners();\n}\n\nexport function clearDefaultTerminalColors(): void {\n\tsetDefaultTerminalColors(undefined);\n}\n\nexport function getTerminalBackgroundKind(): TerminalBackgroundKind | undefined {\n\tconst bg = defaultTerminalColors?.background;\n\tif (bg) {\n\t\treturn isLightColor(bg) ? \"light\" : \"dark\";\n\t}\n\treturn detectBackgroundFromColorFgBg();\n}\n\nexport function onDefaultTerminalColorsChange(listener: () => void): () => void {\n\tdefaultColorListeners.add(listener);\n\treturn () => {\n\t\tdefaultColorListeners.delete(listener);\n\t};\n}\n"]}
|