@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,89 @@
1
+ /**
2
+ * Compute CSS specificity as [id, class, element] tuple.
3
+ * Higher tuple wins. Equal specificity: later rule wins.
4
+ */
5
+ export function computeSpecificity(selector) {
6
+ let ids = 0;
7
+ let classes = 0;
8
+ let elements = 0;
9
+ let pos = 0;
10
+ while (pos < selector.length) {
11
+ const ch = selector[pos];
12
+ if (ch === '*') {
13
+ pos++; // universal: 0 specificity
14
+ }
15
+ else if (ch === '#') {
16
+ ids++;
17
+ pos++;
18
+ pos = skipName(selector, pos);
19
+ }
20
+ else if (ch === '.') {
21
+ classes++;
22
+ pos++;
23
+ pos = skipName(selector, pos);
24
+ }
25
+ else if (ch === '[') {
26
+ classes++; // attribute selector = class-level
27
+ pos = skipUntil(selector, pos, ']') + 1;
28
+ }
29
+ else if (ch === ':') {
30
+ pos++;
31
+ const nameStart = pos;
32
+ pos = skipName(selector, pos);
33
+ const name = selector.substring(nameStart, pos);
34
+ if (name === 'not' && pos < selector.length && selector[pos] === '(') {
35
+ // :not() specificity = specificity of its argument
36
+ pos++;
37
+ const argStart = pos;
38
+ let depth = 1;
39
+ while (pos < selector.length && depth > 0) {
40
+ if (selector[pos] === '(')
41
+ depth++;
42
+ else if (selector[pos] === ')')
43
+ depth--;
44
+ if (depth > 0)
45
+ pos++;
46
+ }
47
+ const arg = selector.substring(argStart, pos);
48
+ pos++; // skip )
49
+ const argSpec = computeSpecificity(arg);
50
+ ids += argSpec[0];
51
+ classes += argSpec[1];
52
+ elements += argSpec[2];
53
+ }
54
+ else if (pos < selector.length && selector[pos] === '(') {
55
+ classes++; // other functional pseudo-class
56
+ pos = skipUntil(selector, pos, ')') + 1;
57
+ }
58
+ else {
59
+ classes++; // simple pseudo-class
60
+ }
61
+ }
62
+ else if (/[a-zA-Z]/.test(ch)) {
63
+ elements++;
64
+ pos = skipName(selector, pos);
65
+ }
66
+ else {
67
+ pos++; // whitespace, combinators
68
+ }
69
+ }
70
+ return [ids, classes, elements];
71
+ }
72
+ function skipName(selector, pos) {
73
+ while (pos < selector.length && /[a-zA-Z0-9_-]/.test(selector[pos]))
74
+ pos++;
75
+ return pos;
76
+ }
77
+ function skipUntil(selector, pos, char) {
78
+ while (pos < selector.length && selector[pos] !== char)
79
+ pos++;
80
+ return pos;
81
+ }
82
+ /** Compare two specificity tuples. Returns positive if a wins, negative if b wins, 0 if equal. */
83
+ export function compareSpecificity(a, b) {
84
+ if (a[0] !== b[0])
85
+ return a[0] - b[0];
86
+ if (a[1] !== b[1])
87
+ return a[1] - b[1];
88
+ return a[2] - b[2];
89
+ }
@@ -0,0 +1,17 @@
1
+ import type { ResolvedStyle } from './compute.js';
2
+ /**
3
+ * Parse a cell value from CSS. Accepts:
4
+ * - `5cell` → 5
5
+ * - `0` → 0 (unitless zero is valid CSS)
6
+ * - Returns 0 for unrecognised values (browser-only units like px, em, rem)
7
+ */
8
+ export declare function parseCellValue(value: string): number;
9
+ export declare function parseSizeValue(value: string): number | string | null;
10
+ export declare function parseJustify(value: string): ResolvedStyle['justifyContent'];
11
+ export declare function parseAlign(value: string): ResolvedStyle['alignItems'];
12
+ export declare function parsePadding(value: string): {
13
+ top: number;
14
+ right: number;
15
+ bottom: number;
16
+ left: number;
17
+ };
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Parse a cell value from CSS. Accepts:
3
+ * - `5cell` → 5
4
+ * - `0` → 0 (unitless zero is valid CSS)
5
+ * - Returns 0 for unrecognised values (browser-only units like px, em, rem)
6
+ */
7
+ export function parseCellValue(value) {
8
+ if (value === '0')
9
+ return 0;
10
+ if (value.endsWith('cell')) {
11
+ const num = parseFloat(value);
12
+ return isNaN(num) ? 0 : Math.round(num);
13
+ }
14
+ return 0;
15
+ }
16
+ export function parseSizeValue(value) {
17
+ if (value === 'auto')
18
+ return null;
19
+ if (value.endsWith('%'))
20
+ return value;
21
+ // Preserve calc/min/max/clamp expressions as strings for layout-time evaluation
22
+ if (value.startsWith('calc(') || value.startsWith('min(') || value.startsWith('max(') || value.startsWith('clamp(')) {
23
+ return value;
24
+ }
25
+ return parseCellValue(value);
26
+ }
27
+ export function parseJustify(value) {
28
+ const map = {
29
+ 'flex-start': 'start', 'start': 'start',
30
+ 'flex-end': 'end', 'end': 'end',
31
+ 'center': 'center',
32
+ 'space-between': 'space-between',
33
+ 'space-around': 'space-around',
34
+ 'space-evenly': 'space-evenly',
35
+ };
36
+ return map[value] ?? 'start';
37
+ }
38
+ export function parseAlign(value) {
39
+ const map = {
40
+ 'flex-start': 'start', 'start': 'start',
41
+ 'flex-end': 'end', 'end': 'end',
42
+ 'center': 'center', 'stretch': 'stretch',
43
+ };
44
+ return map[value] ?? 'start';
45
+ }
46
+ export function parsePadding(value) {
47
+ const parts = value.split(/\s+/).map(parseCellValue);
48
+ if (parts.length === 1) {
49
+ return { top: parts[0], right: parts[0], bottom: parts[0], left: parts[0] };
50
+ }
51
+ if (parts.length === 2) {
52
+ return { top: parts[0], right: parts[1], bottom: parts[0], left: parts[1] };
53
+ }
54
+ if (parts.length === 3) {
55
+ return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[1] };
56
+ }
57
+ return { top: parts[0], right: parts[1], bottom: parts[2], left: parts[3] };
58
+ }
@@ -0,0 +1,6 @@
1
+ import { TermNode } from '../renderer/node.js';
2
+ import { CSSStyleSheet } from './parser.js';
3
+ /** Collect custom properties for each element, with inheritance from ancestors. */
4
+ export declare function collectVariables(root: TermNode, stylesheet: CSSStyleSheet): Map<number, Map<string, string>>;
5
+ /** Resolve var() references in a CSS value. */
6
+ export declare function resolveVar(value: string, vars: Map<string, string>): string;
@@ -0,0 +1,42 @@
1
+ import { matchesSelector } from './selector.js';
2
+ /** Collect custom properties for each element, with inheritance from ancestors. */
3
+ export function collectVariables(root, stylesheet) {
4
+ const result = new Map();
5
+ collectNode(root, stylesheet, result, new Map());
6
+ return result;
7
+ }
8
+ function collectNode(node, stylesheet, result, inherited) {
9
+ if (node.nodeType !== 'element')
10
+ return;
11
+ // Start with inherited variables
12
+ const vars = new Map(inherited);
13
+ // Add variables from matching rules
14
+ for (const rule of stylesheet.rules) {
15
+ if (!rule.selectors.some(sel => matchesSelector(node, sel)))
16
+ continue;
17
+ for (const decl of rule.declarations) {
18
+ if (decl.property.startsWith('--')) {
19
+ vars.set(decl.property, decl.value);
20
+ }
21
+ }
22
+ }
23
+ result.set(node.id, vars);
24
+ for (const child of node.children) {
25
+ collectNode(child, stylesheet, result, vars);
26
+ }
27
+ }
28
+ const VAR_RE = /var\(\s*(--[a-zA-Z0-9_-]+)\s*(?:,\s*([^)]+))?\)/;
29
+ /** Resolve var() references in a CSS value. */
30
+ export function resolveVar(value, vars) {
31
+ let resolved = value;
32
+ let match;
33
+ // Resolve iteratively (handles nested vars if needed)
34
+ let limit = 10;
35
+ while ((match = VAR_RE.exec(resolved)) && limit-- > 0) {
36
+ const varName = match[1];
37
+ const fallback = match[2]?.trim();
38
+ const replacement = vars.get(varName) ?? fallback ?? '';
39
+ resolved = resolved.substring(0, match.index) + replacement + resolved.substring(match.index + match[0].length);
40
+ }
41
+ return resolved;
42
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Console domain — intercepts console.log/warn/error/info and forwards
3
+ * to debug clients as events. Also captures uncaught errors.
4
+ */
5
+ import { DebugServer, type DebugDomain } from './server.js';
6
+ export declare class ConsoleDomain implements DebugDomain {
7
+ private server;
8
+ private buffer;
9
+ private maxBuffer;
10
+ private originals;
11
+ constructor(server: DebugServer);
12
+ start(): void;
13
+ stop(): void;
14
+ handle(method: string, params: Record<string, any>): any;
15
+ private capture;
16
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Console domain — intercepts console.log/warn/error/info and forwards
3
+ * to debug clients as events. Also captures uncaught errors.
4
+ */
5
+ export class ConsoleDomain {
6
+ server;
7
+ buffer = [];
8
+ maxBuffer = 1000;
9
+ originals;
10
+ constructor(server) {
11
+ this.server = server;
12
+ // Save originals before patching
13
+ this.originals = {
14
+ log: console.log.bind(console),
15
+ warn: console.warn.bind(console),
16
+ error: console.error.bind(console),
17
+ info: console.info.bind(console),
18
+ debug: console.debug.bind(console),
19
+ };
20
+ }
21
+ start() {
22
+ const levels = ['log', 'warn', 'error', 'info', 'debug'];
23
+ for (const level of levels) {
24
+ const original = this.originals[level];
25
+ console[level] = (...args) => {
26
+ this.capture(level, args);
27
+ };
28
+ }
29
+ // Capture uncaught errors
30
+ process.on('uncaughtException', (err) => {
31
+ this.capture('error', [`Uncaught: ${err.stack ?? err.message}`]);
32
+ });
33
+ }
34
+ stop() {
35
+ // Restore originals
36
+ console.log = this.originals.log;
37
+ console.warn = this.originals.warn;
38
+ console.error = this.originals.error;
39
+ console.info = this.originals.info;
40
+ console.debug = this.originals.debug;
41
+ }
42
+ handle(method, params) {
43
+ switch (method) {
44
+ case 'getEntries':
45
+ return { entries: this.buffer.slice(-(params.count ?? 100)) };
46
+ case 'clear':
47
+ this.buffer = [];
48
+ return {};
49
+ default:
50
+ throw new Error(`Console.${method} not implemented`);
51
+ }
52
+ }
53
+ capture(level, args) {
54
+ const entry = {
55
+ level,
56
+ args: args.map(a => typeof a === 'string' ? a : JSON.stringify(a, null, 2) ?? String(a)),
57
+ timestamp: Date.now(),
58
+ };
59
+ this.buffer.push(entry);
60
+ if (this.buffer.length > this.maxBuffer) {
61
+ this.buffer.shift();
62
+ }
63
+ this.server.emit('Console.messageAdded', { entry });
64
+ }
65
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Svelterm Debug Protocol server.
3
+ * WebSocket server on localhost using the ws package.
4
+ * Only active when explicitly opted in via mount({ debug: true }).
5
+ */
6
+ export interface DebugDomain {
7
+ handle(method: string, params: Record<string, any>): any;
8
+ }
9
+ export declare class DebugServer {
10
+ private wss;
11
+ private clients;
12
+ private domains;
13
+ private port;
14
+ constructor(port?: number);
15
+ registerDomain(name: string, domain: DebugDomain): void;
16
+ start(): Promise<void>;
17
+ get actualPort(): number;
18
+ stop(): void;
19
+ emit(method: string, params: Record<string, any>): void;
20
+ get connected(): number;
21
+ private handleMessage;
22
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Svelterm Debug Protocol server.
3
+ * WebSocket server on localhost using the ws package.
4
+ * Only active when explicitly opted in via mount({ debug: true }).
5
+ */
6
+ import { WebSocketServer, WebSocket } from 'ws';
7
+ export class DebugServer {
8
+ wss = null;
9
+ clients = new Set();
10
+ domains = new Map();
11
+ port;
12
+ constructor(port = 9444) {
13
+ this.port = port;
14
+ }
15
+ registerDomain(name, domain) {
16
+ this.domains.set(name, domain);
17
+ }
18
+ start() {
19
+ return new Promise((resolve) => {
20
+ this.wss = new WebSocketServer({ host: '127.0.0.1', port: this.port }, () => {
21
+ const addr = this.wss?.address();
22
+ if (addr && typeof addr !== 'string')
23
+ this.port = addr.port;
24
+ resolve();
25
+ });
26
+ this.wss.on('connection', (ws) => {
27
+ this.clients.add(ws);
28
+ ws.on('message', (data) => {
29
+ try {
30
+ const msg = JSON.parse(data.toString());
31
+ this.handleMessage(ws, msg);
32
+ }
33
+ catch {
34
+ // Malformed — ignore
35
+ }
36
+ });
37
+ ws.on('close', () => { this.clients.delete(ws); });
38
+ ws.on('error', () => { this.clients.delete(ws); });
39
+ });
40
+ this.wss.on('error', (err) => {
41
+ console.error(`[debug] server error: ${err.message}`);
42
+ resolve(); // don't block — debug is best-effort
43
+ });
44
+ });
45
+ }
46
+ get actualPort() {
47
+ return this.port;
48
+ }
49
+ stop() {
50
+ for (const client of this.clients)
51
+ client.close();
52
+ this.clients.clear();
53
+ this.wss?.close();
54
+ this.wss = null;
55
+ }
56
+ emit(method, params) {
57
+ if (this.clients.size === 0)
58
+ return;
59
+ const msg = JSON.stringify({ method, params });
60
+ for (const client of this.clients) {
61
+ if (client.readyState === WebSocket.OPEN) {
62
+ client.send(msg);
63
+ }
64
+ }
65
+ }
66
+ get connected() {
67
+ return this.clients.size;
68
+ }
69
+ handleMessage(ws, msg) {
70
+ const dotIdx = msg.method.indexOf('.');
71
+ if (dotIdx === -1) {
72
+ ws.send(JSON.stringify({ id: msg.id, error: { message: `Invalid method: ${msg.method}` } }));
73
+ return;
74
+ }
75
+ const domainName = msg.method.substring(0, dotIdx);
76
+ const methodName = msg.method.substring(dotIdx + 1);
77
+ const domain = this.domains.get(domainName);
78
+ if (!domain) {
79
+ ws.send(JSON.stringify({ id: msg.id, error: { message: `Unknown domain: ${domainName}` } }));
80
+ return;
81
+ }
82
+ try {
83
+ const result = domain.handle(methodName, msg.params ?? {});
84
+ ws.send(JSON.stringify({ id: msg.id, result: result ?? {} }));
85
+ }
86
+ catch (err) {
87
+ ws.send(JSON.stringify({ id: msg.id, error: { message: err.message ?? 'Unknown error' } }));
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,21 @@
1
+ import type { Component, ComponentType, SvelteComponent } from 'svelte';
2
+ import { TermNode } from './renderer/index.js';
3
+ import { CellBuffer } from './render/buffer.js';
4
+ export interface RenderResult {
5
+ buffer: CellBuffer;
6
+ root: TermNode;
7
+ unmount: () => void;
8
+ }
9
+ /**
10
+ * Render a Svelte component headlessly (no terminal) for testing.
11
+ * Returns the cell buffer, virtual tree, and unmount function.
12
+ */
13
+ export declare function renderHeadless<Props extends Record<string, any>>(AppComponent: ComponentType<SvelteComponent<Props>> | Component<Props>, options?: {
14
+ width?: number;
15
+ height?: number;
16
+ css?: string;
17
+ props?: Props;
18
+ }): RenderResult;
19
+ export { bufferToText, bufferToStyledText, bufferToSvg } from './render/snapshot.js';
20
+ export { CellBuffer } from './render/buffer.js';
21
+ export { TermNode } from './renderer/node.js';
@@ -0,0 +1,26 @@
1
+ import { TermNode } from './renderer/index.js';
2
+ import renderer from './renderer/default.js';
3
+ import { CellBuffer } from './render/buffer.js';
4
+ import { paint } from './render/paint.js';
5
+ import { parseCSS } from './css/parser.js';
6
+ import { resolveStyles } from './css/compute.js';
7
+ import { computeLayout } from './layout/engine.js';
8
+ /**
9
+ * Render a Svelte component headlessly (no terminal) for testing.
10
+ * Returns the cell buffer, virtual tree, and unmount function.
11
+ */
12
+ export function renderHeadless(AppComponent, options = {}) {
13
+ const width = options.width ?? 80;
14
+ const height = options.height ?? 24;
15
+ const root = new TermNode('element', 'root');
16
+ const stylesheet = options.css ? parseCSS(options.css) : null;
17
+ const { unmount } = renderer.render(AppComponent, { target: root, props: options.props ?? {} });
18
+ const buffer = new CellBuffer(width, height);
19
+ const styles = stylesheet ? resolveStyles(root, stylesheet) : undefined;
20
+ const layout = styles ? computeLayout(root, styles, width, height) : undefined;
21
+ paint(root, buffer, styles, layout);
22
+ return { buffer, root, unmount };
23
+ }
24
+ export { bufferToText, bufferToStyledText, bufferToSvg } from './render/snapshot.js';
25
+ export { CellBuffer } from './render/buffer.js';
26
+ export { TermNode } from './renderer/node.js';
@@ -0,0 +1,18 @@
1
+ import type { Component, ComponentType, SvelteComponent } from 'svelte';
2
+ export interface MountOptions {
3
+ fullscreen?: boolean;
4
+ css?: string;
5
+ mouse?: boolean;
6
+ debug?: boolean;
7
+ debugPort?: number;
8
+ }
9
+ export declare function run<Props extends Record<string, any>>(AppComponent: ComponentType<SvelteComponent<Props>> | Component<Props>, options?: MountOptions & ({} extends Props ? {
10
+ props?: Props;
11
+ } : {
12
+ props: Props;
13
+ })): () => void;
14
+ export { TermNode } from './renderer/node.js';
15
+ export { CellBuffer } from './render/buffer.js';
16
+ export { parseCSS } from './css/parser.js';
17
+ export { resolveStyles } from './css/compute.js';
18
+ export { StdinRouter } from './terminal/stdin-router.js';