@svelterm/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 +174 -0
- package/dist/src/components/spinner.d.ts +11 -0
- package/dist/src/components/spinner.js +19 -0
- package/dist/src/components/text-buffer.d.ts +21 -0
- package/dist/src/components/text-buffer.js +87 -0
- package/dist/src/css/animation-runner.d.ts +17 -0
- package/dist/src/css/animation-runner.js +72 -0
- package/dist/src/css/animation.d.ts +5 -0
- package/dist/src/css/animation.js +6 -0
- package/dist/src/css/calc.d.ts +5 -0
- package/dist/src/css/calc.js +130 -0
- package/dist/src/css/color.d.ts +1 -0
- package/dist/src/css/color.js +157 -0
- package/dist/src/css/compute.d.ts +63 -0
- package/dist/src/css/compute.js +606 -0
- package/dist/src/css/defaults.d.ts +8 -0
- package/dist/src/css/defaults.js +44 -0
- package/dist/src/css/incremental.d.ts +9 -0
- package/dist/src/css/incremental.js +46 -0
- package/dist/src/css/index.d.ts +5 -0
- package/dist/src/css/index.js +3 -0
- package/dist/src/css/media.d.ts +11 -0
- package/dist/src/css/media.js +59 -0
- package/dist/src/css/parser.d.ts +20 -0
- package/dist/src/css/parser.js +241 -0
- package/dist/src/css/selector.d.ts +17 -0
- package/dist/src/css/selector.js +272 -0
- package/dist/src/css/specificity.d.ts +7 -0
- package/dist/src/css/specificity.js +89 -0
- package/dist/src/css/values.d.ts +17 -0
- package/dist/src/css/values.js +58 -0
- package/dist/src/css/variables.d.ts +6 -0
- package/dist/src/css/variables.js +42 -0
- package/dist/src/debug/console.d.ts +16 -0
- package/dist/src/debug/console.js +65 -0
- package/dist/src/debug/server.d.ts +22 -0
- package/dist/src/debug/server.js +90 -0
- package/dist/src/headless.d.ts +21 -0
- package/dist/src/headless.js +26 -0
- package/dist/src/index.d.ts +18 -0
- package/dist/src/index.js +485 -0
- package/dist/src/input/dispatch.d.ts +18 -0
- package/dist/src/input/dispatch.js +70 -0
- package/dist/src/input/focus.d.ts +18 -0
- package/dist/src/input/focus.js +81 -0
- package/dist/src/input/hit.d.ts +3 -0
- package/dist/src/input/hit.js +29 -0
- package/dist/src/input/keyboard.d.ts +9 -0
- package/dist/src/input/keyboard.js +100 -0
- package/dist/src/input/mouse.d.ts +7 -0
- package/dist/src/input/mouse.js +35 -0
- package/dist/src/input/scroll.d.ts +2 -0
- package/dist/src/input/scroll.js +24 -0
- package/dist/src/layout/cache.d.ts +4 -0
- package/dist/src/layout/cache.js +8 -0
- package/dist/src/layout/engine.d.ts +9 -0
- package/dist/src/layout/engine.js +455 -0
- package/dist/src/layout/flex.d.ts +4 -0
- package/dist/src/layout/flex.js +30 -0
- package/dist/src/layout/incremental.d.ts +8 -0
- package/dist/src/layout/incremental.js +58 -0
- package/dist/src/layout/size.d.ts +2 -0
- package/dist/src/layout/size.js +25 -0
- package/dist/src/layout/text.d.ts +7 -0
- package/dist/src/layout/text.js +52 -0
- package/dist/src/render/ansi.d.ts +23 -0
- package/dist/src/render/ansi.js +108 -0
- package/dist/src/render/border.d.ts +4 -0
- package/dist/src/render/border.js +60 -0
- package/dist/src/render/buffer.d.ts +23 -0
- package/dist/src/render/buffer.js +70 -0
- package/dist/src/render/context.d.ts +19 -0
- package/dist/src/render/context.js +98 -0
- package/dist/src/render/diff.d.ts +2 -0
- package/dist/src/render/diff.js +53 -0
- package/dist/src/render/incremental-paint.d.ts +10 -0
- package/dist/src/render/incremental-paint.js +94 -0
- package/dist/src/render/paint-text.d.ts +29 -0
- package/dist/src/render/paint-text.js +120 -0
- package/dist/src/render/paint.d.ts +5 -0
- package/dist/src/render/paint.js +220 -0
- package/dist/src/render/queue.d.ts +24 -0
- package/dist/src/render/queue.js +54 -0
- package/dist/src/render/scrollbar.d.ts +3 -0
- package/dist/src/render/scrollbar.js +19 -0
- package/dist/src/render/snapshot.d.ts +18 -0
- package/dist/src/render/snapshot.js +126 -0
- package/dist/src/renderer/default.d.ts +3 -0
- package/dist/src/renderer/default.js +3 -0
- package/dist/src/renderer/index.d.ts +11 -0
- package/dist/src/renderer/index.js +116 -0
- package/dist/src/renderer/node.d.ts +44 -0
- package/dist/src/renderer/node.js +153 -0
- package/dist/src/terminal/screen.d.ts +10 -0
- package/dist/src/terminal/screen.js +31 -0
- package/dist/src/terminal/stdin-router.d.ts +31 -0
- package/dist/src/terminal/stdin-router.js +133 -0
- package/package.json +64 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function hitTest(root, layout, col, row) {
|
|
2
|
+
return hitTestNode(root, layout, col, row, 0, 0);
|
|
3
|
+
}
|
|
4
|
+
function hitTestNode(node, layout, col, row, scrollX, scrollY) {
|
|
5
|
+
if (node.nodeType !== 'element')
|
|
6
|
+
return null;
|
|
7
|
+
const box = layout.get(node.id);
|
|
8
|
+
if (!box)
|
|
9
|
+
return null;
|
|
10
|
+
// Apply accumulated scroll offset to convert screen coords to layout coords
|
|
11
|
+
const layoutCol = col + scrollX;
|
|
12
|
+
const layoutRow = row + scrollY;
|
|
13
|
+
if (!isInBox(layoutCol, layoutRow, box))
|
|
14
|
+
return null;
|
|
15
|
+
// Accumulate this node's scroll offset for children
|
|
16
|
+
const childScrollX = scrollX + node.scrollLeft;
|
|
17
|
+
const childScrollY = scrollY + node.scrollTop;
|
|
18
|
+
// Check children deepest-first (last child = highest z)
|
|
19
|
+
for (let i = node.children.length - 1; i >= 0; i--) {
|
|
20
|
+
const hit = hitTestNode(node.children[i], layout, col, row, childScrollX, childScrollY);
|
|
21
|
+
if (hit)
|
|
22
|
+
return hit;
|
|
23
|
+
}
|
|
24
|
+
return node;
|
|
25
|
+
}
|
|
26
|
+
function isInBox(col, row, box) {
|
|
27
|
+
return col >= box.x && col < box.x + box.width
|
|
28
|
+
&& row >= box.y && row < box.y + box.height;
|
|
29
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface KeyEvent {
|
|
2
|
+
key: string;
|
|
3
|
+
ctrl: boolean;
|
|
4
|
+
shift: boolean;
|
|
5
|
+
meta: boolean;
|
|
6
|
+
}
|
|
7
|
+
/** Check if data contains a bracketed paste sequence. Returns the pasted text or null. */
|
|
8
|
+
export declare function parsePaste(data: Buffer): string | null;
|
|
9
|
+
export declare function parseKeyEvent(data: Buffer): KeyEvent | null;
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const PASTE_START = '\x1b[200~';
|
|
2
|
+
const PASTE_END = '\x1b[201~';
|
|
3
|
+
/** Check if data contains a bracketed paste sequence. Returns the pasted text or null. */
|
|
4
|
+
export function parsePaste(data) {
|
|
5
|
+
const str = data.toString();
|
|
6
|
+
if (str.startsWith(PASTE_START)) {
|
|
7
|
+
const endIdx = str.indexOf(PASTE_END);
|
|
8
|
+
if (endIdx !== -1) {
|
|
9
|
+
return str.substring(PASTE_START.length, endIdx);
|
|
10
|
+
}
|
|
11
|
+
// Paste without end marker — take everything after start
|
|
12
|
+
return str.substring(PASTE_START.length);
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
export function parseKeyEvent(data) {
|
|
17
|
+
if (data.length === 0)
|
|
18
|
+
return null;
|
|
19
|
+
const byte = data[0];
|
|
20
|
+
// Ctrl+key (0x01-0x1a, except Tab/Enter/Escape)
|
|
21
|
+
if (byte >= 0x01 && byte <= 0x1a) {
|
|
22
|
+
if (byte === 0x09)
|
|
23
|
+
return { key: 'Tab', ctrl: false, shift: false, meta: false };
|
|
24
|
+
if (byte === 0x0d)
|
|
25
|
+
return { key: 'Enter', ctrl: false, shift: false, meta: false };
|
|
26
|
+
if (byte === 0x1b)
|
|
27
|
+
return parseEscapeSequence(data);
|
|
28
|
+
const letter = String.fromCharCode(byte + 0x60); // 0x01 -> 'a'
|
|
29
|
+
return { key: letter, ctrl: true, shift: false, meta: false };
|
|
30
|
+
}
|
|
31
|
+
// Backspace
|
|
32
|
+
if (byte === 0x7f)
|
|
33
|
+
return { key: 'Backspace', ctrl: false, shift: false, meta: false };
|
|
34
|
+
// Escape sequence
|
|
35
|
+
if (byte === 0x1b)
|
|
36
|
+
return parseEscapeSequence(data);
|
|
37
|
+
// Regular printable character
|
|
38
|
+
if (byte >= 0x20 && byte <= 0x7e) {
|
|
39
|
+
return { key: String.fromCharCode(byte), ctrl: false, shift: false, meta: false };
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function parseEscapeSequence(data) {
|
|
44
|
+
// Bare escape
|
|
45
|
+
if (data.length === 1) {
|
|
46
|
+
return { key: 'Escape', ctrl: false, shift: false, meta: false };
|
|
47
|
+
}
|
|
48
|
+
// CSI sequences: ESC [
|
|
49
|
+
if (data[1] === 0x5b) {
|
|
50
|
+
return parseCSI(data);
|
|
51
|
+
}
|
|
52
|
+
return { key: 'Escape', ctrl: false, shift: false, meta: false };
|
|
53
|
+
}
|
|
54
|
+
function parseCSI(data) {
|
|
55
|
+
const base = { ctrl: false, shift: false, meta: false };
|
|
56
|
+
if (data.length < 3)
|
|
57
|
+
return { key: 'Escape', ...base };
|
|
58
|
+
const third = data[2];
|
|
59
|
+
// Arrow keys: ESC [ A/B/C/D
|
|
60
|
+
switch (third) {
|
|
61
|
+
case 0x41: return { key: 'ArrowUp', ...base };
|
|
62
|
+
case 0x42: return { key: 'ArrowDown', ...base };
|
|
63
|
+
case 0x43: return { key: 'ArrowRight', ...base };
|
|
64
|
+
case 0x44: return { key: 'ArrowLeft', ...base };
|
|
65
|
+
case 0x48: return { key: 'Home', ...base };
|
|
66
|
+
case 0x46: return { key: 'End', ...base };
|
|
67
|
+
case 0x5a: return { key: 'Tab', ctrl: false, shift: true, meta: false }; // Shift+Tab (Back Tab)
|
|
68
|
+
}
|
|
69
|
+
// Extended keys: ESC [ N ~
|
|
70
|
+
if (data.length >= 4 && data[3] === 0x7e) {
|
|
71
|
+
switch (third) {
|
|
72
|
+
case 0x32: return { key: 'Insert', ...base };
|
|
73
|
+
case 0x33: return { key: 'Delete', ...base };
|
|
74
|
+
case 0x35: return { key: 'PageUp', ...base };
|
|
75
|
+
case 0x36: return { key: 'PageDown', ...base };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Modified keys: ESC [ 1 ; modifier X (e.g. Shift+Arrow)
|
|
79
|
+
if (data.length >= 6 && third === 0x31 && data[3] === 0x3b) {
|
|
80
|
+
const modifier = data[4] - 0x30;
|
|
81
|
+
const keyCode = data[5];
|
|
82
|
+
const mods = parseModifier(modifier);
|
|
83
|
+
const keyName = CSI_KEYS[keyCode];
|
|
84
|
+
if (keyName)
|
|
85
|
+
return { key: keyName, ...mods };
|
|
86
|
+
}
|
|
87
|
+
return { key: 'Escape', ...base };
|
|
88
|
+
}
|
|
89
|
+
const CSI_KEYS = {
|
|
90
|
+
0x41: 'ArrowUp', 0x42: 'ArrowDown', 0x43: 'ArrowRight', 0x44: 'ArrowLeft',
|
|
91
|
+
0x48: 'Home', 0x46: 'End',
|
|
92
|
+
};
|
|
93
|
+
function parseModifier(mod) {
|
|
94
|
+
// CSI modifier values: 1=none, 2=Shift, 3=Alt, 4=Shift+Alt, 5=Ctrl, 6=Shift+Ctrl, 7=Alt+Ctrl, 8=Shift+Alt+Ctrl
|
|
95
|
+
return {
|
|
96
|
+
shift: (mod & 1) !== 0,
|
|
97
|
+
meta: (mod & 2) !== 0,
|
|
98
|
+
ctrl: (mod & 4) !== 0,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const SGR_MOUSE_RE = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])/;
|
|
2
|
+
export function parseMouseEvent(data) {
|
|
3
|
+
const str = data.toString();
|
|
4
|
+
const match = SGR_MOUSE_RE.exec(str);
|
|
5
|
+
if (!match)
|
|
6
|
+
return null;
|
|
7
|
+
const code = parseInt(match[1]);
|
|
8
|
+
const col = parseInt(match[2]) - 1;
|
|
9
|
+
const row = parseInt(match[3]) - 1;
|
|
10
|
+
// Strip modifier bits (shift=4, meta=8, ctrl=16)
|
|
11
|
+
const base = code & ~(4 | 8 | 16);
|
|
12
|
+
// Release
|
|
13
|
+
if (match[4] === 'm') {
|
|
14
|
+
return { button: pressButton(base & 3), type: 'release', col, row };
|
|
15
|
+
}
|
|
16
|
+
// Scroll: codes 64-67
|
|
17
|
+
if (base >= 64 && base <= 67) {
|
|
18
|
+
return { button: base === 64 ? 'scrollUp' : 'scrollDown', type: 'scroll', col, row };
|
|
19
|
+
}
|
|
20
|
+
// Motion: bit 5 (32)
|
|
21
|
+
if (base & 32) {
|
|
22
|
+
const held = base & ~32 & 3;
|
|
23
|
+
const button = held === 3 ? 'none' : pressButton(held);
|
|
24
|
+
return { button, type: 'motion', col, row };
|
|
25
|
+
}
|
|
26
|
+
// Press
|
|
27
|
+
return { button: pressButton(base & 3), type: 'press', col, row };
|
|
28
|
+
}
|
|
29
|
+
function pressButton(code) {
|
|
30
|
+
if (code === 1)
|
|
31
|
+
return 'middle';
|
|
32
|
+
if (code === 2)
|
|
33
|
+
return 'right';
|
|
34
|
+
return 'left';
|
|
35
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function applyScrollInput(node, key, contentHeight, viewportHeight, contentWidth, viewportWidth) {
|
|
2
|
+
const maxScrollY = Math.max(0, contentHeight - viewportHeight);
|
|
3
|
+
const maxScrollX = Math.max(0, (contentWidth ?? 0) - (viewportWidth ?? 0));
|
|
4
|
+
switch (key) {
|
|
5
|
+
case 'ArrowDown':
|
|
6
|
+
node.scrollTop = Math.min(node.scrollTop + 1, maxScrollY);
|
|
7
|
+
break;
|
|
8
|
+
case 'ArrowUp':
|
|
9
|
+
node.scrollTop = Math.max(node.scrollTop - 1, 0);
|
|
10
|
+
break;
|
|
11
|
+
case 'ArrowRight':
|
|
12
|
+
node.scrollLeft = Math.min(node.scrollLeft + 1, maxScrollX);
|
|
13
|
+
break;
|
|
14
|
+
case 'ArrowLeft':
|
|
15
|
+
node.scrollLeft = Math.max(node.scrollLeft - 1, 0);
|
|
16
|
+
break;
|
|
17
|
+
case 'PageDown':
|
|
18
|
+
node.scrollTop = Math.min(node.scrollTop + viewportHeight, maxScrollY);
|
|
19
|
+
break;
|
|
20
|
+
case 'PageUp':
|
|
21
|
+
node.scrollTop = Math.max(node.scrollTop - viewportHeight, 0);
|
|
22
|
+
break;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** Sync layout boxes from the computed map into each node's cache. */
|
|
2
|
+
export function syncLayoutCache(node, layout) {
|
|
3
|
+
const box = layout.get(node.id);
|
|
4
|
+
node.cache.layoutBox = box ?? null;
|
|
5
|
+
for (const child of node.children) {
|
|
6
|
+
syncLayoutCache(child, layout);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TermNode } from '../renderer/node.js';
|
|
2
|
+
import { ResolvedStyle } from '../css/compute.js';
|
|
3
|
+
export interface LayoutBox {
|
|
4
|
+
x: number;
|
|
5
|
+
y: number;
|
|
6
|
+
width: number;
|
|
7
|
+
height: number;
|
|
8
|
+
}
|
|
9
|
+
export declare function computeLayout(root: TermNode, styles: Map<number, ResolvedStyle>, availWidth: number, availHeight: number): Map<number, LayoutBox>;
|