@linxiraos/pi-utils 1.1.27 → 1.2.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/CHANGELOG.md +4 -6
- package/README.md +1 -1
- package/dist/types/ar/index.d.ts +1 -0
- package/dist/types/ar/zip-package.d.ts +21 -0
- package/dist/types/color.d.ts +2 -0
- package/dist/types/dirs.d.ts +31 -12
- package/dist/types/docx/converter.d.ts +2 -0
- package/dist/types/dom/core.d.ts +15 -4
- package/dist/types/dom/parser.d.ts +5 -3
- package/dist/types/dom/selector.d.ts +7 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/logger/rotating-file.d.ts +4 -2
- package/dist/types/loop-phase.d.ts +12 -7
- package/dist/types/marked/core.d.ts +7 -6
- package/dist/types/marked-list.d.ts +38 -0
- package/dist/types/math-delimiters.d.ts +10 -0
- package/dist/types/readability/types.d.ts +2 -0
- package/dist/types/release-assets.d.ts +22 -0
- package/dist/types/temp.d.ts +7 -0
- package/dist/types/turndown/types.d.ts +7 -0
- package/dist/types/vterm/buffer.d.ts +16 -5
- package/package.json +3 -3
- package/src/ar/index.ts +1 -0
- package/src/ar/open.ts +29 -8
- package/src/ar/zip-package.ts +57 -0
- package/src/chalk.ts +66 -23
- package/src/color.ts +6 -0
- package/src/dirs.ts +84 -24
- package/src/docx/converter.ts +26 -12
- package/src/dom/core.ts +354 -111
- package/src/dom/parser.ts +42 -19
- package/src/dom/selector.ts +328 -95
- package/src/fetch-retry.ts +2 -0
- package/src/format.ts +5 -1
- package/src/index.ts +1 -0
- package/src/json-lexer.ts +54 -0
- package/src/json-parse.ts +21 -6
- package/src/logger/rotating-file.ts +5 -3
- package/src/logger.ts +39 -23
- package/src/loop-phase.ts +66 -21
- package/src/marked/core.ts +42 -18
- package/src/marked-list.ts +73 -0
- package/src/math-delimiters.ts +16 -0
- package/src/module-timer.ts +5 -5
- package/src/postmortem.ts +1 -1
- package/src/process-name.ts +2 -2
- package/src/prompt.ts +3 -1
- package/src/ptree.ts +28 -35
- package/src/readability/readability.ts +349 -39
- package/src/readability/types.ts +2 -0
- package/src/release-assets.ts +40 -0
- package/src/runtime-install.ts +2 -2
- package/src/stream.ts +34 -13
- package/src/temp.ts +7 -0
- package/src/template.ts +7 -4
- package/src/timing-buffer.ts +1 -1
- package/src/tls-fetch.ts +1 -1
- package/src/turndown/gfm.ts +16 -5
- package/src/turndown/html.ts +28 -8
- package/src/turndown/service.ts +43 -15
- package/src/turndown/types.ts +7 -0
- package/src/vterm/buffer.ts +31 -11
- package/src/vterm/terminal.ts +213 -100
package/src/vterm/terminal.ts
CHANGED
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
BufferLine,
|
|
3
3
|
type BufferState,
|
|
4
4
|
BufferView,
|
|
5
|
-
blankCell,
|
|
6
5
|
type CellAttributes,
|
|
7
6
|
type CellData,
|
|
8
|
-
|
|
7
|
+
DEFAULT_CELL_ATTRIBUTES,
|
|
8
|
+
sharedBlankCell,
|
|
9
9
|
} from "./buffer";
|
|
10
10
|
|
|
11
11
|
const segmenter = new Intl.Segmenter();
|
|
@@ -39,7 +39,7 @@ export interface TerminalBuffers {
|
|
|
39
39
|
interface SavedCursor {
|
|
40
40
|
x: number;
|
|
41
41
|
y: number;
|
|
42
|
-
attrs: CellAttributes
|
|
42
|
+
attrs: Readonly<CellAttributes>;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function createState(columns: number, rows: number): BufferState {
|
|
@@ -52,8 +52,36 @@ function createState(columns: number, rows: number): BufferState {
|
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
function
|
|
56
|
-
return
|
|
55
|
+
function sameAttributes(a: Readonly<CellAttributes>, b: Readonly<CellAttributes>): boolean {
|
|
56
|
+
return (
|
|
57
|
+
a.bold === b.bold &&
|
|
58
|
+
a.dim === b.dim &&
|
|
59
|
+
a.italic === b.italic &&
|
|
60
|
+
a.underline === b.underline &&
|
|
61
|
+
a.inverse === b.inverse &&
|
|
62
|
+
a.strikethrough === b.strikethrough &&
|
|
63
|
+
a.overline === b.overline &&
|
|
64
|
+
a.fgMode === b.fgMode &&
|
|
65
|
+
a.fg === b.fg &&
|
|
66
|
+
a.bgMode === b.bgMode &&
|
|
67
|
+
a.bg === b.bg
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Parses CSI parameters (`;`/`:` separated); null when the body holds anything but digits. */
|
|
72
|
+
function parseCsiParams(body: string): number[] | null {
|
|
73
|
+
const params: number[] = [];
|
|
74
|
+
let value = 0;
|
|
75
|
+
for (let index = 0; index < body.length; index++) {
|
|
76
|
+
const code = body.charCodeAt(index);
|
|
77
|
+
if (code >= 0x30 && code <= 0x39) value = value * 10 + (code - 0x30);
|
|
78
|
+
else if (code === 0x3b || code === 0x3a) {
|
|
79
|
+
params.push(value);
|
|
80
|
+
value = 0;
|
|
81
|
+
} else return null;
|
|
82
|
+
}
|
|
83
|
+
params.push(value);
|
|
84
|
+
return params;
|
|
57
85
|
}
|
|
58
86
|
|
|
59
87
|
/** Behavior-compatible reimplementation of @xterm/headless's used surface. */
|
|
@@ -72,7 +100,12 @@ export class Terminal {
|
|
|
72
100
|
#alternate: BufferState;
|
|
73
101
|
#active: BufferState;
|
|
74
102
|
#usingAlternate = false;
|
|
75
|
-
|
|
103
|
+
// Renditions are immutable and shared by every cell printed with them;
|
|
104
|
+
// SGR builds a new frozen object instead of mutating this one.
|
|
105
|
+
#attrs: Readonly<CellAttributes> = DEFAULT_CELL_ATTRIBUTES;
|
|
106
|
+
// Frozen printable-ASCII cells for #attrs, indexed by char code - 0x20.
|
|
107
|
+
#asciiCells: CellData[] = [];
|
|
108
|
+
#asciiCellsAttrs: Readonly<CellAttributes> | undefined;
|
|
76
109
|
#scrollTop = 0;
|
|
77
110
|
#scrollBottom: number;
|
|
78
111
|
#originMode = false;
|
|
@@ -149,13 +182,30 @@ export class Terminal {
|
|
|
149
182
|
}
|
|
150
183
|
|
|
151
184
|
#parse(text: string): void {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
185
|
+
const length = text.length;
|
|
186
|
+
let index = 0;
|
|
187
|
+
while (index < length) {
|
|
188
|
+
if (this.#state === "ground") {
|
|
189
|
+
let end = index;
|
|
190
|
+
while (end < length) {
|
|
191
|
+
const code = text.charCodeAt(end);
|
|
192
|
+
if (code < 0x20 || code === 0x7f) break;
|
|
193
|
+
end++;
|
|
194
|
+
}
|
|
195
|
+
if (end > index) {
|
|
196
|
+
this.#printRun(text, index, end);
|
|
197
|
+
index = end;
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const char = text[index]!;
|
|
201
|
+
index++;
|
|
202
|
+
if (char === "\x1b") this.#state = "escape";
|
|
203
|
+
else this.#handleControl(char);
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
const code = text.codePointAt(index)!;
|
|
207
|
+
const char = code > 0xffff ? text.slice(index, index + 2) : text[index]!;
|
|
208
|
+
index += char.length;
|
|
159
209
|
if (this.#state === "string") {
|
|
160
210
|
if (this.#stringEscape && char === "\\") {
|
|
161
211
|
this.#state = "ground";
|
|
@@ -172,30 +222,41 @@ export class Terminal {
|
|
|
172
222
|
this.#handleEscape(char);
|
|
173
223
|
continue;
|
|
174
224
|
}
|
|
175
|
-
if (
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
} else if (char === "\x1b") {
|
|
181
|
-
this.#state = "escape";
|
|
182
|
-
this.#sequence = "";
|
|
183
|
-
} else {
|
|
184
|
-
this.#sequence += char;
|
|
185
|
-
}
|
|
186
|
-
continue;
|
|
187
|
-
}
|
|
188
|
-
if (char === "\x1b") {
|
|
189
|
-
flush();
|
|
225
|
+
if (char >= "@" && char <= "~") {
|
|
226
|
+
this.#handleCsi(this.#sequence, char);
|
|
227
|
+
this.#sequence = "";
|
|
228
|
+
this.#state = "ground";
|
|
229
|
+
} else if (char === "\x1b") {
|
|
190
230
|
this.#state = "escape";
|
|
191
|
-
|
|
192
|
-
flush();
|
|
193
|
-
this.#handleControl(char);
|
|
231
|
+
this.#sequence = "";
|
|
194
232
|
} else {
|
|
195
|
-
|
|
233
|
+
this.#sequence += char;
|
|
196
234
|
}
|
|
197
235
|
}
|
|
198
|
-
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Prints a run of non-control text. Printable ASCII is one cell wide and
|
|
240
|
+
* every boundary between two ASCII characters is a grapheme break, so ASCII
|
|
241
|
+
* stretches skip segmentation; the last ASCII character before non-ASCII text
|
|
242
|
+
* may start a cluster (combining mark, keycap) and goes to the segmenter.
|
|
243
|
+
*/
|
|
244
|
+
#printRun(text: string, start: number, end: number): void {
|
|
245
|
+
let index = start;
|
|
246
|
+
while (index < end) {
|
|
247
|
+
let asciiEnd = index;
|
|
248
|
+
while (asciiEnd < end && text.charCodeAt(asciiEnd) < 0x80) asciiEnd++;
|
|
249
|
+
if (asciiEnd < end && asciiEnd > index) asciiEnd--;
|
|
250
|
+
for (; index < asciiEnd; index++) this.#putAscii(text.charCodeAt(index));
|
|
251
|
+
if (index >= end) return;
|
|
252
|
+
// Segment up to the first break between two ASCII characters.
|
|
253
|
+
let clusterEnd = index + 1;
|
|
254
|
+
while (clusterEnd < end && !(text.charCodeAt(clusterEnd) < 0x80 && text.charCodeAt(clusterEnd - 1) < 0x80)) {
|
|
255
|
+
clusterEnd++;
|
|
256
|
+
}
|
|
257
|
+
for (const part of segmenter.segment(text.slice(index, clusterEnd))) this.#print(part.segment);
|
|
258
|
+
index = clusterEnd;
|
|
259
|
+
}
|
|
199
260
|
}
|
|
200
261
|
|
|
201
262
|
#handleControl(char: string): void {
|
|
@@ -258,15 +319,38 @@ export class Terminal {
|
|
|
258
319
|
}
|
|
259
320
|
}
|
|
260
321
|
|
|
322
|
+
/**
|
|
323
|
+
* Dispatches a CSI sequence on its whole identifier (private prefix,
|
|
324
|
+
* intermediates, final), dropping the ones without a handler as xterm does:
|
|
325
|
+
* `CSI < u` pops the kitty keyboard stack rather than restoring the cursor,
|
|
326
|
+
* `CSI > 4;1 m` sets modifyOtherKeys rather than SGR, and `CSI 0 SP q` sets
|
|
327
|
+
* the cursor shape rather than anything this buffer models.
|
|
328
|
+
*/
|
|
261
329
|
#handleCsi(raw: string, final: string): void {
|
|
262
|
-
const
|
|
263
|
-
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
.map(value => (value === "" ? 0 : Number(value)));
|
|
330
|
+
const marker = raw.charCodeAt(0);
|
|
331
|
+
// `<`, `=`, `>`, `?` open a private sequence family.
|
|
332
|
+
const prefixed = marker >= 0x3c && marker <= 0x3f;
|
|
333
|
+
// Null when intermediates or a misplaced private marker follow the parameters.
|
|
334
|
+
const params = parseCsiParams(prefixed ? raw.slice(1) : raw);
|
|
335
|
+
if (!params) return;
|
|
269
336
|
const first = params[0] ?? 0;
|
|
337
|
+
if (prefixed) {
|
|
338
|
+
if (marker !== 0x3f) return;
|
|
339
|
+
switch (final) {
|
|
340
|
+
case "h":
|
|
341
|
+
case "l":
|
|
342
|
+
this.#setModes(params, true, final === "h");
|
|
343
|
+
break;
|
|
344
|
+
// DECSED/DECSEL: no cell is ever protected, so selective erase is plain erase.
|
|
345
|
+
case "J":
|
|
346
|
+
this.#eraseDisplay(first);
|
|
347
|
+
break;
|
|
348
|
+
case "K":
|
|
349
|
+
this.#eraseLine(first);
|
|
350
|
+
break;
|
|
351
|
+
}
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
270
354
|
const amount = Math.max(1, first);
|
|
271
355
|
switch (final) {
|
|
272
356
|
case "A":
|
|
@@ -336,7 +420,7 @@ export class Terminal {
|
|
|
336
420
|
this.#setRendition(params);
|
|
337
421
|
break;
|
|
338
422
|
case "r":
|
|
339
|
-
|
|
423
|
+
this.#setScrollRegion(params);
|
|
340
424
|
break;
|
|
341
425
|
case "s":
|
|
342
426
|
this.#saveCursor();
|
|
@@ -346,25 +430,42 @@ export class Terminal {
|
|
|
346
430
|
break;
|
|
347
431
|
case "h":
|
|
348
432
|
case "l":
|
|
349
|
-
this.#setModes(params,
|
|
433
|
+
this.#setModes(params, false, final === "h");
|
|
350
434
|
break;
|
|
351
435
|
case "n":
|
|
352
|
-
if (
|
|
353
|
-
this.#emitData(`\x1b[${this.#active.cursorY + 1};${this.#active.cursorX + 1}R`);
|
|
436
|
+
if (first === 6) this.#emitData(`\x1b[${this.#active.cursorY + 1};${this.#active.cursorX + 1}R`);
|
|
354
437
|
break;
|
|
355
438
|
case "c":
|
|
356
|
-
|
|
439
|
+
this.#emitData("\x1b[?1;2c");
|
|
357
440
|
break;
|
|
358
441
|
}
|
|
359
442
|
}
|
|
360
443
|
|
|
361
444
|
#print(grapheme: string): void {
|
|
362
|
-
|
|
445
|
+
const width = Bun.stringWidth(grapheme);
|
|
363
446
|
if (width === 0) {
|
|
364
447
|
this.#appendCombining(grapheme);
|
|
365
448
|
return;
|
|
366
449
|
}
|
|
367
|
-
|
|
450
|
+
const cellWidth = Math.min(2, width);
|
|
451
|
+
this.#placeCell(Object.freeze({ chars: grapheme, width: cellWidth, attrs: this.#attrs }), cellWidth);
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
#putAscii(code: number): void {
|
|
455
|
+
if (this.#asciiCellsAttrs !== this.#attrs) {
|
|
456
|
+
this.#asciiCells = [];
|
|
457
|
+
this.#asciiCellsAttrs = this.#attrs;
|
|
458
|
+
}
|
|
459
|
+
const slot = code - 0x20;
|
|
460
|
+
let cell = this.#asciiCells[slot];
|
|
461
|
+
if (!cell) {
|
|
462
|
+
cell = Object.freeze({ chars: String.fromCharCode(code), width: 1, attrs: this.#attrs });
|
|
463
|
+
this.#asciiCells[slot] = cell;
|
|
464
|
+
}
|
|
465
|
+
this.#placeCell(cell, 1);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
#placeCell(cell: CellData, width: number): void {
|
|
368
469
|
if (this.#pendingWrap || (width === 2 && this.#active.cursorX === this.cols - 1)) {
|
|
369
470
|
if (!this.#autowrap) {
|
|
370
471
|
this.#active.cursorX = this.cols - width;
|
|
@@ -377,9 +478,9 @@ export class Terminal {
|
|
|
377
478
|
const column = Math.min(this.#active.cursorX, this.cols - 1);
|
|
378
479
|
if (this.#insertMode) this.#shiftCells(line, column, width);
|
|
379
480
|
this.#clearWideAt(line, column);
|
|
380
|
-
line.cells[column] =
|
|
481
|
+
line.cells[column] = cell;
|
|
381
482
|
if (width === 2 && column + 1 < this.cols) {
|
|
382
|
-
line.cells[column + 1] = { chars: "", width: 0, attrs:
|
|
483
|
+
line.cells[column + 1] = Object.freeze({ chars: "", width: 0, attrs: cell.attrs });
|
|
383
484
|
}
|
|
384
485
|
this.#active.cursorX = column + width;
|
|
385
486
|
this.#pendingWrap = this.#active.cursorX >= this.cols;
|
|
@@ -396,7 +497,9 @@ export class Terminal {
|
|
|
396
497
|
if (!line) return;
|
|
397
498
|
while (column >= 0 && line.cells[column]?.width === 0) column--;
|
|
398
499
|
const cell = line.cells[column];
|
|
399
|
-
|
|
500
|
+
// Cells are frozen and may be shared; replace instead of mutating.
|
|
501
|
+
if (cell?.chars)
|
|
502
|
+
line.cells[column] = Object.freeze({ chars: cell.chars + mark, width: cell.width, attrs: cell.attrs });
|
|
400
503
|
}
|
|
401
504
|
|
|
402
505
|
#lineFeed(wrapped: boolean): void {
|
|
@@ -489,8 +592,8 @@ export class Terminal {
|
|
|
489
592
|
}
|
|
490
593
|
|
|
491
594
|
#eraseRange(line: BufferLine, start: number, end: number): void {
|
|
492
|
-
|
|
493
|
-
|
|
595
|
+
const blank = sharedBlankCell(this.#attrs);
|
|
596
|
+
for (let column = Math.max(0, start); column < Math.min(this.cols, end); column++) line.cells[column] = blank;
|
|
494
597
|
this.#repairWideCells(line);
|
|
495
598
|
}
|
|
496
599
|
|
|
@@ -521,14 +624,18 @@ export class Terminal {
|
|
|
521
624
|
}
|
|
522
625
|
|
|
523
626
|
#shiftCells(line: BufferLine, column: number, count: number): void {
|
|
524
|
-
|
|
627
|
+
// Inserting more than a row's worth only pushes everything past the edge.
|
|
628
|
+
const blank = sharedBlankCell(this.#attrs);
|
|
629
|
+
const blanks = Array.from({ length: Math.min(count, this.cols) }, () => blank);
|
|
630
|
+
line.cells.splice(column, 0, ...blanks);
|
|
525
631
|
line.cells.length = this.cols;
|
|
526
632
|
}
|
|
527
633
|
|
|
528
634
|
#deleteCells(count: number): void {
|
|
529
635
|
const line = this.#currentLine();
|
|
530
636
|
line.cells.splice(this.#active.cursorX, count);
|
|
531
|
-
|
|
637
|
+
const blank = sharedBlankCell(this.#attrs);
|
|
638
|
+
while (line.cells.length < this.cols) line.cells.push(blank);
|
|
532
639
|
this.#repairWideCells(line);
|
|
533
640
|
}
|
|
534
641
|
|
|
@@ -537,64 +644,67 @@ export class Terminal {
|
|
|
537
644
|
}
|
|
538
645
|
|
|
539
646
|
#clearWideAt(line: BufferLine, column: number): void {
|
|
540
|
-
|
|
541
|
-
if (
|
|
647
|
+
const width = line.cells[column]?.width;
|
|
648
|
+
if (width === 0 && column > 0) line.cells[column - 1] = sharedBlankCell(this.#attrs);
|
|
649
|
+
if (width === 2 && column + 1 < this.cols) line.cells[column + 1] = sharedBlankCell(this.#attrs);
|
|
542
650
|
}
|
|
543
651
|
|
|
544
652
|
#repairWideCells(line: BufferLine): void {
|
|
545
653
|
for (let column = 0; column < this.cols; column++) {
|
|
546
654
|
const cell = line.cells[column]!;
|
|
547
655
|
if (cell.width === 0 && (column === 0 || line.cells[column - 1]?.width !== 2))
|
|
548
|
-
line.cells[column] =
|
|
656
|
+
line.cells[column] = sharedBlankCell(this.#attrs);
|
|
549
657
|
if (cell.width === 2 && (column === this.cols - 1 || line.cells[column + 1]?.width !== 0))
|
|
550
|
-
line.cells[column] =
|
|
658
|
+
line.cells[column] = sharedBlankCell(this.#attrs);
|
|
551
659
|
}
|
|
552
660
|
}
|
|
553
661
|
|
|
554
662
|
#setRendition(values: number[]): void {
|
|
555
663
|
const params = values.length === 0 ? [0] : values;
|
|
664
|
+
// Copy-on-write: cells keep referencing the previous rendition object.
|
|
665
|
+
const attrs: CellAttributes = { ...this.#attrs };
|
|
556
666
|
for (let index = 0; index < params.length; index++) {
|
|
557
667
|
const code = params[index] ?? 0;
|
|
558
|
-
if (code === 0)
|
|
559
|
-
else if (code === 1)
|
|
560
|
-
else if (code === 2)
|
|
561
|
-
else if (code === 3)
|
|
562
|
-
else if (code === 4 || code === 21)
|
|
563
|
-
else if (code === 7)
|
|
564
|
-
else if (code === 9)
|
|
668
|
+
if (code === 0) Object.assign(attrs, DEFAULT_CELL_ATTRIBUTES);
|
|
669
|
+
else if (code === 1) attrs.bold = true;
|
|
670
|
+
else if (code === 2) attrs.dim = true;
|
|
671
|
+
else if (code === 3) attrs.italic = true;
|
|
672
|
+
else if (code === 4 || code === 21) attrs.underline = true;
|
|
673
|
+
else if (code === 7) attrs.inverse = true;
|
|
674
|
+
else if (code === 9) attrs.strikethrough = true;
|
|
565
675
|
else if (code === 22) {
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
} else if (code === 23)
|
|
569
|
-
else if (code === 24)
|
|
570
|
-
else if (code === 27)
|
|
571
|
-
else if (code === 29)
|
|
572
|
-
else if (code === 53)
|
|
573
|
-
else if (code === 55)
|
|
676
|
+
attrs.bold = false;
|
|
677
|
+
attrs.dim = false;
|
|
678
|
+
} else if (code === 23) attrs.italic = false;
|
|
679
|
+
else if (code === 24) attrs.underline = false;
|
|
680
|
+
else if (code === 27) attrs.inverse = false;
|
|
681
|
+
else if (code === 29) attrs.strikethrough = false;
|
|
682
|
+
else if (code === 53) attrs.overline = true;
|
|
683
|
+
else if (code === 55) attrs.overline = false;
|
|
574
684
|
else if (code >= 30 && code <= 37) {
|
|
575
|
-
|
|
576
|
-
|
|
685
|
+
attrs.fgMode = 1;
|
|
686
|
+
attrs.fg = code - 30;
|
|
577
687
|
} else if (code >= 40 && code <= 47) {
|
|
578
|
-
|
|
579
|
-
|
|
688
|
+
attrs.bgMode = 1;
|
|
689
|
+
attrs.bg = code - 40;
|
|
580
690
|
} else if (code >= 90 && code <= 97) {
|
|
581
|
-
|
|
582
|
-
|
|
691
|
+
attrs.fgMode = 1;
|
|
692
|
+
attrs.fg = code - 82;
|
|
583
693
|
} else if (code >= 100 && code <= 107) {
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
} else if (code === 39)
|
|
587
|
-
else if (code === 49)
|
|
694
|
+
attrs.bgMode = 1;
|
|
695
|
+
attrs.bg = code - 92;
|
|
696
|
+
} else if (code === 39) attrs.fgMode = 0;
|
|
697
|
+
else if (code === 49) attrs.bgMode = 0;
|
|
588
698
|
else if (code === 38 || code === 48) {
|
|
589
699
|
const foreground = code === 38;
|
|
590
700
|
const mode = params[index + 1];
|
|
591
701
|
if (mode === 5 && params[index + 2] !== undefined) {
|
|
592
702
|
if (foreground) {
|
|
593
|
-
|
|
594
|
-
|
|
703
|
+
attrs.fgMode = 1;
|
|
704
|
+
attrs.fg = params[index + 2]! & 0xff;
|
|
595
705
|
} else {
|
|
596
|
-
|
|
597
|
-
|
|
706
|
+
attrs.bgMode = 1;
|
|
707
|
+
attrs.bg = params[index + 2]! & 0xff;
|
|
598
708
|
}
|
|
599
709
|
index += 2;
|
|
600
710
|
} else if (mode === 2 && params[index + 4] !== undefined) {
|
|
@@ -603,16 +713,18 @@ export class Terminal {
|
|
|
603
713
|
((params[index + 3]! & 0xff) << 8) |
|
|
604
714
|
(params[index + 4]! & 0xff);
|
|
605
715
|
if (foreground) {
|
|
606
|
-
|
|
607
|
-
|
|
716
|
+
attrs.fgMode = 2;
|
|
717
|
+
attrs.fg = color;
|
|
608
718
|
} else {
|
|
609
|
-
|
|
610
|
-
|
|
719
|
+
attrs.bgMode = 2;
|
|
720
|
+
attrs.bg = color;
|
|
611
721
|
}
|
|
612
722
|
index += 4;
|
|
613
723
|
}
|
|
614
724
|
}
|
|
615
725
|
}
|
|
726
|
+
if (sameAttributes(attrs, this.#attrs)) return;
|
|
727
|
+
this.#attrs = sameAttributes(attrs, DEFAULT_CELL_ATTRIBUTES) ? DEFAULT_CELL_ATTRIBUTES : Object.freeze(attrs);
|
|
616
728
|
}
|
|
617
729
|
|
|
618
730
|
#setScrollRegion(params: number[]): void {
|
|
@@ -643,21 +755,20 @@ export class Terminal {
|
|
|
643
755
|
}
|
|
644
756
|
|
|
645
757
|
#saveCursor(): void {
|
|
646
|
-
this.#saved = { x: this.#active.cursorX, y: this.#active.cursorY, attrs:
|
|
758
|
+
this.#saved = { x: this.#active.cursorX, y: this.#active.cursorY, attrs: this.#attrs };
|
|
647
759
|
}
|
|
648
760
|
|
|
649
761
|
#restoreCursor(): void {
|
|
650
762
|
if (!this.#saved) return;
|
|
651
763
|
this.#active.cursorX = Math.min(this.cols - 1, this.#saved.x);
|
|
652
764
|
this.#active.cursorY = Math.min(this.rows - 1, this.#saved.y);
|
|
653
|
-
this.#attrs =
|
|
765
|
+
this.#attrs = this.#saved.attrs;
|
|
654
766
|
this.#pendingWrap = false;
|
|
655
767
|
}
|
|
656
768
|
|
|
657
769
|
#enterAlternate(saveCursor: boolean): void {
|
|
658
770
|
if (this.#usingAlternate) return;
|
|
659
|
-
if (saveCursor)
|
|
660
|
-
this.#alternateSaved = { x: this.#normal.cursorX, y: this.#normal.cursorY, attrs: { ...this.#attrs } };
|
|
771
|
+
if (saveCursor) this.#alternateSaved = { x: this.#normal.cursorX, y: this.#normal.cursorY, attrs: this.#attrs };
|
|
661
772
|
this.#alternate = createState(this.cols, this.rows);
|
|
662
773
|
this.#active = this.#alternate;
|
|
663
774
|
this.#usingAlternate = true;
|
|
@@ -673,7 +784,7 @@ export class Terminal {
|
|
|
673
784
|
if (restoreCursor && this.#alternateSaved) {
|
|
674
785
|
this.#normal.cursorX = this.#alternateSaved.x;
|
|
675
786
|
this.#normal.cursorY = this.#alternateSaved.y;
|
|
676
|
-
this.#attrs =
|
|
787
|
+
this.#attrs = this.#alternateSaved.attrs;
|
|
677
788
|
}
|
|
678
789
|
this.#scrollTop = 0;
|
|
679
790
|
this.#scrollBottom = this.rows - 1;
|
|
@@ -681,7 +792,7 @@ export class Terminal {
|
|
|
681
792
|
}
|
|
682
793
|
|
|
683
794
|
#reset(): void {
|
|
684
|
-
this.#attrs =
|
|
795
|
+
this.#attrs = DEFAULT_CELL_ATTRIBUTES;
|
|
685
796
|
this.#normal = createState(this.cols, this.rows);
|
|
686
797
|
this.#alternate = createState(this.cols, this.rows);
|
|
687
798
|
this.#usingAlternate = false;
|
|
@@ -704,7 +815,8 @@ export class Terminal {
|
|
|
704
815
|
const group = groups.at(-1)!;
|
|
705
816
|
const used = line.isWrapped || state.lines[row + 1]?.isWrapped ? line.cells.length : this.#usedColumns(line);
|
|
706
817
|
if (row === absoluteCursor) group.cursorOffset = group.cells.length + state.cursorX;
|
|
707
|
-
|
|
818
|
+
// Cells are immutable, so the reflowed grid reuses them.
|
|
819
|
+
for (let column = 0; column < used; column++) group.cells.push(line.cells[column]!);
|
|
708
820
|
}
|
|
709
821
|
const lines: BufferLine[] = [];
|
|
710
822
|
let cursorAbsolute = 0;
|
|
@@ -725,8 +837,9 @@ export class Terminal {
|
|
|
725
837
|
continue;
|
|
726
838
|
}
|
|
727
839
|
if (cell.width === 2 && target === columns - 1) break;
|
|
728
|
-
line.cells[target] =
|
|
729
|
-
if (cell.width === 2)
|
|
840
|
+
line.cells[target] = cell;
|
|
841
|
+
if (cell.width === 2)
|
|
842
|
+
line.cells[target + 1] = Object.freeze({ chars: "", width: 0, attrs: cell.attrs });
|
|
730
843
|
target += cell.width;
|
|
731
844
|
source += cell.width;
|
|
732
845
|
}
|