@valyrianjs/terminal 0.1.0 → 0.1.2
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/README.md +105 -55
- package/dist/ansi.d.ts +20 -4
- package/dist/ansi.d.ts.map +1 -1
- package/dist/ansi.js +171 -47
- package/dist/ansi.js.map +1 -1
- package/dist/editor-state.d.ts +22 -0
- package/dist/editor-state.d.ts.map +1 -0
- package/dist/editor-state.js +110 -0
- package/dist/editor-state.js.map +1 -0
- package/dist/events.d.ts +1 -4
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +15 -38
- package/dist/events.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/keymap.d.ts +7 -0
- package/dist/keymap.d.ts.map +1 -0
- package/dist/keymap.js +133 -0
- package/dist/keymap.js.map +1 -0
- package/dist/layout.d.ts +10 -1
- package/dist/layout.d.ts.map +1 -1
- package/dist/layout.js +97 -7
- package/dist/layout.js.map +1 -1
- package/dist/mouse.d.ts +1 -0
- package/dist/mouse.d.ts.map +1 -1
- package/dist/mouse.js +24 -1
- package/dist/mouse.js.map +1 -1
- package/dist/output-writer.d.ts +9 -0
- package/dist/output-writer.d.ts.map +1 -0
- package/dist/output-writer.js +79 -0
- package/dist/output-writer.js.map +1 -0
- package/dist/paste.d.ts +7 -0
- package/dist/paste.d.ts.map +1 -0
- package/dist/paste.js +18 -0
- package/dist/paste.js.map +1 -0
- package/dist/primitives.d.ts +8 -1
- package/dist/primitives.d.ts.map +1 -1
- package/dist/primitives.js +9 -1
- package/dist/primitives.js.map +1 -1
- package/dist/render.d.ts +8 -3
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +840 -67
- package/dist/render.js.map +1 -1
- package/dist/runtime.d.ts +29 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +215 -0
- package/dist/runtime.js.map +1 -0
- package/dist/scheduler.d.ts +8 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +24 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +729 -199
- package/dist/session.js.map +1 -1
- package/dist/stream-log.d.ts +40 -0
- package/dist/stream-log.d.ts.map +1 -0
- package/dist/stream-log.js +73 -0
- package/dist/stream-log.js.map +1 -0
- package/dist/text.d.ts +3 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +19 -0
- package/dist/text.js.map +1 -0
- package/dist/theme.d.ts +7 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +254 -0
- package/dist/theme.js.map +1 -0
- package/dist/tree.d.ts +2 -0
- package/dist/tree.d.ts.map +1 -1
- package/dist/tree.js +42 -1
- package/dist/tree.js.map +1 -1
- package/dist/types.d.ts +183 -18
- package/dist/types.d.ts.map +1 -1
- package/docs/api-reference.md +302 -136
- package/docs/assets/quick-note.svg +13 -0
- package/docs/cookbook.md +297 -202
- package/docs/core-concepts.md +143 -55
- package/docs/getting-started.md +209 -90
- package/docs/interaction-model.md +95 -61
- package/docs/primitive-gallery.md +365 -0
- package/docs/session-runtime.md +132 -363
- package/docs/valyrian-modules.md +3196 -0
- package/llms-full.txt +5357 -0
- package/package.json +21 -8
- package/src/ansi.ts +269 -0
- package/src/clipboard.ts +76 -0
- package/src/editor-state.ts +162 -0
- package/src/events.ts +163 -0
- package/src/index.ts +92 -0
- package/src/keymap.ts +151 -0
- package/src/layout.ts +282 -0
- package/src/mouse.ts +68 -0
- package/src/output-writer.ts +93 -0
- package/src/paste.ts +23 -0
- package/src/primitives.ts +52 -0
- package/src/render.ts +1107 -0
- package/src/runtime.ts +273 -0
- package/src/scheduler.ts +33 -0
- package/src/session.ts +1260 -0
- package/src/stream-log.ts +96 -0
- package/src/text.ts +20 -0
- package/src/theme.ts +263 -0
- package/src/tree.ts +169 -0
- package/src/types.ts +523 -0
- package/tsconfig.json +4 -7
- package/docs/local-demo.md +0 -28
package/src/mouse.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { parseTerminalKey } from "./events.js";
|
|
2
|
+
|
|
3
|
+
import type { ParsedTerminalInput, TerminalHitbox } from "./types.js";
|
|
4
|
+
|
|
5
|
+
const nestedParentTags = new Set(["terminal-box", "terminal-view"]);
|
|
6
|
+
|
|
7
|
+
function containsHitbox(parent: TerminalHitbox, child: TerminalHitbox) {
|
|
8
|
+
return child.x1 >= parent.x1 && child.x2 <= parent.x2 && child.y1 >= parent.y1 && child.y2 <= parent.y2;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function samePointerLayer(left: TerminalHitbox, right: TerminalHitbox) {
|
|
12
|
+
return (left.pointerLayer ?? 0) === (right.pointerLayer ?? 0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function parseTerminalInput(chunk: string | Uint8Array): ParsedTerminalInput {
|
|
16
|
+
const value = typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
17
|
+
const match = /^\u001b\[<(\d+);(\d+);(\d+)([Mm])$/.exec(value);
|
|
18
|
+
if (match) {
|
|
19
|
+
const code = Number(match[1]);
|
|
20
|
+
let action: "press" | "release" | "drag" | "wheel-up" | "wheel-down" = match[4] === "M" ? "press" : "release";
|
|
21
|
+
if (code >= 64) {
|
|
22
|
+
action = code === 64 ? "wheel-up" : "wheel-down";
|
|
23
|
+
} else if (code >= 32 && match[4] === "M") {
|
|
24
|
+
action = "drag";
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
type: "mouse",
|
|
28
|
+
button: code,
|
|
29
|
+
x: Number(match[2]),
|
|
30
|
+
y: Number(match[3]),
|
|
31
|
+
action
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return { type: "key", key: parseTerminalKey(value) };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function findHitboxAt(hitboxes: TerminalHitbox[], x: number, y: number) {
|
|
38
|
+
let target: TerminalHitbox | null = null;
|
|
39
|
+
|
|
40
|
+
for (const box of hitboxes) {
|
|
41
|
+
if (y < box.y1 || y > box.y2 || x < box.x1 || x > box.x2) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!target) {
|
|
46
|
+
target = box;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (nestedParentTags.has(target.tag) && samePointerLayer(target, box) && containsHitbox(target, box)) {
|
|
51
|
+
target = box;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return target;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function resolvePointerTarget(hitboxes: TerminalHitbox[], x: number, y: number) {
|
|
59
|
+
return findHitboxAt(hitboxes, x, y);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function cursorFromHitbox(hitbox: TerminalHitbox, x: number) {
|
|
63
|
+
if (typeof hitbox.textStartX !== "number") {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
const textLength = Number(hitbox.textLength || 0);
|
|
67
|
+
return Math.max(0, Math.min(textLength, x - hitbox.textStartX));
|
|
68
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { TerminalOutputStream } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export interface OutputWriter {
|
|
4
|
+
write(chunk: string, options?: { force?: boolean }): void;
|
|
5
|
+
destroy(): void;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function createOutputWriter(stream: TerminalOutputStream | undefined): OutputWriter {
|
|
9
|
+
const queue: string[] = [];
|
|
10
|
+
let blocked = false;
|
|
11
|
+
let destroyed = false;
|
|
12
|
+
let listeningForDrain = false;
|
|
13
|
+
|
|
14
|
+
const onDrain = () => {
|
|
15
|
+
removeDrainListener();
|
|
16
|
+
blocked = false;
|
|
17
|
+
flushQueue();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function canListenForDrain() {
|
|
21
|
+
return (
|
|
22
|
+
!!stream &&
|
|
23
|
+
typeof stream.on === "function" &&
|
|
24
|
+
(typeof stream.off === "function" || typeof stream.removeListener === "function")
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function addDrainListener() {
|
|
29
|
+
if (listeningForDrain || !canListenForDrain()) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const target = stream;
|
|
33
|
+
if (!target) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
listeningForDrain = true;
|
|
37
|
+
target.on!("drain", onDrain);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function removeDrainListener() {
|
|
41
|
+
if (!stream || !listeningForDrain) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (typeof stream.off === "function") {
|
|
45
|
+
stream.off("drain", onDrain);
|
|
46
|
+
} else if (typeof stream.removeListener === "function") {
|
|
47
|
+
stream.removeListener("drain", onDrain);
|
|
48
|
+
}
|
|
49
|
+
listeningForDrain = false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function writeNow(chunk: string) {
|
|
53
|
+
if (!stream || destroyed) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const accepted = stream.write(chunk);
|
|
57
|
+
if (accepted === false && canListenForDrain()) {
|
|
58
|
+
blocked = true;
|
|
59
|
+
addDrainListener();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function flushQueue() {
|
|
64
|
+
while (!destroyed && !blocked && queue.length > 0) {
|
|
65
|
+
writeNow(queue.shift() as string);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
write(chunk: string, options?: { force?: boolean }) {
|
|
71
|
+
if (destroyed || !stream) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (options?.force) {
|
|
75
|
+
stream.write(chunk);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
if (blocked) {
|
|
79
|
+
queue.push(chunk);
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
writeNow(chunk);
|
|
83
|
+
},
|
|
84
|
+
destroy() {
|
|
85
|
+
if (destroyed) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
destroyed = true;
|
|
89
|
+
queue.length = 0;
|
|
90
|
+
removeDrainListener();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
package/src/paste.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
const BRACKETED_PASTE_START = "\u001b[200~";
|
|
2
|
+
const BRACKETED_PASTE_END = "\u001b[201~";
|
|
3
|
+
|
|
4
|
+
export type TerminalPasteEvent = { type: "paste"; text: string; rest: string };
|
|
5
|
+
|
|
6
|
+
function normalizePasteLineEndings(value: string) {
|
|
7
|
+
return value.replace(/\r\n?/g, "\n");
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function parseBracketedPaste(value: string): TerminalPasteEvent | null {
|
|
11
|
+
if (!value.startsWith(BRACKETED_PASTE_START)) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const textStart = BRACKETED_PASTE_START.length;
|
|
16
|
+
const textEnd = value.indexOf(BRACKETED_PASTE_END, textStart);
|
|
17
|
+
if (textEnd === -1) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const restStart = textEnd + BRACKETED_PASTE_END.length;
|
|
22
|
+
return { type: "paste", text: normalizePasteLineEndings(value.slice(textStart, textEnd)), rest: value.slice(restStart) };
|
|
23
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { v } from "valyrian.js";
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AnyProps,
|
|
5
|
+
TerminalBoxProps,
|
|
6
|
+
TerminalButtonProps,
|
|
7
|
+
TerminalEditorProps,
|
|
8
|
+
TerminalFocusScopeProps,
|
|
9
|
+
TerminalFixedProps,
|
|
10
|
+
TerminalInputProps,
|
|
11
|
+
TerminalListProps,
|
|
12
|
+
TerminalLogViewProps,
|
|
13
|
+
TerminalOverlayProps,
|
|
14
|
+
TerminalPaneProps,
|
|
15
|
+
TerminalPrimitiveTag,
|
|
16
|
+
TerminalRowProps,
|
|
17
|
+
TerminalScreenProps,
|
|
18
|
+
TerminalScrollViewProps,
|
|
19
|
+
TerminalSplitProps,
|
|
20
|
+
TerminalTableProps,
|
|
21
|
+
TerminalTdProps,
|
|
22
|
+
TerminalTextProps,
|
|
23
|
+
TerminalViewProps
|
|
24
|
+
} from "./types.js";
|
|
25
|
+
|
|
26
|
+
type TerminalPrimitiveComponent<P> = (props: P | AnyProps, children: any[]) => any;
|
|
27
|
+
|
|
28
|
+
function primitive<P = Record<string, any>>(tag: TerminalPrimitiveTag): TerminalPrimitiveComponent<P> {
|
|
29
|
+
return function TerminalPrimitive(props: P | AnyProps, children: any[]) {
|
|
30
|
+
const nextProps = { ...(props || {}) } as Record<string, any>;
|
|
31
|
+
return v(tag, nextProps, ...children);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const Screen = primitive<TerminalScreenProps>("terminal-screen");
|
|
36
|
+
export const Box = primitive<TerminalBoxProps>("terminal-box");
|
|
37
|
+
export const View = primitive<TerminalViewProps>("terminal-view");
|
|
38
|
+
export const Pane = primitive<TerminalPaneProps>("terminal-pane");
|
|
39
|
+
export const Split = primitive<TerminalSplitProps>("terminal-split");
|
|
40
|
+
export const Fixed = primitive<TerminalFixedProps>("terminal-fixed");
|
|
41
|
+
export const Overlay = primitive<TerminalOverlayProps>("terminal-overlay");
|
|
42
|
+
export const FocusScope = primitive<TerminalFocusScopeProps>("terminal-focus-scope");
|
|
43
|
+
export const Text = primitive<TerminalTextProps>("terminal-text");
|
|
44
|
+
export const Input = primitive<TerminalInputProps>("terminal-input");
|
|
45
|
+
export const Editor = primitive<TerminalEditorProps>("terminal-editor");
|
|
46
|
+
export const Button = primitive<TerminalButtonProps>("terminal-button");
|
|
47
|
+
export const ScrollView = primitive<TerminalScrollViewProps>("terminal-scroll");
|
|
48
|
+
export const LogView = primitive<TerminalLogViewProps>("terminal-log-view");
|
|
49
|
+
export const List = primitive<TerminalListProps<any>>("terminal-list");
|
|
50
|
+
export const Table = primitive<TerminalTableProps>("terminal-table");
|
|
51
|
+
export const Row = primitive<TerminalRowProps>("terminal-row");
|
|
52
|
+
export const Td = primitive<TerminalTdProps>("terminal-td");
|