@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.
Files changed (99) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +174 -0
  3. package/dist/src/components/spinner.d.ts +11 -0
  4. package/dist/src/components/spinner.js +19 -0
  5. package/dist/src/components/text-buffer.d.ts +21 -0
  6. package/dist/src/components/text-buffer.js +87 -0
  7. package/dist/src/css/animation-runner.d.ts +17 -0
  8. package/dist/src/css/animation-runner.js +72 -0
  9. package/dist/src/css/animation.d.ts +5 -0
  10. package/dist/src/css/animation.js +6 -0
  11. package/dist/src/css/calc.d.ts +5 -0
  12. package/dist/src/css/calc.js +130 -0
  13. package/dist/src/css/color.d.ts +1 -0
  14. package/dist/src/css/color.js +157 -0
  15. package/dist/src/css/compute.d.ts +63 -0
  16. package/dist/src/css/compute.js +606 -0
  17. package/dist/src/css/defaults.d.ts +8 -0
  18. package/dist/src/css/defaults.js +44 -0
  19. package/dist/src/css/incremental.d.ts +9 -0
  20. package/dist/src/css/incremental.js +46 -0
  21. package/dist/src/css/index.d.ts +5 -0
  22. package/dist/src/css/index.js +3 -0
  23. package/dist/src/css/media.d.ts +11 -0
  24. package/dist/src/css/media.js +59 -0
  25. package/dist/src/css/parser.d.ts +20 -0
  26. package/dist/src/css/parser.js +241 -0
  27. package/dist/src/css/selector.d.ts +17 -0
  28. package/dist/src/css/selector.js +272 -0
  29. package/dist/src/css/specificity.d.ts +7 -0
  30. package/dist/src/css/specificity.js +89 -0
  31. package/dist/src/css/values.d.ts +17 -0
  32. package/dist/src/css/values.js +58 -0
  33. package/dist/src/css/variables.d.ts +6 -0
  34. package/dist/src/css/variables.js +42 -0
  35. package/dist/src/debug/console.d.ts +16 -0
  36. package/dist/src/debug/console.js +65 -0
  37. package/dist/src/debug/server.d.ts +22 -0
  38. package/dist/src/debug/server.js +90 -0
  39. package/dist/src/headless.d.ts +21 -0
  40. package/dist/src/headless.js +26 -0
  41. package/dist/src/index.d.ts +18 -0
  42. package/dist/src/index.js +485 -0
  43. package/dist/src/input/dispatch.d.ts +18 -0
  44. package/dist/src/input/dispatch.js +70 -0
  45. package/dist/src/input/focus.d.ts +18 -0
  46. package/dist/src/input/focus.js +81 -0
  47. package/dist/src/input/hit.d.ts +3 -0
  48. package/dist/src/input/hit.js +29 -0
  49. package/dist/src/input/keyboard.d.ts +9 -0
  50. package/dist/src/input/keyboard.js +100 -0
  51. package/dist/src/input/mouse.d.ts +7 -0
  52. package/dist/src/input/mouse.js +35 -0
  53. package/dist/src/input/scroll.d.ts +2 -0
  54. package/dist/src/input/scroll.js +24 -0
  55. package/dist/src/layout/cache.d.ts +4 -0
  56. package/dist/src/layout/cache.js +8 -0
  57. package/dist/src/layout/engine.d.ts +9 -0
  58. package/dist/src/layout/engine.js +455 -0
  59. package/dist/src/layout/flex.d.ts +4 -0
  60. package/dist/src/layout/flex.js +30 -0
  61. package/dist/src/layout/incremental.d.ts +8 -0
  62. package/dist/src/layout/incremental.js +58 -0
  63. package/dist/src/layout/size.d.ts +2 -0
  64. package/dist/src/layout/size.js +25 -0
  65. package/dist/src/layout/text.d.ts +7 -0
  66. package/dist/src/layout/text.js +52 -0
  67. package/dist/src/render/ansi.d.ts +23 -0
  68. package/dist/src/render/ansi.js +108 -0
  69. package/dist/src/render/border.d.ts +4 -0
  70. package/dist/src/render/border.js +60 -0
  71. package/dist/src/render/buffer.d.ts +23 -0
  72. package/dist/src/render/buffer.js +70 -0
  73. package/dist/src/render/context.d.ts +19 -0
  74. package/dist/src/render/context.js +98 -0
  75. package/dist/src/render/diff.d.ts +2 -0
  76. package/dist/src/render/diff.js +53 -0
  77. package/dist/src/render/incremental-paint.d.ts +10 -0
  78. package/dist/src/render/incremental-paint.js +94 -0
  79. package/dist/src/render/paint-text.d.ts +29 -0
  80. package/dist/src/render/paint-text.js +120 -0
  81. package/dist/src/render/paint.d.ts +5 -0
  82. package/dist/src/render/paint.js +220 -0
  83. package/dist/src/render/queue.d.ts +24 -0
  84. package/dist/src/render/queue.js +54 -0
  85. package/dist/src/render/scrollbar.d.ts +3 -0
  86. package/dist/src/render/scrollbar.js +19 -0
  87. package/dist/src/render/snapshot.d.ts +18 -0
  88. package/dist/src/render/snapshot.js +126 -0
  89. package/dist/src/renderer/default.d.ts +3 -0
  90. package/dist/src/renderer/default.js +3 -0
  91. package/dist/src/renderer/index.d.ts +11 -0
  92. package/dist/src/renderer/index.js +116 -0
  93. package/dist/src/renderer/node.d.ts +44 -0
  94. package/dist/src/renderer/node.js +153 -0
  95. package/dist/src/terminal/screen.d.ts +10 -0
  96. package/dist/src/terminal/screen.js +31 -0
  97. package/dist/src/terminal/stdin-router.d.ts +31 -0
  98. package/dist/src/terminal/stdin-router.js +133 -0
  99. 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,7 @@
1
+ export interface MouseEvent {
2
+ button: 'left' | 'right' | 'middle' | 'scrollUp' | 'scrollDown' | 'none';
3
+ type: 'press' | 'release' | 'motion' | 'scroll';
4
+ col: number;
5
+ row: number;
6
+ }
7
+ export declare function parseMouseEvent(data: Buffer): MouseEvent | null;
@@ -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,2 @@
1
+ import { TermNode } from '../renderer/node.js';
2
+ export declare function applyScrollInput(node: TermNode, key: string, contentHeight: number, viewportHeight: number, contentWidth?: number, viewportWidth?: number): void;
@@ -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,4 @@
1
+ import { TermNode } from '../renderer/node.js';
2
+ import type { LayoutBox } from './engine.js';
3
+ /** Sync layout boxes from the computed map into each node's cache. */
4
+ export declare function syncLayoutCache(node: TermNode, layout: Map<number, LayoutBox>): void;
@@ -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>;