@svelterm/core 0.1.0 → 0.21.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/CHANGELOG.md +425 -0
- package/README.md +42 -29
- package/dist/src/cli/build.d.ts +13 -0
- package/dist/src/cli/build.js +119 -0
- package/dist/src/cli/bundle.d.ts +25 -0
- package/dist/src/cli/bundle.js +61 -0
- package/dist/src/cli/dev.d.ts +10 -0
- package/dist/src/cli/dev.js +152 -0
- package/dist/src/cli/devtools.d.ts +9 -0
- package/dist/src/cli/devtools.js +47 -0
- package/dist/src/cli/init.d.ts +8 -0
- package/dist/src/cli/init.js +153 -0
- package/dist/src/cli/main.d.ts +9 -0
- package/dist/src/cli/main.js +52 -0
- package/dist/src/cli/svt-bin.d.ts +2 -0
- package/dist/src/cli/svt-bin.js +6 -0
- package/dist/src/cli/svt.d.ts +14 -0
- package/dist/src/cli/svt.js +76 -0
- package/dist/src/components/text-buffer.js +8 -5
- package/dist/src/css/animation-runner.d.ts +15 -6
- package/dist/src/css/animation-runner.js +80 -29
- package/dist/src/css/animation.d.ts +12 -0
- package/dist/src/css/animation.js +21 -0
- package/dist/src/css/calc.js +4 -3
- package/dist/src/css/color.d.ts +19 -0
- package/dist/src/css/color.js +371 -62
- package/dist/src/css/compute.d.ts +30 -3
- package/dist/src/css/compute.js +272 -33
- package/dist/src/css/defaults.d.ts +1 -1
- package/dist/src/css/defaults.js +9 -0
- package/dist/src/css/easing.d.ts +9 -0
- package/dist/src/css/easing.js +95 -0
- package/dist/src/css/incremental.d.ts +1 -1
- package/dist/src/css/incremental.js +2 -2
- package/dist/src/css/interpolate.d.ts +13 -0
- package/dist/src/css/interpolate.js +41 -0
- package/dist/src/css/parser.js +59 -3
- package/dist/src/css/pseudo-elements.d.ts +9 -0
- package/dist/src/css/pseudo-elements.js +97 -0
- package/dist/src/css/selector.d.ts +17 -2
- package/dist/src/css/selector.js +128 -13
- package/dist/src/css/specificity.js +17 -6
- package/dist/src/css/values.d.ts +6 -1
- package/dist/src/css/values.js +13 -6
- package/dist/src/debug/context.d.ts +13 -0
- package/dist/src/debug/context.js +11 -0
- package/dist/src/debug/css.d.ts +12 -0
- package/dist/src/debug/css.js +28 -0
- package/dist/src/debug/dom.d.ts +17 -0
- package/dist/src/debug/dom.js +92 -0
- package/dist/src/devtools/DevTools.compiled.js +327 -0
- package/dist/src/devtools/DevTools.css.js +1 -0
- package/dist/src/devtools/client.d.ts +36 -0
- package/dist/src/devtools/client.js +76 -0
- package/dist/src/framelog.d.ts +54 -0
- package/dist/src/framelog.js +99 -0
- package/dist/src/headless.js +12 -4
- package/dist/src/index.d.ts +65 -3
- package/dist/src/index.js +609 -81
- package/dist/src/input/checkable.d.ts +8 -0
- package/dist/src/input/checkable.js +66 -0
- package/dist/src/input/details.d.ts +6 -0
- package/dist/src/input/details.js +34 -0
- package/dist/src/input/focus.d.ts +6 -0
- package/dist/src/input/focus.js +27 -9
- package/dist/src/input/keyboard.d.ts +2 -2
- package/dist/src/input/keyboard.js +32 -5
- package/dist/src/input/label.d.ts +8 -0
- package/dist/src/input/label.js +53 -0
- package/dist/src/input/modal.d.ts +9 -0
- package/dist/src/input/modal.js +28 -0
- package/dist/src/input/mouse.d.ts +2 -2
- package/dist/src/input/mouse.js +15 -2
- package/dist/src/input/select.d.ts +12 -0
- package/dist/src/input/select.js +63 -0
- package/dist/src/input/selection.d.ts +48 -0
- package/dist/src/input/selection.js +150 -0
- package/dist/src/layout/engine.d.ts +2 -0
- package/dist/src/layout/engine.js +1084 -142
- package/dist/src/layout/flex.js +4 -4
- package/dist/src/layout/size.js +3 -2
- package/dist/src/layout/text.d.ts +3 -2
- package/dist/src/layout/text.js +96 -17
- package/dist/src/layout/unicode.d.ts +20 -0
- package/dist/src/layout/unicode.js +121 -0
- package/dist/src/render/animation-clock.d.ts +51 -0
- package/dist/src/render/animation-clock.js +213 -0
- package/dist/src/render/ansi-text.d.ts +26 -0
- package/dist/src/render/ansi-text.js +131 -0
- package/dist/src/render/ansi.d.ts +18 -0
- package/dist/src/render/ansi.js +64 -19
- package/dist/src/render/border.js +166 -17
- package/dist/src/render/buffer.d.ts +1 -0
- package/dist/src/render/buffer.js +5 -2
- package/dist/src/render/color-depth.d.ts +8 -0
- package/dist/src/render/color-depth.js +59 -0
- package/dist/src/render/context.d.ts +1 -0
- package/dist/src/render/context.js +17 -21
- package/dist/src/render/cursor-emit.d.ts +18 -0
- package/dist/src/render/cursor-emit.js +50 -0
- package/dist/src/render/diff.d.ts +12 -0
- package/dist/src/render/diff.js +120 -0
- package/dist/src/render/generation.d.ts +9 -0
- package/dist/src/render/generation.js +14 -0
- package/dist/src/render/graphics-layer.d.ts +27 -0
- package/dist/src/render/graphics-layer.js +86 -0
- package/dist/src/render/image.d.ts +27 -0
- package/dist/src/render/image.js +113 -0
- package/dist/src/render/incremental-paint.d.ts +7 -3
- package/dist/src/render/incremental-paint.js +52 -79
- package/dist/src/render/inline.d.ts +59 -0
- package/dist/src/render/inline.js +219 -0
- package/dist/src/render/kitty-graphics.d.ts +24 -0
- package/dist/src/render/kitty-graphics.js +58 -0
- package/dist/src/render/paint-text.js +68 -22
- package/dist/src/render/paint.d.ts +8 -1
- package/dist/src/render/paint.js +328 -30
- package/dist/src/render/png.d.ts +13 -0
- package/dist/src/render/png.js +145 -0
- package/dist/src/render/scrollbar.d.ts +8 -2
- package/dist/src/render/scrollbar.js +71 -14
- package/dist/src/render/snapshot.js +3 -1
- package/dist/src/renderer/default.d.ts +7 -0
- package/dist/src/renderer/default.js +11 -0
- package/dist/src/renderer/index.d.ts +8 -2
- package/dist/src/renderer/index.js +4 -2
- package/dist/src/renderer/node.d.ts +109 -0
- package/dist/src/renderer/node.js +165 -1
- package/dist/src/terminal/capabilities.d.ts +33 -0
- package/dist/src/terminal/capabilities.js +66 -0
- package/dist/src/terminal/clipboard.d.ts +9 -0
- package/dist/src/terminal/clipboard.js +39 -0
- package/dist/src/terminal/io.d.ts +82 -0
- package/dist/src/terminal/io.js +155 -0
- package/dist/src/terminal/screen.d.ts +3 -10
- package/dist/src/terminal/screen.js +5 -28
- package/dist/src/terminal/stdin-router.d.ts +8 -5
- package/dist/src/terminal/stdin-router.js +22 -11
- package/dist/src/utils/node-map.d.ts +24 -0
- package/dist/src/utils/node-map.js +75 -0
- package/dist/src/vite/config.d.ts +62 -0
- package/dist/src/vite/config.js +191 -0
- package/docs/compatibility.md +67 -0
- package/docs/debug/devtools.md +40 -0
- package/docs/debug/svt.md +50 -0
- package/docs/distribution.md +106 -0
- package/docs/elements.md +120 -0
- package/docs/getting-started.md +177 -0
- package/docs/guide/css.md +187 -0
- package/docs/guide/input.md +143 -0
- package/docs/guide/layout.md +171 -0
- package/docs/guide/theming.md +94 -0
- package/docs/how-it-works.md +115 -0
- package/docs/inline-mode.md +77 -0
- package/docs/layout.md +106 -0
- package/docs/motion.md +91 -0
- package/docs/reference/README.md +65 -0
- package/docs/reference/css/properties/border-corner.md +82 -0
- package/docs/reference/css/properties/border-style.md +168 -0
- package/docs/reference.md +226 -0
- package/docs/selectors.md +80 -0
- package/docs/terminal-css.md +149 -0
- package/docs/terminals.md +83 -0
- package/package.json +28 -7
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ANSI/SGR parser for the <svt-ansi> passthrough element:
|
|
3
|
+
* pre-styled tool output (git diff, ls --color, build logs) renders as
|
|
4
|
+
* styled cells. SGR sequences apply; every other escape sequence is
|
|
5
|
+
* dropped. Content is `pre` — lines split on newline, no wrapping.
|
|
6
|
+
*/
|
|
7
|
+
const SGR_NAMES = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'];
|
|
8
|
+
/** Standard xterm palette entries 8–15 (the bright variants). */
|
|
9
|
+
const BRIGHT_HEX = ['#808080', '#ff0000', '#00ff00', '#ffff00', '#0000ff', '#ff00ff', '#00ffff', '#ffffff'];
|
|
10
|
+
const CUBE_LEVELS = [0, 95, 135, 175, 215, 255];
|
|
11
|
+
/** The xterm 256-palette entry as #rrggbb (16–255; 0–15 use names/brights). */
|
|
12
|
+
export function palette256(index) {
|
|
13
|
+
if (index < 8)
|
|
14
|
+
return SGR_NAMES[index];
|
|
15
|
+
if (index < 16)
|
|
16
|
+
return BRIGHT_HEX[index - 8];
|
|
17
|
+
if (index < 232) {
|
|
18
|
+
const n = index - 16;
|
|
19
|
+
const r = CUBE_LEVELS[Math.floor(n / 36)];
|
|
20
|
+
const g = CUBE_LEVELS[Math.floor(n / 6) % 6];
|
|
21
|
+
const b = CUBE_LEVELS[n % 6];
|
|
22
|
+
return hex(r, g, b);
|
|
23
|
+
}
|
|
24
|
+
const grey = 8 + (index - 232) * 10;
|
|
25
|
+
return hex(grey, grey, grey);
|
|
26
|
+
}
|
|
27
|
+
function hex(r, g, b) {
|
|
28
|
+
return '#' + [r, g, b].map(c => c.toString(16).padStart(2, '0')).join('');
|
|
29
|
+
}
|
|
30
|
+
const DEFAULT_STYLE = {
|
|
31
|
+
fg: 'default', bg: 'default',
|
|
32
|
+
bold: false, dim: false, italic: false,
|
|
33
|
+
underline: false, strikethrough: false, inverse: false,
|
|
34
|
+
};
|
|
35
|
+
const ESCAPE_RE = /\x1b(?:\[([0-9;]*)([a-zA-Z])|\][^\x07\x1b]*(?:\x07|\x1b\\)|[a-zA-Z=><])/;
|
|
36
|
+
/** Parse ANSI-styled text into lines of styled cells. */
|
|
37
|
+
export function parseAnsiText(text) {
|
|
38
|
+
const lines = [[]];
|
|
39
|
+
let style = { ...DEFAULT_STYLE };
|
|
40
|
+
let rest = text;
|
|
41
|
+
while (rest.length > 0) {
|
|
42
|
+
const match = ESCAPE_RE.exec(rest);
|
|
43
|
+
const plain = match ? rest.slice(0, match.index) : rest;
|
|
44
|
+
for (const char of plain) {
|
|
45
|
+
const line = lines[lines.length - 1];
|
|
46
|
+
if (char === '\n')
|
|
47
|
+
lines.push([]);
|
|
48
|
+
else if (char === '\r')
|
|
49
|
+
continue;
|
|
50
|
+
else if (char === '\t') {
|
|
51
|
+
const stop = (Math.floor(line.length / 8) + 1) * 8;
|
|
52
|
+
while (line.length < stop)
|
|
53
|
+
line.push({ char: ' ', ...style });
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
line.push({ char, ...style });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (!match)
|
|
60
|
+
break;
|
|
61
|
+
if (match[2] === 'm')
|
|
62
|
+
style = applySgr(style, match[1] ?? '');
|
|
63
|
+
rest = rest.slice(match.index + match[0].length);
|
|
64
|
+
}
|
|
65
|
+
return lines;
|
|
66
|
+
}
|
|
67
|
+
function applySgr(style, params) {
|
|
68
|
+
const next = { ...style };
|
|
69
|
+
const codes = params === '' ? [0] : params.split(';').map(n => parseInt(n, 10) || 0);
|
|
70
|
+
for (let i = 0; i < codes.length; i++) {
|
|
71
|
+
const code = codes[i];
|
|
72
|
+
if (code === 0)
|
|
73
|
+
Object.assign(next, DEFAULT_STYLE);
|
|
74
|
+
else if (code === 1)
|
|
75
|
+
next.bold = true;
|
|
76
|
+
else if (code === 2)
|
|
77
|
+
next.dim = true;
|
|
78
|
+
else if (code === 3)
|
|
79
|
+
next.italic = true;
|
|
80
|
+
else if (code === 4)
|
|
81
|
+
next.underline = true;
|
|
82
|
+
else if (code === 7)
|
|
83
|
+
next.inverse = true;
|
|
84
|
+
else if (code === 9)
|
|
85
|
+
next.strikethrough = true;
|
|
86
|
+
else if (code === 22) {
|
|
87
|
+
next.bold = false;
|
|
88
|
+
next.dim = false;
|
|
89
|
+
}
|
|
90
|
+
else if (code === 23)
|
|
91
|
+
next.italic = false;
|
|
92
|
+
else if (code === 24)
|
|
93
|
+
next.underline = false;
|
|
94
|
+
else if (code === 27)
|
|
95
|
+
next.inverse = false;
|
|
96
|
+
else if (code === 29)
|
|
97
|
+
next.strikethrough = false;
|
|
98
|
+
else if (code >= 30 && code <= 37)
|
|
99
|
+
next.fg = SGR_NAMES[code - 30];
|
|
100
|
+
else if (code === 39)
|
|
101
|
+
next.fg = 'default';
|
|
102
|
+
else if (code >= 40 && code <= 47)
|
|
103
|
+
next.bg = SGR_NAMES[code - 40];
|
|
104
|
+
else if (code === 49)
|
|
105
|
+
next.bg = 'default';
|
|
106
|
+
else if (code >= 90 && code <= 97)
|
|
107
|
+
next.fg = BRIGHT_HEX[code - 90];
|
|
108
|
+
else if (code >= 100 && code <= 107)
|
|
109
|
+
next.bg = BRIGHT_HEX[code - 100];
|
|
110
|
+
else if (code === 38 || code === 48) {
|
|
111
|
+
const target = code === 38 ? 'fg' : 'bg';
|
|
112
|
+
if (codes[i + 1] === 5 && codes.length > i + 2) {
|
|
113
|
+
next[target] = palette256(codes[i + 2]);
|
|
114
|
+
i += 2;
|
|
115
|
+
}
|
|
116
|
+
else if (codes[i + 1] === 2 && codes.length > i + 4) {
|
|
117
|
+
next[target] = hex(codes[i + 2], codes[i + 3], codes[i + 4]);
|
|
118
|
+
i += 4;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return next;
|
|
123
|
+
}
|
|
124
|
+
/** Width/height of parsed content (pre semantics — longest line, no wrap). */
|
|
125
|
+
export function measureAnsiText(text) {
|
|
126
|
+
const lines = parseAnsiText(text);
|
|
127
|
+
return {
|
|
128
|
+
width: lines.reduce((max, line) => Math.max(max, line.length), 0),
|
|
129
|
+
height: lines.length,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import type { ColorDepth } from '../terminal/capabilities.js';
|
|
2
|
+
export declare function setColorDepth(depth: ColorDepth): void;
|
|
3
|
+
export declare function getColorDepth(): ColorDepth;
|
|
1
4
|
export declare function moveTo(col: number, row: number): string;
|
|
2
5
|
export declare function clearScreen(): string;
|
|
3
6
|
export declare function hideCursor(): string;
|
|
@@ -10,6 +13,7 @@ export declare function dim(): string;
|
|
|
10
13
|
export declare function italic(): string;
|
|
11
14
|
export declare function underline(): string;
|
|
12
15
|
export declare function strikethrough(): string;
|
|
16
|
+
export declare function inverse(): string;
|
|
13
17
|
export declare function fgColor(color: string): string;
|
|
14
18
|
export declare function bgColor(color: string): string;
|
|
15
19
|
export declare function hyperlinkOpen(url: string): string;
|
|
@@ -17,7 +21,21 @@ export declare function hyperlinkClose(): string;
|
|
|
17
21
|
export declare function enableMouse(): string;
|
|
18
22
|
export declare function disableMouse(): string;
|
|
19
23
|
export declare function setCursorShape(shape: 'block' | 'underline' | 'bar'): string;
|
|
24
|
+
/** DECSCUSR 0 — the terminal's configured default cursor. */
|
|
25
|
+
export declare function resetCursorShape(): string;
|
|
26
|
+
/** Kitty keyboard protocol: push disambiguate-escape-codes mode. */
|
|
27
|
+
export declare function pushKittyKeyboard(): string;
|
|
28
|
+
/** Kitty keyboard protocol: pop our mode entry. */
|
|
29
|
+
export declare function popKittyKeyboard(): string;
|
|
20
30
|
export declare function enableBracketedPaste(): string;
|
|
21
31
|
export declare function disableBracketedPaste(): string;
|
|
32
|
+
/** DECSTBM — set the scroll region to rows [top, bottom] (1-based). */
|
|
33
|
+
export declare function setScrollRegion(top: number, bottom: number): string;
|
|
34
|
+
/** DECSTBM reset — scroll region back to the full screen. */
|
|
35
|
+
export declare function resetScrollRegion(): string;
|
|
36
|
+
/** IND — index: move down one line, scrolling the region at its bottom. */
|
|
37
|
+
export declare function index(): string;
|
|
38
|
+
/** RI — reverse index: move up one line, scrolling the region at its top. */
|
|
39
|
+
export declare function reverseIndex(): string;
|
|
22
40
|
export declare function beginSyncUpdate(): string;
|
|
23
41
|
export declare function endSyncUpdate(): string;
|
package/dist/src/render/ansi.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import { quantizeTo256, quantizeTo16 } from './color-depth.js';
|
|
1
2
|
const ESC = '\x1b';
|
|
2
3
|
const CSI = `${ESC}[`;
|
|
4
|
+
// Colour depth applies to the whole process's output stream, set once
|
|
5
|
+
// after capability detection (default: truecolor, today's common case).
|
|
6
|
+
let colorDepth = 'truecolor';
|
|
7
|
+
export function setColorDepth(depth) {
|
|
8
|
+
colorDepth = depth;
|
|
9
|
+
}
|
|
10
|
+
export function getColorDepth() {
|
|
11
|
+
return colorDepth;
|
|
12
|
+
}
|
|
3
13
|
function expandHex(color) {
|
|
4
14
|
if (color.length === 4) {
|
|
5
15
|
return '#' + color[1] + color[1] + color[2] + color[2] + color[3] + color[3];
|
|
@@ -42,31 +52,38 @@ export function underline() {
|
|
|
42
52
|
export function strikethrough() {
|
|
43
53
|
return `${CSI}9m`;
|
|
44
54
|
}
|
|
55
|
+
export function inverse() {
|
|
56
|
+
return `${CSI}7m`;
|
|
57
|
+
}
|
|
45
58
|
export function fgColor(color) {
|
|
46
|
-
|
|
47
|
-
if (code !== undefined)
|
|
48
|
-
return `${CSI}${code}m`;
|
|
49
|
-
if (color.startsWith('#')) {
|
|
50
|
-
const hex = expandHex(color);
|
|
51
|
-
const r = parseInt(hex.slice(1, 3), 16);
|
|
52
|
-
const g = parseInt(hex.slice(3, 5), 16);
|
|
53
|
-
const b = parseInt(hex.slice(5, 7), 16);
|
|
54
|
-
return `${CSI}38;2;${r};${g};${b}m`;
|
|
55
|
-
}
|
|
56
|
-
return '';
|
|
59
|
+
return sgrColor(color, ANSI_FG, 38);
|
|
57
60
|
}
|
|
58
61
|
export function bgColor(color) {
|
|
59
|
-
|
|
62
|
+
return sgrColor(color, ANSI_BG, 48);
|
|
63
|
+
}
|
|
64
|
+
function sgrColor(color, names, extended) {
|
|
65
|
+
if (colorDepth === 'mono') {
|
|
66
|
+
// Colour is disabled; only default (the reset) still emits.
|
|
67
|
+
return color === 'default' ? `${CSI}${names.default}m` : '';
|
|
68
|
+
}
|
|
69
|
+
const code = names[color];
|
|
60
70
|
if (code !== undefined)
|
|
61
71
|
return `${CSI}${code}m`;
|
|
62
|
-
if (color.startsWith('#'))
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
if (!color.startsWith('#'))
|
|
73
|
+
return '';
|
|
74
|
+
const hex = expandHex(color);
|
|
75
|
+
switch (colorDepth) {
|
|
76
|
+
case '256':
|
|
77
|
+
return `${CSI}${extended};5;${quantizeTo256(hex)}m`;
|
|
78
|
+
case '16':
|
|
79
|
+
return `${CSI}${names[quantizeTo16(hex)]}m`;
|
|
80
|
+
default: {
|
|
81
|
+
const r = parseInt(hex.slice(1, 3), 16);
|
|
82
|
+
const g = parseInt(hex.slice(3, 5), 16);
|
|
83
|
+
const b = parseInt(hex.slice(5, 7), 16);
|
|
84
|
+
return `${CSI}${extended};2;${r};${g};${b}m`;
|
|
85
|
+
}
|
|
68
86
|
}
|
|
69
|
-
return '';
|
|
70
87
|
}
|
|
71
88
|
const ANSI_FG = {
|
|
72
89
|
black: 30, red: 31, green: 32, yellow: 33,
|
|
@@ -89,12 +106,40 @@ export function setCursorShape(shape) {
|
|
|
89
106
|
const code = shape === 'block' ? 2 : shape === 'underline' ? 4 : 6;
|
|
90
107
|
return `${CSI}${code} q`;
|
|
91
108
|
}
|
|
109
|
+
/** DECSCUSR 0 — the terminal's configured default cursor. */
|
|
110
|
+
export function resetCursorShape() {
|
|
111
|
+
return `${CSI}0 q`;
|
|
112
|
+
}
|
|
113
|
+
/** Kitty keyboard protocol: push disambiguate-escape-codes mode. */
|
|
114
|
+
export function pushKittyKeyboard() {
|
|
115
|
+
return `${CSI}>1u`;
|
|
116
|
+
}
|
|
117
|
+
/** Kitty keyboard protocol: pop our mode entry. */
|
|
118
|
+
export function popKittyKeyboard() {
|
|
119
|
+
return `${CSI}<u`;
|
|
120
|
+
}
|
|
92
121
|
export function enableBracketedPaste() {
|
|
93
122
|
return `${CSI}?2004h`;
|
|
94
123
|
}
|
|
95
124
|
export function disableBracketedPaste() {
|
|
96
125
|
return `${CSI}?2004l`;
|
|
97
126
|
}
|
|
127
|
+
/** DECSTBM — set the scroll region to rows [top, bottom] (1-based). */
|
|
128
|
+
export function setScrollRegion(top, bottom) {
|
|
129
|
+
return `${CSI}${top};${bottom}r`;
|
|
130
|
+
}
|
|
131
|
+
/** DECSTBM reset — scroll region back to the full screen. */
|
|
132
|
+
export function resetScrollRegion() {
|
|
133
|
+
return `${CSI}r`;
|
|
134
|
+
}
|
|
135
|
+
/** IND — index: move down one line, scrolling the region at its bottom. */
|
|
136
|
+
export function index() {
|
|
137
|
+
return `${ESC}D`;
|
|
138
|
+
}
|
|
139
|
+
/** RI — reverse index: move up one line, scrolling the region at its top. */
|
|
140
|
+
export function reverseIndex() {
|
|
141
|
+
return `${ESC}M`;
|
|
142
|
+
}
|
|
98
143
|
export function beginSyncUpdate() {
|
|
99
144
|
return `${CSI}?2026h`;
|
|
100
145
|
}
|
|
@@ -1,12 +1,96 @@
|
|
|
1
1
|
const BORDER_SETS = {
|
|
2
|
-
single: { topLeft: '┌', topRight: '┐', bottomLeft: '└', bottomRight: '┘', horizontal: '─', vertical: '│' },
|
|
3
|
-
double: { topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝', horizontal: '═', vertical: '║' },
|
|
4
|
-
rounded: { topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯', horizontal: '─', vertical: '│' },
|
|
5
|
-
heavy: { topLeft: '┏', topRight: '┓', bottomLeft: '┗', bottomRight: '┛', horizontal: '━', vertical: '┃' },
|
|
2
|
+
single: { topLeft: '┌', topRight: '┐', bottomLeft: '└', bottomRight: '┘', horizontal: '─', vertical: '│', teeLeft: '├', teeRight: '┤', teeTop: '┬', teeBottom: '┴', cross: '┼' },
|
|
3
|
+
double: { topLeft: '╔', topRight: '╗', bottomLeft: '╚', bottomRight: '╝', horizontal: '═', vertical: '║', teeLeft: '╠', teeRight: '╣', teeTop: '╦', teeBottom: '╩', cross: '╬' },
|
|
4
|
+
rounded: { topLeft: '╭', topRight: '╮', bottomLeft: '╰', bottomRight: '╯', horizontal: '─', vertical: '│', teeLeft: '├', teeRight: '┤', teeTop: '┬', teeBottom: '┴', cross: '┼' },
|
|
5
|
+
heavy: { topLeft: '┏', topRight: '┓', bottomLeft: '┗', bottomRight: '┛', horizontal: '━', vertical: '┃', teeLeft: '┣', teeRight: '┫', teeTop: '┳', teeBottom: '┻', cross: '╋' },
|
|
6
|
+
ascii: { topLeft: '+', topRight: '+', bottomLeft: '+', bottomRight: '+', horizontal: '-', vertical: '|', teeLeft: '+', teeRight: '+', teeTop: '+', teeBottom: '+', cross: '+' },
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Direction masks for box-drawing glyph merging. Each glyph is the set of
|
|
10
|
+
* directions its strokes point in; overlapping glyphs merge by unioning
|
|
11
|
+
* their masks (e.g. ┐ over └ → all four directions → ┼).
|
|
12
|
+
*/
|
|
13
|
+
const UP = 1, RIGHT = 2, DOWN = 4, LEFT = 8;
|
|
14
|
+
const GLYPH_MASKS = {};
|
|
15
|
+
for (const s of Object.values(BORDER_SETS)) {
|
|
16
|
+
GLYPH_MASKS[s.topLeft] = RIGHT | DOWN;
|
|
17
|
+
GLYPH_MASKS[s.topRight] = LEFT | DOWN;
|
|
18
|
+
GLYPH_MASKS[s.bottomLeft] = UP | RIGHT;
|
|
19
|
+
GLYPH_MASKS[s.bottomRight] = UP | LEFT;
|
|
20
|
+
GLYPH_MASKS[s.horizontal] = LEFT | RIGHT;
|
|
21
|
+
GLYPH_MASKS[s.vertical] = UP | DOWN;
|
|
22
|
+
GLYPH_MASKS[s.teeLeft] = UP | DOWN | RIGHT;
|
|
23
|
+
GLYPH_MASKS[s.teeRight] = UP | DOWN | LEFT;
|
|
24
|
+
GLYPH_MASKS[s.teeTop] = LEFT | RIGHT | DOWN;
|
|
25
|
+
GLYPH_MASKS[s.teeBottom] = LEFT | RIGHT | UP;
|
|
26
|
+
GLYPH_MASKS[s.cross] = UP | RIGHT | DOWN | LEFT;
|
|
27
|
+
}
|
|
28
|
+
function glyphForMask(chars, mask) {
|
|
29
|
+
switch (mask) {
|
|
30
|
+
case RIGHT | DOWN: return chars.topLeft;
|
|
31
|
+
case LEFT | DOWN: return chars.topRight;
|
|
32
|
+
case UP | RIGHT: return chars.bottomLeft;
|
|
33
|
+
case UP | LEFT: return chars.bottomRight;
|
|
34
|
+
case LEFT | RIGHT: return chars.horizontal;
|
|
35
|
+
case UP | DOWN: return chars.vertical;
|
|
36
|
+
case UP | DOWN | RIGHT: return chars.teeLeft;
|
|
37
|
+
case UP | DOWN | LEFT: return chars.teeRight;
|
|
38
|
+
case LEFT | RIGHT | DOWN: return chars.teeTop;
|
|
39
|
+
case LEFT | RIGHT | UP: return chars.teeBottom;
|
|
40
|
+
case UP | RIGHT | DOWN | LEFT: return chars.cross;
|
|
41
|
+
default: return undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Merge a border glyph with whatever box-drawing character is already in the
|
|
46
|
+
* buffer cell, producing T-junctions and crosses where strokes meet. The new
|
|
47
|
+
* glyph's family (single/double/...) wins for the merged character.
|
|
48
|
+
*/
|
|
49
|
+
function mergeGlyph(buffer, cx, cy, newMask, chars) {
|
|
50
|
+
const fallback = glyphForMask(chars, newMask) ?? chars.cross;
|
|
51
|
+
const existing = buffer.getCell(cx, cy)?.char;
|
|
52
|
+
const existingMask = existing !== undefined ? GLYPH_MASKS[existing] : undefined;
|
|
53
|
+
if (existingMask === undefined)
|
|
54
|
+
return fallback;
|
|
55
|
+
return glyphForMask(chars, existingMask | newMask) ?? fallback;
|
|
56
|
+
}
|
|
57
|
+
const BLOCK_EDGES = {
|
|
58
|
+
'eighth-cell-inner': { top: '\u2581', bottom: '\u2594', left: '\u2595', right: '\u258F' },
|
|
59
|
+
'eighth-cell-outer': { top: '\u2594', bottom: '\u2581', left: '\u258F', right: '\u2595' },
|
|
60
|
+
'half-cell-inner': { top: '\u2584', bottom: '\u2580', left: '\u2590', right: '\u258C' },
|
|
61
|
+
'half-cell-outer': { top: '\u2580', bottom: '\u2584', left: '\u258C', right: '\u2590' },
|
|
62
|
+
'full-cell': { top: '\u2588', bottom: '\u2588', left: '\u2588', right: '\u2588' },
|
|
63
|
+
};
|
|
64
|
+
const BLOCK_CORNERS = {
|
|
65
|
+
// Inner-facing: strokes face the content area, so corner glyphs fill the
|
|
66
|
+
// inner quadrant relative to the cell (e.g. top-left corner fills lower-right).
|
|
67
|
+
'half-cell-inner': {
|
|
68
|
+
topLeft: '\u2597', // ▗ lower-right quadrant
|
|
69
|
+
topRight: '\u2596', // ▖ lower-left quadrant
|
|
70
|
+
bottomLeft: '\u259D', // ▝ upper-right quadrant
|
|
71
|
+
bottomRight: '\u2598', // ▘ upper-left quadrant
|
|
72
|
+
},
|
|
73
|
+
// Outer-facing: strokes face outward. Corner cells combine the full upper
|
|
74
|
+
// half (or lower half) with the full left half (or right half), forming
|
|
75
|
+
// three-quadrant L glyphs with just the diagonally-inner quadrant empty.
|
|
76
|
+
'half-cell-outer': {
|
|
77
|
+
topLeft: '\u259B', // ▛ TL+TR+BL (missing BR)
|
|
78
|
+
topRight: '\u259C', // ▜ TL+TR+BR (missing BL)
|
|
79
|
+
bottomLeft: '\u2599', // ▙ TL+BL+BR (missing TR)
|
|
80
|
+
bottomRight: '\u259F', // ▟ TR+BL+BR (missing TL)
|
|
81
|
+
},
|
|
82
|
+
'full-cell': {
|
|
83
|
+
topLeft: '\u2588', topRight: '\u2588', bottomLeft: '\u2588', bottomRight: '\u2588',
|
|
84
|
+
},
|
|
6
85
|
};
|
|
7
86
|
export function renderBorder(buffer, box, style) {
|
|
8
87
|
if (style.borderStyle === 'none')
|
|
9
88
|
return;
|
|
89
|
+
const blockEdges = BLOCK_EDGES[style.borderStyle];
|
|
90
|
+
if (blockEdges) {
|
|
91
|
+
renderBlockBorder(buffer, box, style, blockEdges);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
10
94
|
const chars = BORDER_SETS[style.borderStyle];
|
|
11
95
|
if (!chars)
|
|
12
96
|
return;
|
|
@@ -16,21 +100,25 @@ export function renderBorder(buffer, box, style) {
|
|
|
16
100
|
const right = style.borderRight;
|
|
17
101
|
const bottom = style.borderBottom;
|
|
18
102
|
const left = style.borderLeft;
|
|
19
|
-
// Corners
|
|
20
|
-
if (top && left)
|
|
21
|
-
buffer.setCell(x, y, { char: chars
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
if (bottom &&
|
|
27
|
-
buffer.setCell(x +
|
|
103
|
+
// Corners — merge into T-junctions or crosses when overlapping a sibling's border
|
|
104
|
+
if (top && left) {
|
|
105
|
+
buffer.setCell(x, y, { char: mergeGlyph(buffer, x, y, RIGHT | DOWN, chars), fg });
|
|
106
|
+
}
|
|
107
|
+
if (top && right) {
|
|
108
|
+
buffer.setCell(x + width - 1, y, { char: mergeGlyph(buffer, x + width - 1, y, LEFT | DOWN, chars), fg });
|
|
109
|
+
}
|
|
110
|
+
if (bottom && left) {
|
|
111
|
+
buffer.setCell(x, y + height - 1, { char: mergeGlyph(buffer, x, y + height - 1, UP | RIGHT, chars), fg });
|
|
112
|
+
}
|
|
113
|
+
if (bottom && right) {
|
|
114
|
+
buffer.setCell(x + width - 1, y + height - 1, { char: mergeGlyph(buffer, x + width - 1, y + height - 1, UP | LEFT, chars), fg });
|
|
115
|
+
}
|
|
28
116
|
// Top edge
|
|
29
117
|
if (top) {
|
|
30
118
|
const startCol = left ? x + 1 : x;
|
|
31
119
|
const endCol = right ? x + width - 1 : x + width;
|
|
32
120
|
for (let col = startCol; col < endCol; col++) {
|
|
33
|
-
buffer.setCell(col, y, { char: chars
|
|
121
|
+
buffer.setCell(col, y, { char: mergeGlyph(buffer, col, y, LEFT | RIGHT, chars), fg });
|
|
34
122
|
}
|
|
35
123
|
}
|
|
36
124
|
// Bottom edge
|
|
@@ -38,7 +126,7 @@ export function renderBorder(buffer, box, style) {
|
|
|
38
126
|
const startCol = left ? x + 1 : x;
|
|
39
127
|
const endCol = right ? x + width - 1 : x + width;
|
|
40
128
|
for (let col = startCol; col < endCol; col++) {
|
|
41
|
-
buffer.setCell(col, y + height - 1, { char: chars
|
|
129
|
+
buffer.setCell(col, y + height - 1, { char: mergeGlyph(buffer, col, y + height - 1, LEFT | RIGHT, chars), fg });
|
|
42
130
|
}
|
|
43
131
|
}
|
|
44
132
|
// Left edge
|
|
@@ -46,7 +134,7 @@ export function renderBorder(buffer, box, style) {
|
|
|
46
134
|
const startRow = top ? y + 1 : y;
|
|
47
135
|
const endRow = bottom ? y + height - 1 : y + height;
|
|
48
136
|
for (let row = startRow; row < endRow; row++) {
|
|
49
|
-
buffer.setCell(x, row, { char: chars
|
|
137
|
+
buffer.setCell(x, row, { char: mergeGlyph(buffer, x, row, UP | DOWN, chars), fg });
|
|
50
138
|
}
|
|
51
139
|
}
|
|
52
140
|
// Right edge
|
|
@@ -54,7 +142,68 @@ export function renderBorder(buffer, box, style) {
|
|
|
54
142
|
const startRow = top ? y + 1 : y;
|
|
55
143
|
const endRow = bottom ? y + height - 1 : y + height;
|
|
56
144
|
for (let row = startRow; row < endRow; row++) {
|
|
57
|
-
buffer.setCell(x + width - 1, row, { char: chars
|
|
145
|
+
buffer.setCell(x + width - 1, row, { char: mergeGlyph(buffer, x + width - 1, row, UP | DOWN, chars), fg });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Render a block-character border (eighth-cell-*, half-cell-*, full-cell).
|
|
151
|
+
* Corner cells are owned by the axis selected via border-corner: 'h' (default top/bottom),
|
|
152
|
+
* 'v' (sides), or 'none' (corners blank).
|
|
153
|
+
*/
|
|
154
|
+
function renderBlockBorder(buffer, box, style, edges) {
|
|
155
|
+
const fg = style.borderColor !== 'default' ? style.borderColor : undefined;
|
|
156
|
+
const { x, y, width, height } = box;
|
|
157
|
+
const top = style.borderTop;
|
|
158
|
+
const right = style.borderRight;
|
|
159
|
+
const bottom = style.borderBottom;
|
|
160
|
+
const left = style.borderLeft;
|
|
161
|
+
const corner = style.borderCorner;
|
|
162
|
+
// Corner ownership controls how far horizontal vs vertical edges extend.
|
|
163
|
+
// 'h' = top/bottom strokes extend through the full row, sides indent by 1
|
|
164
|
+
// 'v' = sides extend through the full column, top/bottom indent by 1
|
|
165
|
+
// 'none' = corners blank (or filled by dedicated corner glyphs, if available)
|
|
166
|
+
//
|
|
167
|
+
// eighth-cell-outer defaults to 'h' when 'none' is specified — there's no
|
|
168
|
+
// corner glyph in Block Elements for 1/8-thick L pieces, and with no
|
|
169
|
+
// extension the corners would be visibly missing from the outer frame.
|
|
170
|
+
const effectiveCorner = (corner === 'none' && style.borderStyle === 'eighth-cell-outer') ? 'h' : corner;
|
|
171
|
+
const hOwnsCorners = effectiveCorner === 'h';
|
|
172
|
+
const vOwnsCorners = effectiveCorner === 'v';
|
|
173
|
+
const corners = (effectiveCorner === 'none') ? BLOCK_CORNERS[style.borderStyle] : undefined;
|
|
174
|
+
const horizStart = (left && !hOwnsCorners) ? x + 1 : x;
|
|
175
|
+
const horizEnd = (right && !hOwnsCorners) ? x + width - 1 : x + width;
|
|
176
|
+
const vertStart = (top && !vOwnsCorners) ? y + 1 : y;
|
|
177
|
+
const vertEnd = (bottom && !vOwnsCorners) ? y + height - 1 : y + height;
|
|
178
|
+
// Place corner glyphs first so edge-paint loops don't overwrite them.
|
|
179
|
+
if (corners) {
|
|
180
|
+
if (top && left)
|
|
181
|
+
buffer.setCell(x, y, { char: corners.topLeft, fg });
|
|
182
|
+
if (top && right)
|
|
183
|
+
buffer.setCell(x + width - 1, y, { char: corners.topRight, fg });
|
|
184
|
+
if (bottom && left)
|
|
185
|
+
buffer.setCell(x, y + height - 1, { char: corners.bottomLeft, fg });
|
|
186
|
+
if (bottom && right)
|
|
187
|
+
buffer.setCell(x + width - 1, y + height - 1, { char: corners.bottomRight, fg });
|
|
188
|
+
}
|
|
189
|
+
if (top) {
|
|
190
|
+
for (let col = horizStart; col < horizEnd; col++) {
|
|
191
|
+
buffer.setCell(col, y, { char: edges.top, fg });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
if (bottom) {
|
|
195
|
+
for (let col = horizStart; col < horizEnd; col++) {
|
|
196
|
+
buffer.setCell(col, y + height - 1, { char: edges.bottom, fg });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (left) {
|
|
200
|
+
for (let row = vertStart; row < vertEnd; row++) {
|
|
201
|
+
buffer.setCell(x, row, { char: edges.left, fg });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (right) {
|
|
205
|
+
for (let row = vertStart; row < vertEnd; row++) {
|
|
206
|
+
buffer.setCell(x + width - 1, row, { char: edges.right, fg });
|
|
58
207
|
}
|
|
59
208
|
}
|
|
60
209
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const EMPTY_CELL = {
|
|
2
2
|
char: ' ', fg: 'default', bg: 'default',
|
|
3
3
|
bold: false, italic: false, underline: false,
|
|
4
|
-
strikethrough: false, dim: false,
|
|
4
|
+
strikethrough: false, dim: false, inverse: false,
|
|
5
5
|
};
|
|
6
6
|
export class CellBuffer {
|
|
7
7
|
width;
|
|
@@ -37,6 +37,7 @@ export class CellBuffer {
|
|
|
37
37
|
underline: cell.underline ?? existing.underline,
|
|
38
38
|
strikethrough: cell.strikethrough ?? existing.strikethrough,
|
|
39
39
|
dim: cell.dim ?? existing.dim,
|
|
40
|
+
inverse: cell.inverse ?? existing.inverse,
|
|
40
41
|
hyperlink: cell.hyperlink ?? existing.hyperlink,
|
|
41
42
|
};
|
|
42
43
|
}
|
|
@@ -51,6 +52,7 @@ export class CellBuffer {
|
|
|
51
52
|
underline: style?.underline,
|
|
52
53
|
strikethrough: style?.strikethrough,
|
|
53
54
|
dim: style?.dim,
|
|
55
|
+
inverse: style?.inverse,
|
|
54
56
|
});
|
|
55
57
|
}
|
|
56
58
|
}
|
|
@@ -66,5 +68,6 @@ export function cellsEqual(a, b) {
|
|
|
66
68
|
return a.char === b.char && a.fg === b.fg && a.bg === b.bg
|
|
67
69
|
&& a.bold === b.bold && a.italic === b.italic
|
|
68
70
|
&& a.underline === b.underline && a.strikethrough === b.strikethrough
|
|
69
|
-
&& a.dim === b.dim && a.
|
|
71
|
+
&& a.dim === b.dim && a.inverse === b.inverse
|
|
72
|
+
&& a.hyperlink === b.hyperlink;
|
|
70
73
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour quantization for terminals without truecolor: hex → the xterm
|
|
3
|
+
* 256-colour palette, or the nearest of the 16 base colours.
|
|
4
|
+
*/
|
|
5
|
+
/** Map #rrggbb to the nearest xterm 256-palette index (cube or grey ramp). */
|
|
6
|
+
export declare function quantizeTo256(hex: string): number;
|
|
7
|
+
/** Map #rrggbb to the nearest of the 16 base colour names. */
|
|
8
|
+
export declare function quantizeTo16(hex: string): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Colour quantization for terminals without truecolor: hex → the xterm
|
|
3
|
+
* 256-colour palette, or the nearest of the 16 base colours.
|
|
4
|
+
*/
|
|
5
|
+
/** Nominal xterm RGB values for the 16 SGR names. */
|
|
6
|
+
const BASE16 = [
|
|
7
|
+
{ name: 'black', rgb: [0, 0, 0] },
|
|
8
|
+
{ name: 'red', rgb: [205, 0, 0] },
|
|
9
|
+
{ name: 'green', rgb: [0, 205, 0] },
|
|
10
|
+
{ name: 'yellow', rgb: [205, 205, 0] },
|
|
11
|
+
{ name: 'blue', rgb: [0, 0, 238] },
|
|
12
|
+
{ name: 'magenta', rgb: [205, 0, 205] },
|
|
13
|
+
{ name: 'cyan', rgb: [0, 205, 205] },
|
|
14
|
+
{ name: 'white', rgb: [229, 229, 229] },
|
|
15
|
+
];
|
|
16
|
+
function hexToRgb(hex) {
|
|
17
|
+
return [
|
|
18
|
+
parseInt(hex.slice(1, 3), 16),
|
|
19
|
+
parseInt(hex.slice(3, 5), 16),
|
|
20
|
+
parseInt(hex.slice(5, 7), 16),
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
/** The 0–5 colour-cube step nearest a channel value, per xterm's levels. */
|
|
24
|
+
const CUBE_LEVELS = [0, 95, 135, 175, 215, 255];
|
|
25
|
+
function nearestCubeStep(value) {
|
|
26
|
+
let best = 0;
|
|
27
|
+
for (let i = 1; i < CUBE_LEVELS.length; i++) {
|
|
28
|
+
if (Math.abs(CUBE_LEVELS[i] - value) < Math.abs(CUBE_LEVELS[best] - value))
|
|
29
|
+
best = i;
|
|
30
|
+
}
|
|
31
|
+
return best;
|
|
32
|
+
}
|
|
33
|
+
function distance(a, b) {
|
|
34
|
+
return (a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2 + (a[2] - b[2]) ** 2;
|
|
35
|
+
}
|
|
36
|
+
/** Map #rrggbb to the nearest xterm 256-palette index (cube or grey ramp). */
|
|
37
|
+
export function quantizeTo256(hex) {
|
|
38
|
+
const rgb = hexToRgb(hex);
|
|
39
|
+
const [r, g, b] = rgb;
|
|
40
|
+
const steps = [nearestCubeStep(r), nearestCubeStep(g), nearestCubeStep(b)];
|
|
41
|
+
const cubeIndex = 16 + 36 * steps[0] + 6 * steps[1] + steps[2];
|
|
42
|
+
const cubeRgb = [CUBE_LEVELS[steps[0]], CUBE_LEVELS[steps[1]], CUBE_LEVELS[steps[2]]];
|
|
43
|
+
// Grey ramp: 232–255 covering 8..238 in steps of 10
|
|
44
|
+
const grey = Math.round((r + g + b) / 3);
|
|
45
|
+
const greyStep = Math.min(23, Math.max(0, Math.round((grey - 8) / 10)));
|
|
46
|
+
const greyValue = 8 + greyStep * 10;
|
|
47
|
+
const greyRgb = [greyValue, greyValue, greyValue];
|
|
48
|
+
return distance(rgb, greyRgb) < distance(rgb, cubeRgb) ? 232 + greyStep : cubeIndex;
|
|
49
|
+
}
|
|
50
|
+
/** Map #rrggbb to the nearest of the 16 base colour names. */
|
|
51
|
+
export function quantizeTo16(hex) {
|
|
52
|
+
const rgb = hexToRgb(hex);
|
|
53
|
+
let best = BASE16[0];
|
|
54
|
+
for (const candidate of BASE16) {
|
|
55
|
+
if (distance(rgb, candidate.rgb) < distance(rgb, best.rgb))
|
|
56
|
+
best = candidate;
|
|
57
|
+
}
|
|
58
|
+
return best.name;
|
|
59
|
+
}
|
|
@@ -11,6 +11,7 @@ export declare class RenderContext {
|
|
|
11
11
|
onSetText(node: TermNode, newText: string): void;
|
|
12
12
|
onSetAttribute(node: TermNode, key: string, value: string): void;
|
|
13
13
|
onRemoveAttribute(node: TermNode, key: string): void;
|
|
14
|
+
private invalidateStyles;
|
|
14
15
|
onInsert(parent: TermNode, child: TermNode): void;
|
|
15
16
|
onRemove(child: TermNode, parent: TermNode): void;
|
|
16
17
|
onScroll(node: TermNode): void;
|