@textui/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.d.ts.map +1 -0
- package/dist/adapters/index.js +11 -0
- package/dist/app/app.d.ts +200 -0
- package/dist/app/app.d.ts.map +1 -0
- package/dist/app/app.js +980 -0
- package/dist/core/animation.d.ts +34 -0
- package/dist/core/animation.d.ts.map +1 -0
- package/dist/core/animation.js +125 -0
- package/dist/core/clipboard.d.ts +29 -0
- package/dist/core/clipboard.d.ts.map +1 -0
- package/dist/core/clipboard.js +30 -0
- package/dist/core/commands.d.ts +42 -0
- package/dist/core/commands.d.ts.map +1 -0
- package/dist/core/commands.js +146 -0
- package/dist/core/components.d.ts +27 -0
- package/dist/core/components.d.ts.map +1 -0
- package/dist/core/components.js +78 -0
- package/dist/core/events.d.ts +23 -0
- package/dist/core/events.d.ts.map +1 -0
- package/dist/core/events.js +77 -0
- package/dist/core/focus.d.ts +69 -0
- package/dist/core/focus.d.ts.map +1 -0
- package/dist/core/focus.js +336 -0
- package/dist/core/i18n.d.ts +27 -0
- package/dist/core/i18n.d.ts.map +1 -0
- package/dist/core/i18n.js +88 -0
- package/dist/core/keybindings.d.ts +53 -0
- package/dist/core/keybindings.d.ts.map +1 -0
- package/dist/core/keybindings.js +163 -0
- package/dist/core/layers.d.ts +28 -0
- package/dist/core/layers.d.ts.map +1 -0
- package/dist/core/layers.js +84 -0
- package/dist/core/manifest.d.ts +22 -0
- package/dist/core/manifest.d.ts.map +1 -0
- package/dist/core/manifest.js +85 -0
- package/dist/core/navigation.d.ts +47 -0
- package/dist/core/navigation.d.ts.map +1 -0
- package/dist/core/navigation.js +110 -0
- package/dist/core/resources.d.ts +115 -0
- package/dist/core/resources.d.ts.map +1 -0
- package/dist/core/resources.js +321 -0
- package/dist/core/services.d.ts +21 -0
- package/dist/core/services.d.ts.map +1 -0
- package/dist/core/services.js +63 -0
- package/dist/core/store.d.ts +60 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +593 -0
- package/dist/core/surfaces.d.ts +81 -0
- package/dist/core/surfaces.d.ts.map +1 -0
- package/dist/core/surfaces.js +237 -0
- package/dist/core/syntax.d.ts +49 -0
- package/dist/core/syntax.d.ts.map +1 -0
- package/dist/core/syntax.js +172 -0
- package/dist/core/when.d.ts +15 -0
- package/dist/core/when.d.ts.map +1 -0
- package/dist/core/when.js +224 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +54 -0
- package/dist/jsx/factory.d.ts +27 -0
- package/dist/jsx/factory.d.ts.map +1 -0
- package/dist/jsx/factory.js +102 -0
- package/dist/jsx/intrinsics.d.ts +96 -0
- package/dist/jsx/intrinsics.d.ts.map +1 -0
- package/dist/jsx/intrinsics.js +1 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +13 -0
- package/dist/jsx/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/jsx/jsx-dev-runtime.js +12 -0
- package/dist/jsx/jsx-runtime.d.ts +42 -0
- package/dist/jsx/jsx-runtime.d.ts.map +1 -0
- package/dist/jsx/jsx-runtime.js +16 -0
- package/dist/render/buffer.d.ts +77 -0
- package/dist/render/buffer.d.ts.map +1 -0
- package/dist/render/buffer.js +275 -0
- package/dist/render/color.d.ts +35 -0
- package/dist/render/color.d.ts.map +1 -0
- package/dist/render/color.js +160 -0
- package/dist/render/diff.d.ts +37 -0
- package/dist/render/diff.d.ts.map +1 -0
- package/dist/render/diff.js +66 -0
- package/dist/render/layout.d.ts +72 -0
- package/dist/render/layout.d.ts.map +1 -0
- package/dist/render/layout.js +633 -0
- package/dist/render/static.d.ts +63 -0
- package/dist/render/static.d.ts.map +1 -0
- package/dist/render/static.js +209 -0
- package/dist/runtime/bindings.d.ts +46 -0
- package/dist/runtime/bindings.d.ts.map +1 -0
- package/dist/runtime/bindings.js +116 -0
- package/dist/runtime/hooks.d.ts +284 -0
- package/dist/runtime/hooks.d.ts.map +1 -0
- package/dist/runtime/hooks.js +846 -0
- package/dist/runtime/instance.d.ts +106 -0
- package/dist/runtime/instance.d.ts.map +1 -0
- package/dist/runtime/instance.js +181 -0
- package/dist/runtime/paint.d.ts +27 -0
- package/dist/runtime/paint.d.ts.map +1 -0
- package/dist/runtime/paint.js +567 -0
- package/dist/runtime/reconcile.d.ts +23 -0
- package/dist/runtime/reconcile.d.ts.map +1 -0
- package/dist/runtime/reconcile.js +260 -0
- package/dist/runtime/runtime.d.ts +42 -0
- package/dist/runtime/runtime.d.ts.map +1 -0
- package/dist/runtime/runtime.js +1 -0
- package/dist/runtime/style.d.ts +55 -0
- package/dist/runtime/style.d.ts.map +1 -0
- package/dist/runtime/style.js +143 -0
- package/dist/themes/borders.d.ts +10 -0
- package/dist/themes/borders.d.ts.map +1 -0
- package/dist/themes/borders.js +88 -0
- package/dist/themes/builtin.d.ts +31 -0
- package/dist/themes/builtin.d.ts.map +1 -0
- package/dist/themes/builtin.js +291 -0
- package/dist/themes/glyphs.d.ts +15 -0
- package/dist/themes/glyphs.d.ts.map +1 -0
- package/dist/themes/glyphs.js +97 -0
- package/dist/themes/index.d.ts +5 -0
- package/dist/themes/index.d.ts.map +1 -0
- package/dist/themes/index.js +4 -0
- package/dist/themes/registry.d.ts +20 -0
- package/dist/themes/registry.d.ts.map +1 -0
- package/dist/themes/registry.js +204 -0
- package/dist/types/adapter.d.ts +38 -0
- package/dist/types/adapter.d.ts.map +1 -0
- package/dist/types/adapter.js +1 -0
- package/dist/types/animation.d.ts +31 -0
- package/dist/types/animation.d.ts.map +1 -0
- package/dist/types/animation.js +1 -0
- package/dist/types/app.d.ts +148 -0
- package/dist/types/app.d.ts.map +1 -0
- package/dist/types/app.js +1 -0
- package/dist/types/async.d.ts +30 -0
- package/dist/types/async.d.ts.map +1 -0
- package/dist/types/async.js +1 -0
- package/dist/types/capabilities.d.ts +44 -0
- package/dist/types/capabilities.d.ts.map +1 -0
- package/dist/types/capabilities.js +33 -0
- package/dist/types/cells.d.ts +72 -0
- package/dist/types/cells.d.ts.map +1 -0
- package/dist/types/cells.js +10 -0
- package/dist/types/command.d.ts +127 -0
- package/dist/types/command.d.ts.map +1 -0
- package/dist/types/command.js +1 -0
- package/dist/types/component-registry.d.ts +77 -0
- package/dist/types/component-registry.d.ts.map +1 -0
- package/dist/types/component-registry.js +1 -0
- package/dist/types/disposable.d.ts +8 -0
- package/dist/types/disposable.d.ts.map +1 -0
- package/dist/types/disposable.js +1 -0
- package/dist/types/focus.d.ts +72 -0
- package/dist/types/focus.d.ts.map +1 -0
- package/dist/types/focus.js +1 -0
- package/dist/types/geometry.d.ts +28 -0
- package/dist/types/geometry.d.ts.map +1 -0
- package/dist/types/geometry.js +14 -0
- package/dist/types/graph.d.ts +129 -0
- package/dist/types/graph.d.ts.map +1 -0
- package/dist/types/graph.js +31 -0
- package/dist/types/i18n.d.ts +23 -0
- package/dist/types/i18n.d.ts.map +1 -0
- package/dist/types/i18n.js +1 -0
- package/dist/types/index.d.ts +32 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +31 -0
- package/dist/types/input.d.ts +65 -0
- package/dist/types/input.d.ts.map +1 -0
- package/dist/types/input.js +1 -0
- package/dist/types/keybinding.d.ts +49 -0
- package/dist/types/keybinding.d.ts.map +1 -0
- package/dist/types/keybinding.js +1 -0
- package/dist/types/layer.d.ts +54 -0
- package/dist/types/layer.d.ts.map +1 -0
- package/dist/types/layer.js +1 -0
- package/dist/types/manifest.d.ts +114 -0
- package/dist/types/manifest.d.ts.map +1 -0
- package/dist/types/manifest.js +1 -0
- package/dist/types/markdown.d.ts +58 -0
- package/dist/types/markdown.d.ts.map +1 -0
- package/dist/types/markdown.js +1 -0
- package/dist/types/navigation.d.ts +40 -0
- package/dist/types/navigation.d.ts.map +1 -0
- package/dist/types/navigation.js +1 -0
- package/dist/types/render.d.ts +72 -0
- package/dist/types/render.d.ts.map +1 -0
- package/dist/types/render.js +1 -0
- package/dist/types/resource.d.ts +170 -0
- package/dist/types/resource.d.ts.map +1 -0
- package/dist/types/resource.js +1 -0
- package/dist/types/services.d.ts +24 -0
- package/dist/types/services.d.ts.map +1 -0
- package/dist/types/services.js +3 -0
- package/dist/types/shell.d.ts +32 -0
- package/dist/types/shell.d.ts.map +1 -0
- package/dist/types/shell.js +1 -0
- package/dist/types/store.d.ts +124 -0
- package/dist/types/store.d.ts.map +1 -0
- package/dist/types/store.js +1 -0
- package/dist/types/stream.d.ts +18 -0
- package/dist/types/stream.d.ts.map +1 -0
- package/dist/types/stream.js +1 -0
- package/dist/types/style.d.ts +166 -0
- package/dist/types/style.d.ts.map +1 -0
- package/dist/types/style.js +1 -0
- package/dist/types/surface.d.ts +97 -0
- package/dist/types/surface.d.ts.map +1 -0
- package/dist/types/surface.js +1 -0
- package/dist/types/syntax.d.ts +68 -0
- package/dist/types/syntax.d.ts.map +1 -0
- package/dist/types/syntax.js +5 -0
- package/dist/types/terminal.d.ts +52 -0
- package/dist/types/terminal.d.ts.map +1 -0
- package/dist/types/terminal.js +1 -0
- package/dist/types/theme.d.ts +117 -0
- package/dist/types/theme.d.ts.map +1 -0
- package/dist/types/theme.js +1 -0
- package/dist/types/when.d.ts +19 -0
- package/dist/types/when.d.ts.map +1 -0
- package/dist/types/when.js +1 -0
- package/dist/ui/primitives.d.ts +56 -0
- package/dist/ui/primitives.d.ts.map +1 -0
- package/dist/ui/primitives.js +108 -0
- package/dist/ui/screen.d.ts +23 -0
- package/dist/ui/screen.d.ts.map +1 -0
- package/dist/ui/screen.js +18 -0
- package/dist/util/disposable.d.ts +6 -0
- package/dist/util/disposable.d.ts.map +1 -0
- package/dist/util/disposable.js +49 -0
- package/dist/util/markdown.d.ts +24 -0
- package/dist/util/markdown.d.ts.map +1 -0
- package/dist/util/markdown.js +220 -0
- package/dist/util/paths.d.ts +47 -0
- package/dist/util/paths.d.ts.map +1 -0
- package/dist/util/paths.js +134 -0
- package/dist/util/stream.d.ts +32 -0
- package/dist/util/stream.d.ts.map +1 -0
- package/dist/util/stream.js +206 -0
- package/dist/util/text.d.ts +65 -0
- package/dist/util/text.d.ts.map +1 -0
- package/dist/util/text.js +419 -0
- package/package.json +70 -0
- package/src/adapters/index.ts +11 -0
- package/src/app/app.ts +1096 -0
- package/src/core/animation.ts +144 -0
- package/src/core/clipboard.ts +40 -0
- package/src/core/commands.ts +169 -0
- package/src/core/components.ts +91 -0
- package/src/core/events.ts +95 -0
- package/src/core/focus.ts +358 -0
- package/src/core/i18n.ts +107 -0
- package/src/core/keybindings.ts +184 -0
- package/src/core/layers.ts +94 -0
- package/src/core/manifest.ts +84 -0
- package/src/core/navigation.ts +135 -0
- package/src/core/resources.ts +362 -0
- package/src/core/services.ts +69 -0
- package/src/core/store.ts +640 -0
- package/src/core/surfaces.ts +292 -0
- package/src/core/syntax.ts +200 -0
- package/src/core/when.ts +238 -0
- package/src/index.ts +76 -0
- package/src/jsx/factory.ts +124 -0
- package/src/jsx/intrinsics.ts +99 -0
- package/src/jsx/jsx-dev-runtime.ts +23 -0
- package/src/jsx/jsx-runtime.ts +76 -0
- package/src/render/buffer.ts +318 -0
- package/src/render/color.ts +180 -0
- package/src/render/diff.ts +99 -0
- package/src/render/layout.ts +764 -0
- package/src/render/static.ts +290 -0
- package/src/runtime/bindings.ts +150 -0
- package/src/runtime/hooks.ts +1057 -0
- package/src/runtime/instance.ts +284 -0
- package/src/runtime/paint.ts +737 -0
- package/src/runtime/reconcile.ts +335 -0
- package/src/runtime/runtime.ts +47 -0
- package/src/runtime/style.ts +205 -0
- package/src/themes/borders.ts +95 -0
- package/src/themes/builtin.ts +301 -0
- package/src/themes/glyphs.ts +101 -0
- package/src/themes/index.ts +4 -0
- package/src/themes/registry.ts +224 -0
- package/src/types/adapter.ts +41 -0
- package/src/types/animation.ts +34 -0
- package/src/types/app.ts +149 -0
- package/src/types/async.ts +35 -0
- package/src/types/capabilities.ts +79 -0
- package/src/types/cells.ts +79 -0
- package/src/types/command.ts +139 -0
- package/src/types/component-registry.ts +82 -0
- package/src/types/disposable.ts +8 -0
- package/src/types/focus.ts +77 -0
- package/src/types/geometry.ts +50 -0
- package/src/types/graph.ts +174 -0
- package/src/types/i18n.ts +25 -0
- package/src/types/index.ts +31 -0
- package/src/types/input.ts +81 -0
- package/src/types/keybinding.ts +51 -0
- package/src/types/layer.ts +46 -0
- package/src/types/manifest.ts +101 -0
- package/src/types/markdown.ts +47 -0
- package/src/types/navigation.ts +42 -0
- package/src/types/render.ts +86 -0
- package/src/types/resource.ts +185 -0
- package/src/types/services.ts +28 -0
- package/src/types/shell.ts +30 -0
- package/src/types/store.ts +148 -0
- package/src/types/stream.ts +24 -0
- package/src/types/style.ts +205 -0
- package/src/types/surface.ts +119 -0
- package/src/types/syntax.ts +93 -0
- package/src/types/terminal.ts +58 -0
- package/src/types/theme.ts +121 -0
- package/src/types/when.ts +21 -0
- package/src/ui/primitives.ts +118 -0
- package/src/ui/screen.ts +41 -0
- package/src/util/disposable.ts +49 -0
- package/src/util/markdown.ts +225 -0
- package/src/util/paths.ts +138 -0
- package/src/util/stream.ts +213 -0
- package/src/util/text.ts +428 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
import type { Rect } from '../types/geometry.js';
|
|
2
|
+
import type { Cell, CellBuffer } from '../types/cells.js';
|
|
3
|
+
import { COLOR_DEFAULT, packColor, type PackedColor } from './color.js';
|
|
4
|
+
|
|
5
|
+
/** One cell as it is stored: colours packed, nothing allocated to read it. */
|
|
6
|
+
export interface PackedCell {
|
|
7
|
+
char: string;
|
|
8
|
+
fg: PackedColor;
|
|
9
|
+
bg: PackedColor;
|
|
10
|
+
attrs: number;
|
|
11
|
+
link: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
import { graphemeWidth } from '../util/text.js';
|
|
14
|
+
|
|
15
|
+
/** Internal cell flags, kept out of the public attrs bitfield. */
|
|
16
|
+
export const FLAG_CONTINUATION = 1;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The frame.
|
|
20
|
+
*
|
|
21
|
+
* Cells live in parallel arrays rather than objects because the diff walks
|
|
22
|
+
* every cell of every frame, and one allocation per cell per frame is the
|
|
23
|
+
* difference between a smooth redraw and a visibly stuttering one.
|
|
24
|
+
*/
|
|
25
|
+
export class Buffer implements CellBuffer {
|
|
26
|
+
width: number;
|
|
27
|
+
height: number;
|
|
28
|
+
|
|
29
|
+
chars: string[];
|
|
30
|
+
fg: Int32Array;
|
|
31
|
+
bg: Int32Array;
|
|
32
|
+
attrs: Uint16Array;
|
|
33
|
+
flags: Uint8Array;
|
|
34
|
+
links: (string | undefined)[];
|
|
35
|
+
|
|
36
|
+
private prevChars: string[];
|
|
37
|
+
private prevFg: Int32Array;
|
|
38
|
+
private prevBg: Int32Array;
|
|
39
|
+
private prevAttrs: Uint16Array;
|
|
40
|
+
private prevLinks: (string | undefined)[];
|
|
41
|
+
private committed = false;
|
|
42
|
+
|
|
43
|
+
constructor(width: number, height: number) {
|
|
44
|
+
this.width = Math.max(0, width);
|
|
45
|
+
this.height = Math.max(0, height);
|
|
46
|
+
const n = this.width * this.height;
|
|
47
|
+
this.chars = new Array<string>(n).fill(' ');
|
|
48
|
+
this.fg = new Int32Array(n).fill(COLOR_DEFAULT);
|
|
49
|
+
this.bg = new Int32Array(n).fill(COLOR_DEFAULT);
|
|
50
|
+
this.attrs = new Uint16Array(n);
|
|
51
|
+
this.flags = new Uint8Array(n);
|
|
52
|
+
this.links = new Array<string | undefined>(n).fill(undefined);
|
|
53
|
+
|
|
54
|
+
this.prevChars = new Array<string>(n).fill(' ');
|
|
55
|
+
this.prevFg = new Int32Array(n).fill(COLOR_DEFAULT);
|
|
56
|
+
this.prevBg = new Int32Array(n).fill(COLOR_DEFAULT);
|
|
57
|
+
this.prevAttrs = new Uint16Array(n);
|
|
58
|
+
this.prevLinks = new Array<string | undefined>(n).fill(undefined);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Cell-object access, for the `CellBuffer` contract and the test harness. */
|
|
62
|
+
get(x: number, y: number): Cell | undefined {
|
|
63
|
+
if (!this.inBounds(x, y)) return undefined;
|
|
64
|
+
const i = this.index(x, y);
|
|
65
|
+
return {
|
|
66
|
+
char: this.chars[i] as string,
|
|
67
|
+
fg: packedToColor(this.fg[i] as number),
|
|
68
|
+
bg: packedToColor(this.bg[i] as number),
|
|
69
|
+
attrs: this.attrs[i] as number,
|
|
70
|
+
link: this.links[i],
|
|
71
|
+
continuation: ((this.flags[i] as number) & FLAG_CONTINUATION) !== 0,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The same cell, as the numbers it is actually stored as.
|
|
77
|
+
*
|
|
78
|
+
* `get` is the friendly shape: it unpacks both colours into `Color` values,
|
|
79
|
+
* which is right for a test or a caller reasoning about colour and wrong for
|
|
80
|
+
* a loop over a region - packing them straight back is an allocation and a
|
|
81
|
+
* round trip per cell. This is what the renderer's inner loops use.
|
|
82
|
+
*/
|
|
83
|
+
packedAt(x: number, y: number): PackedCell | undefined {
|
|
84
|
+
if (!this.inBounds(x, y)) return undefined;
|
|
85
|
+
const i = this.index(x, y);
|
|
86
|
+
return {
|
|
87
|
+
char: this.chars[i] as string,
|
|
88
|
+
fg: this.fg[i] as number,
|
|
89
|
+
bg: this.bg[i] as number,
|
|
90
|
+
attrs: this.attrs[i] as number,
|
|
91
|
+
link: this.links[i],
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
set(x: number, y: number, cell: Cell): void {
|
|
96
|
+
this.put(
|
|
97
|
+
x, y, cell.char,
|
|
98
|
+
packColor(cell.fg), packColor(cell.bg), cell.attrs, cell.link,
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
index(x: number, y: number): number {
|
|
103
|
+
return y * this.width + x;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
inBounds(x: number, y: number): boolean {
|
|
107
|
+
return x >= 0 && x < this.width && y >= 0 && y < this.height;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Write one grapheme. A double-width grapheme claims the next cell as a
|
|
112
|
+
* continuation, and writing over either half clears the other - otherwise a
|
|
113
|
+
* half-overwritten wide character shifts every cell after it on that row.
|
|
114
|
+
*/
|
|
115
|
+
put(
|
|
116
|
+
x: number,
|
|
117
|
+
y: number,
|
|
118
|
+
char: string,
|
|
119
|
+
fg: PackedColor = COLOR_DEFAULT,
|
|
120
|
+
bg: PackedColor = COLOR_DEFAULT,
|
|
121
|
+
attrs = 0,
|
|
122
|
+
link?: string,
|
|
123
|
+
): void {
|
|
124
|
+
if (!this.inBounds(x, y)) return;
|
|
125
|
+
const i = this.index(x, y);
|
|
126
|
+
|
|
127
|
+
// Overwriting the right half of a wide pair: blank the left half.
|
|
128
|
+
if (((this.flags[i] as number) & FLAG_CONTINUATION) !== 0 && x > 0) {
|
|
129
|
+
this.chars[i - 1] = ' ';
|
|
130
|
+
this.flags[i - 1] = 0;
|
|
131
|
+
}
|
|
132
|
+
// Overwriting a wide left half: blank its continuation.
|
|
133
|
+
if (x + 1 < this.width && ((this.flags[i + 1] as number) & FLAG_CONTINUATION) !== 0) {
|
|
134
|
+
this.chars[i + 1] = ' ';
|
|
135
|
+
this.flags[i + 1] = 0;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/*
|
|
139
|
+
* The width of one printable ASCII character, without asking.
|
|
140
|
+
*
|
|
141
|
+
* `graphemeWidth` is a function call per cell painted - twelve thousand of
|
|
142
|
+
* them a frame on a full screen - to answer "is `a` one cell wide". It is,
|
|
143
|
+
* and the character code says so.
|
|
144
|
+
*/
|
|
145
|
+
const only = char.length === 1 ? char.charCodeAt(0) : -1;
|
|
146
|
+
const w = only >= 0x20 && only <= 0x7e ? 1 : graphemeWidth(char);
|
|
147
|
+
if (w === 0) return;
|
|
148
|
+
|
|
149
|
+
this.chars[i] = char;
|
|
150
|
+
this.fg[i] = fg;
|
|
151
|
+
this.bg[i] = bg;
|
|
152
|
+
this.attrs[i] = attrs;
|
|
153
|
+
this.flags[i] = 0;
|
|
154
|
+
this.links[i] = link;
|
|
155
|
+
|
|
156
|
+
if (w === 2) {
|
|
157
|
+
if (x + 1 >= this.width) {
|
|
158
|
+
// A wide grapheme in the last column cannot render; blank it.
|
|
159
|
+
this.chars[i] = ' ';
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const j = i + 1;
|
|
163
|
+
if (x + 2 < this.width && ((this.flags[j + 1] as number) & FLAG_CONTINUATION) !== 0) {
|
|
164
|
+
this.chars[j + 1] = ' ';
|
|
165
|
+
this.flags[j + 1] = 0;
|
|
166
|
+
}
|
|
167
|
+
this.chars[j] = '';
|
|
168
|
+
this.fg[j] = fg;
|
|
169
|
+
this.bg[j] = bg;
|
|
170
|
+
this.attrs[j] = attrs;
|
|
171
|
+
this.flags[j] = FLAG_CONTINUATION;
|
|
172
|
+
this.links[j] = link;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Blank a region, or the whole buffer.
|
|
178
|
+
*
|
|
179
|
+
* By span rather than by cell. Clearing everything happens once a frame, and
|
|
180
|
+
* a cell-at-a-time loop over twelve thousand of them - each with two bounds
|
|
181
|
+
* checks and an index multiply - was costing more than painting the content
|
|
182
|
+
* that replaced it. `fill` is one call into the engine per array, and the
|
|
183
|
+
* four that matter are typed.
|
|
184
|
+
*/
|
|
185
|
+
clear(rect?: Rect, bg: PackedColor = COLOR_DEFAULT): void {
|
|
186
|
+
if (rect === undefined) {
|
|
187
|
+
this.fillSpan(0, this.width * this.height, bg);
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const x0 = Math.max(0, rect.x);
|
|
192
|
+
const x1 = Math.min(this.width, rect.x + rect.width);
|
|
193
|
+
const y0 = Math.max(0, rect.y);
|
|
194
|
+
const y1 = Math.min(this.height, rect.y + rect.height);
|
|
195
|
+
if (x1 <= x0 || y1 <= y0) return;
|
|
196
|
+
|
|
197
|
+
// A row at a time: rows are contiguous, columns are not.
|
|
198
|
+
for (let y = y0; y < y1; y++) {
|
|
199
|
+
const start = y * this.width;
|
|
200
|
+
this.fillSpan(start + x0, start + x1, bg);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
private fillSpan(from: number, to: number, bg: PackedColor): void {
|
|
205
|
+
this.chars.fill(' ', from, to);
|
|
206
|
+
this.fg.fill(COLOR_DEFAULT, from, to);
|
|
207
|
+
this.bg.fill(bg, from, to);
|
|
208
|
+
this.attrs.fill(0, from, to);
|
|
209
|
+
this.flags.fill(0, from, to);
|
|
210
|
+
this.links.fill(undefined, from, to);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
resize(width: number, height: number): void {
|
|
214
|
+
if (width === this.width && height === this.height) return;
|
|
215
|
+
const next = new Buffer(width, height);
|
|
216
|
+
const w = Math.min(width, this.width);
|
|
217
|
+
const h = Math.min(height, this.height);
|
|
218
|
+
for (let y = 0; y < h; y++) {
|
|
219
|
+
for (let x = 0; x < w; x++) {
|
|
220
|
+
const from = this.index(x, y);
|
|
221
|
+
const to = next.index(x, y);
|
|
222
|
+
next.chars[to] = this.chars[from] as string;
|
|
223
|
+
next.fg[to] = this.fg[from] as number;
|
|
224
|
+
next.bg[to] = this.bg[from] as number;
|
|
225
|
+
next.attrs[to] = this.attrs[from] as number;
|
|
226
|
+
next.flags[to] = this.flags[from] as number;
|
|
227
|
+
next.links[to] = this.links[from];
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
this.width = next.width;
|
|
231
|
+
this.height = next.height;
|
|
232
|
+
this.chars = next.chars;
|
|
233
|
+
this.fg = next.fg;
|
|
234
|
+
this.bg = next.bg;
|
|
235
|
+
this.attrs = next.attrs;
|
|
236
|
+
this.flags = next.flags;
|
|
237
|
+
this.links = next.links;
|
|
238
|
+
|
|
239
|
+
// A resize invalidates the previous frame entirely.
|
|
240
|
+
const n = width * height;
|
|
241
|
+
this.prevChars = new Array<string>(n).fill(' ');
|
|
242
|
+
this.prevFg = new Int32Array(n).fill(COLOR_DEFAULT);
|
|
243
|
+
this.prevBg = new Int32Array(n).fill(COLOR_DEFAULT);
|
|
244
|
+
this.prevAttrs = new Uint16Array(n);
|
|
245
|
+
this.prevLinks = new Array<string | undefined>(n).fill(undefined);
|
|
246
|
+
this.committed = false;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
cellChanged(i: number): boolean {
|
|
250
|
+
return (
|
|
251
|
+
this.chars[i] !== this.prevChars[i] ||
|
|
252
|
+
this.fg[i] !== this.prevFg[i] ||
|
|
253
|
+
this.bg[i] !== this.prevBg[i] ||
|
|
254
|
+
this.attrs[i] !== this.prevAttrs[i] ||
|
|
255
|
+
this.links[i] !== this.prevLinks[i]
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
dirtyRows(): number[] {
|
|
260
|
+
if (!this.committed) {
|
|
261
|
+
return Array.from({ length: this.height }, (_, y) => y);
|
|
262
|
+
}
|
|
263
|
+
const rows: number[] = [];
|
|
264
|
+
for (let y = 0; y < this.height; y++) {
|
|
265
|
+
const start = y * this.width;
|
|
266
|
+
for (let x = 0; x < this.width; x++) {
|
|
267
|
+
if (this.cellChanged(start + x)) {
|
|
268
|
+
rows.push(y);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return rows;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
commit(): void {
|
|
277
|
+
this.prevChars = this.chars.slice();
|
|
278
|
+
this.prevFg = this.fg.slice();
|
|
279
|
+
this.prevBg = this.bg.slice();
|
|
280
|
+
this.prevAttrs = this.attrs.slice();
|
|
281
|
+
this.prevLinks = this.links.slice();
|
|
282
|
+
this.committed = true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Force the next diff to repaint everything. After a resize or a redraw. */
|
|
286
|
+
invalidate(): void {
|
|
287
|
+
this.committed = false;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
toText(rect?: Rect): string {
|
|
291
|
+
const r = rect ?? { x: 0, y: 0, width: this.width, height: this.height };
|
|
292
|
+
const lines: string[] = [];
|
|
293
|
+
for (let y = r.y; y < r.y + r.height; y++) {
|
|
294
|
+
if (y < 0 || y >= this.height) continue;
|
|
295
|
+
let line = '';
|
|
296
|
+
for (let x = r.x; x < r.x + r.width; x++) {
|
|
297
|
+
if (x < 0 || x >= this.width) continue;
|
|
298
|
+
const i = this.index(x, y);
|
|
299
|
+
if (((this.flags[i] as number) & FLAG_CONTINUATION) !== 0) continue;
|
|
300
|
+
line += this.chars[i] as string;
|
|
301
|
+
}
|
|
302
|
+
lines.push(line.replace(/[ ]+$/, ''));
|
|
303
|
+
}
|
|
304
|
+
return lines.join('\n');
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
export function createBuffer(width: number, height: number): Buffer {
|
|
309
|
+
return new Buffer(width, height);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
function packedToColor(packed: PackedColor): Cell['fg'] {
|
|
313
|
+
if (packed === COLOR_DEFAULT) return 'default';
|
|
314
|
+
if (packed >= 0x1000000) {
|
|
315
|
+
return { rgb: [(packed >> 16) & 0xff, (packed >> 8) & 0xff, packed & 0xff] };
|
|
316
|
+
}
|
|
317
|
+
return { palette: packed };
|
|
318
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { AnsiColorName, Color, ColorDepth } from '../types/index.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Colours are packed into one integer so a cell is cheap to store and cheap to
|
|
5
|
+
* compare - the frame diff runs over every cell, every frame.
|
|
6
|
+
*
|
|
7
|
+
* -1 terminal default
|
|
8
|
+
* 0..255 palette index (0..15 are the ANSI names)
|
|
9
|
+
* >= 0x1000000 24-bit rgb, as 0x1000000 | rrggbb
|
|
10
|
+
*/
|
|
11
|
+
export type PackedColor = number;
|
|
12
|
+
|
|
13
|
+
export const COLOR_DEFAULT: PackedColor = -1;
|
|
14
|
+
const RGB_FLAG = 0x1000000;
|
|
15
|
+
|
|
16
|
+
export const ANSI_NAMES: Record<AnsiColorName, number> = {
|
|
17
|
+
black: 0, red: 1, green: 2, yellow: 3,
|
|
18
|
+
blue: 4, magenta: 5, cyan: 6, white: 7,
|
|
19
|
+
brightBlack: 8, brightRed: 9, brightGreen: 10, brightYellow: 11,
|
|
20
|
+
brightBlue: 12, brightMagenta: 13, brightCyan: 14, brightWhite: 15,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export function packRgb(r: number, g: number, b: number): PackedColor {
|
|
24
|
+
return RGB_FLAG | ((r & 0xff) << 16) | ((g & 0xff) << 8) | (b & 0xff);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isRgb(c: PackedColor): boolean {
|
|
28
|
+
return c >= RGB_FLAG;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function unpackRgb(c: PackedColor): [number, number, number] {
|
|
32
|
+
return [(c >> 16) & 0xff, (c >> 8) & 0xff, c & 0xff];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function packColor(color: Color | undefined): PackedColor {
|
|
36
|
+
if (color === undefined || color === 'default') return COLOR_DEFAULT;
|
|
37
|
+
if (typeof color === 'string') {
|
|
38
|
+
if (color.startsWith('#')) {
|
|
39
|
+
const hex = color.slice(1);
|
|
40
|
+
if (hex.length === 3) {
|
|
41
|
+
const r = parseInt(hex[0] as string, 16);
|
|
42
|
+
const g = parseInt(hex[1] as string, 16);
|
|
43
|
+
const b = parseInt(hex[2] as string, 16);
|
|
44
|
+
return packRgb(r * 17, g * 17, b * 17);
|
|
45
|
+
}
|
|
46
|
+
if (hex.length === 6) {
|
|
47
|
+
return packRgb(
|
|
48
|
+
parseInt(hex.slice(0, 2), 16),
|
|
49
|
+
parseInt(hex.slice(2, 4), 16),
|
|
50
|
+
parseInt(hex.slice(4, 6), 16),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
return COLOR_DEFAULT;
|
|
54
|
+
}
|
|
55
|
+
const idx = ANSI_NAMES[color as AnsiColorName];
|
|
56
|
+
return idx === undefined ? COLOR_DEFAULT : idx;
|
|
57
|
+
}
|
|
58
|
+
if ('rgb' in color) {
|
|
59
|
+
const [r, g, b] = color.rgb;
|
|
60
|
+
return packRgb(r, g, b);
|
|
61
|
+
}
|
|
62
|
+
if ('palette' in color) return color.palette & 0xff;
|
|
63
|
+
return COLOR_DEFAULT;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The xterm 256 palette, so 24-bit can be reduced without a lookup table. */
|
|
67
|
+
const CUBE_STEPS = [0, 95, 135, 175, 215, 255];
|
|
68
|
+
|
|
69
|
+
function nearestCube(v: number): number {
|
|
70
|
+
let best = 0;
|
|
71
|
+
let bestDist = Infinity;
|
|
72
|
+
for (let i = 0; i < CUBE_STEPS.length; i++) {
|
|
73
|
+
const d = Math.abs((CUBE_STEPS[i] as number) - v);
|
|
74
|
+
if (d < bestDist) { bestDist = d; best = i; }
|
|
75
|
+
}
|
|
76
|
+
return best;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function rgbTo256(r: number, g: number, b: number): number {
|
|
80
|
+
// Greyscale ramp is a better fit when the channels are close.
|
|
81
|
+
if (Math.abs(r - g) < 8 && Math.abs(g - b) < 8) {
|
|
82
|
+
if (r < 8) return 16;
|
|
83
|
+
if (r > 248) return 231;
|
|
84
|
+
return 232 + Math.round(((r - 8) / 247) * 23);
|
|
85
|
+
}
|
|
86
|
+
return 16 + 36 * nearestCube(r) + 6 * nearestCube(g) + nearestCube(b);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const ANSI16_RGB: [number, number, number][] = [
|
|
90
|
+
[0, 0, 0], [205, 49, 49], [13, 188, 121], [229, 229, 16],
|
|
91
|
+
[36, 114, 200], [188, 63, 188], [17, 168, 205], [229, 229, 229],
|
|
92
|
+
[102, 102, 102], [241, 76, 76], [35, 209, 139], [245, 245, 67],
|
|
93
|
+
[59, 142, 234], [214, 112, 214], [41, 184, 219], [255, 255, 255],
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
function rgbTo16(r: number, g: number, b: number): number {
|
|
97
|
+
let best = 0;
|
|
98
|
+
let bestDist = Infinity;
|
|
99
|
+
for (let i = 0; i < 16; i++) {
|
|
100
|
+
const [cr, cg, cb] = ANSI16_RGB[i] as [number, number, number];
|
|
101
|
+
const d = (r - cr) ** 2 + (g - cg) ** 2 + (b - cb) ** 2;
|
|
102
|
+
if (d < bestDist) { bestDist = d; best = i; }
|
|
103
|
+
}
|
|
104
|
+
return best;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function palette256ToRgb(idx: number): [number, number, number] {
|
|
108
|
+
if (idx < 16) return ANSI16_RGB[idx] as [number, number, number];
|
|
109
|
+
if (idx >= 232) {
|
|
110
|
+
const v = 8 + (idx - 232) * 10;
|
|
111
|
+
return [v, v, v];
|
|
112
|
+
}
|
|
113
|
+
const n = idx - 16;
|
|
114
|
+
return [
|
|
115
|
+
CUBE_STEPS[Math.floor(n / 36) % 6] as number,
|
|
116
|
+
CUBE_STEPS[Math.floor(n / 6) % 6] as number,
|
|
117
|
+
CUBE_STEPS[n % 6] as number,
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Reduce a colour to what the terminal can show. A component never picks a
|
|
123
|
+
* fallback itself - it names a token, and this decides what that survives as.
|
|
124
|
+
*/
|
|
125
|
+
export function downsample(c: PackedColor, depth: ColorDepth): PackedColor {
|
|
126
|
+
if (c === COLOR_DEFAULT) return c;
|
|
127
|
+
if (depth === 0) return COLOR_DEFAULT;
|
|
128
|
+
if (depth === 24) return c;
|
|
129
|
+
|
|
130
|
+
if (isRgb(c)) {
|
|
131
|
+
const [r, g, b] = unpackRgb(c);
|
|
132
|
+
return depth === 8 ? rgbTo256(r, g, b) : rgbTo16(r, g, b);
|
|
133
|
+
}
|
|
134
|
+
if (depth === 8) return c;
|
|
135
|
+
if (c < 16) return c;
|
|
136
|
+
const [r, g, b] = palette256ToRgb(c);
|
|
137
|
+
return rgbTo16(r, g, b);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Relative luminance, for picking readable foreground over a background. */
|
|
141
|
+
export function luminance(c: PackedColor): number {
|
|
142
|
+
const [r, g, b] = isRgb(c) ? unpackRgb(c) : palette256ToRgb(c < 0 ? 0 : c);
|
|
143
|
+
const f = (v: number): number => {
|
|
144
|
+
const s = v / 255;
|
|
145
|
+
return s <= 0.03928 ? s / 12.92 : ((s + 0.055) / 1.055) ** 2.4;
|
|
146
|
+
};
|
|
147
|
+
return 0.2126 * f(r) + 0.7152 * f(g) + 0.0722 * f(b);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** Mix two packed colours. `t` of 0 is `a`, 1 is `b`. */
|
|
151
|
+
export function mix(a: PackedColor, b: PackedColor, t: number): PackedColor {
|
|
152
|
+
const [ar, ag, ab] = isRgb(a) ? unpackRgb(a) : palette256ToRgb(Math.max(0, a));
|
|
153
|
+
const [br, bg, bb] = isRgb(b) ? unpackRgb(b) : palette256ToRgb(Math.max(0, b));
|
|
154
|
+
return packRgb(
|
|
155
|
+
Math.round(ar + (br - ar) * t),
|
|
156
|
+
Math.round(ag + (bg - ag) * t),
|
|
157
|
+
Math.round(ab + (bb - ab) * t),
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* A packed colour back as a `Color`, which is the shape a style takes.
|
|
163
|
+
*
|
|
164
|
+
* The inverse of `packColor`, for the places that compute a colour and then
|
|
165
|
+
* have to hand it to a component - blending a tint out of two theme colours,
|
|
166
|
+
* say. Painting goes the other way and should stay packed.
|
|
167
|
+
*/
|
|
168
|
+
export function unpackColor(packed: PackedColor): Color {
|
|
169
|
+
if (packed === COLOR_DEFAULT) return 'default';
|
|
170
|
+
if (isRgb(packed)) {
|
|
171
|
+
const [r, g, b] = unpackRgb(packed);
|
|
172
|
+
return { rgb: [r, g, b] };
|
|
173
|
+
}
|
|
174
|
+
return { palette: packed };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function toHex(c: PackedColor): string {
|
|
178
|
+
const [r, g, b] = isRgb(c) ? unpackRgb(c) : palette256ToRgb(Math.max(0, c));
|
|
179
|
+
return `#${[r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
|
|
180
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { Buffer } from './buffer.js';
|
|
2
|
+
import { FLAG_CONTINUATION } from './buffer.js';
|
|
3
|
+
import type { PackedColor } from './color.js';
|
|
4
|
+
|
|
5
|
+
/** One contiguous run of identical-style cells - what the writer emits. */
|
|
6
|
+
export interface Run {
|
|
7
|
+
x: number;
|
|
8
|
+
y: number;
|
|
9
|
+
text: string;
|
|
10
|
+
fg: PackedColor;
|
|
11
|
+
bg: PackedColor;
|
|
12
|
+
attrs: number;
|
|
13
|
+
link: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface Frame {
|
|
17
|
+
runs: Run[];
|
|
18
|
+
cursor: { x: number; y: number; visible: boolean } | null;
|
|
19
|
+
/** True when every row was emitted - first frame, or after invalidate. */
|
|
20
|
+
full: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Turn changed cells into runs.
|
|
25
|
+
*
|
|
26
|
+
* Two economies matter here and nowhere else: only touched rows are walked,
|
|
27
|
+
* and within a row, adjacent changed cells sharing a style become one run, so
|
|
28
|
+
* a redraw costs one cursor move and one SGR change per run rather than per
|
|
29
|
+
* cell. A run continues through a short stretch of unchanged cells when
|
|
30
|
+
* repainting them is cheaper than moving the cursor around them.
|
|
31
|
+
*/
|
|
32
|
+
export function diffFrame(
|
|
33
|
+
buffer: Buffer,
|
|
34
|
+
cursor: { x: number; y: number; visible: boolean } | null = null,
|
|
35
|
+
gapTolerance = 4,
|
|
36
|
+
): Frame {
|
|
37
|
+
const runs: Run[] = [];
|
|
38
|
+
const rows = buffer.dirtyRows();
|
|
39
|
+
const full = rows.length === buffer.height && buffer.height > 0;
|
|
40
|
+
|
|
41
|
+
for (const y of rows) {
|
|
42
|
+
const base = y * buffer.width;
|
|
43
|
+
let x = 0;
|
|
44
|
+
|
|
45
|
+
while (x < buffer.width) {
|
|
46
|
+
const i = base + x;
|
|
47
|
+
if (((buffer.flags[i] as number) & FLAG_CONTINUATION) !== 0 || !buffer.cellChanged(i)) {
|
|
48
|
+
x++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const fg = buffer.fg[i] as number;
|
|
53
|
+
const bg = buffer.bg[i] as number;
|
|
54
|
+
const attrs = buffer.attrs[i] as number;
|
|
55
|
+
const link = buffer.links[i];
|
|
56
|
+
|
|
57
|
+
let text = '';
|
|
58
|
+
let cx = x;
|
|
59
|
+
let gap = 0;
|
|
60
|
+
let pendingGap = '';
|
|
61
|
+
|
|
62
|
+
while (cx < buffer.width) {
|
|
63
|
+
const j = base + cx;
|
|
64
|
+
if (((buffer.flags[j] as number) & FLAG_CONTINUATION) !== 0) {
|
|
65
|
+
cx++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const sameStyle =
|
|
70
|
+
buffer.fg[j] === fg &&
|
|
71
|
+
buffer.bg[j] === bg &&
|
|
72
|
+
buffer.attrs[j] === attrs &&
|
|
73
|
+
buffer.links[j] === link;
|
|
74
|
+
if (!sameStyle) break;
|
|
75
|
+
|
|
76
|
+
if (buffer.cellChanged(j)) {
|
|
77
|
+
if (pendingGap !== '') {
|
|
78
|
+
text += pendingGap;
|
|
79
|
+
pendingGap = '';
|
|
80
|
+
gap = 0;
|
|
81
|
+
}
|
|
82
|
+
text += buffer.chars[j] as string;
|
|
83
|
+
cx++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
gap++;
|
|
88
|
+
if (gap > gapTolerance) break;
|
|
89
|
+
pendingGap += buffer.chars[j] as string;
|
|
90
|
+
cx++;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (text !== '') runs.push({ x, y, text, fg, bg, attrs, link });
|
|
94
|
+
x = cx > x ? cx : x + 1;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { runs, cursor, full };
|
|
99
|
+
}
|