@valyrianjs/terminal 0.1.1 → 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 +296 -201
- package/docs/core-concepts.md +143 -55
- package/docs/getting-started.md +209 -90
- package/docs/interaction-model.md +86 -52
- package/docs/primitive-gallery.md +365 -0
- package/docs/session-runtime.md +131 -362
- 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/docs/session-runtime.md
CHANGED
|
@@ -1,451 +1,220 @@
|
|
|
1
1
|
# Session Runtime
|
|
2
2
|
|
|
3
|
-
This guide
|
|
3
|
+
This guide explains how to use `mountTerminal()` in common cases first, then how to integrate it with host streams and runtime features.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## When to use the session runtime
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Use `mountTerminal()` when the UI needs one or more of these capabilities:
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
- focus and keyboard dispatch
|
|
10
|
+
- repeated renders after state changes
|
|
11
|
+
- clipboard integration
|
|
12
|
+
- mouse and coordinate-based interaction
|
|
13
|
+
- `stdin` and `stdout` connection
|
|
14
|
+
- ANSI output for a real terminal
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
- preserves focus across renders
|
|
13
|
-
- dispatches keyboard input per session
|
|
14
|
-
- connects `stdin` and `stdout`
|
|
15
|
-
- responds to coordinates and mouse events
|
|
16
|
+
Use `renderTerminal()` for deterministic static plain text.
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
## Practical recommendations
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
- Start without streams while building examples and scripted flows.
|
|
21
|
+
- Give stable `id` values to focusable primitives you want to control.
|
|
22
|
+
- Keep application state outside the renderer.
|
|
23
|
+
- Valyrian reactive state read by components refreshes the terminal when it changes, including external async changes. Call `session.update()` for non-reactive state or an explicit manual refresh.
|
|
24
|
+
- Call `session.destroy()` when a session is connected to real streams.
|
|
25
|
+
- Design keyboard-first flows and connect resize, modified keys, clipboard, and mouse through the host capabilities available in each terminal environment.
|
|
26
|
+
|
|
27
|
+
## Common runtime usage
|
|
28
|
+
|
|
29
|
+
Full example: [`examples/docs/interactive-note.tsx`](../examples/docs/interactive-note.tsx). Run it with `bun examples/docs/interactive-note.tsx`, type a note, press `Enter`, and quit with `Ctrl+C`.
|
|
22
30
|
|
|
23
|
-
|
|
24
|
-
import { Input, Screen, Text, mountTerminal } from "valyrianjs
|
|
31
|
+
```tsx
|
|
32
|
+
import { Input, Screen, Text, mountTerminal } from "@valyrianjs/terminal";
|
|
25
33
|
|
|
26
34
|
const state = { value: "" };
|
|
27
35
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
36
|
+
function App() {
|
|
37
|
+
return (
|
|
38
|
+
<Screen title="Runtime demo">
|
|
39
|
+
<Input
|
|
40
|
+
id="name"
|
|
41
|
+
value={state.value}
|
|
42
|
+
placeholder="Name"
|
|
43
|
+
onchange={(event) => {
|
|
44
|
+
state.value = event.value;
|
|
45
|
+
}}
|
|
46
|
+
/>
|
|
47
|
+
<Text>Current: {state.value || "(empty)"}</Text>
|
|
48
|
+
</Screen>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const session = mountTerminal(<App />);
|
|
40
53
|
|
|
41
54
|
session.focus("name");
|
|
42
55
|
session.dispatchKey("A");
|
|
43
56
|
session.dispatchKey("B");
|
|
44
57
|
|
|
45
58
|
console.log(session.output());
|
|
59
|
+
session.destroy();
|
|
46
60
|
```
|
|
47
61
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
`mountTerminal(input, options?)` accepts `TerminalMountOptions` with four integration points:
|
|
51
|
-
|
|
52
|
-
### `ansi?: boolean`
|
|
53
|
-
|
|
54
|
-
Enables incremental ANSI output to `stdout`. `session.ansiOutput()` is still available even if you do not enable this option; what changes is the session's automatic write behavior when it rerenders. If you do not enable it, the primary output written to `stdout` is plain text.
|
|
55
|
-
|
|
56
|
-
### `clipboard?: TerminalClipboardAdapter | false`
|
|
57
|
-
|
|
58
|
-
- if you pass an adapter, the session uses `readText()` and `writeText()` when handling shortcuts such as `CTRL_C` or `CTRL_V`
|
|
59
|
-
- if you pass `false`, system clipboard integration is disabled
|
|
60
|
-
- even with `clipboard: false`, `session.clipboard()` and `session.setClipboard()` still work as the session buffer
|
|
61
|
-
|
|
62
|
-
```tsx
|
|
63
|
-
/** @jsx v */
|
|
64
|
-
/** @jsxFrag v.fragment */
|
|
65
|
-
|
|
66
|
-
import { v } from "valyrian.js";
|
|
67
|
-
import { Screen, mountTerminal } from "valyrianjs-terminal";
|
|
68
|
-
|
|
69
|
-
const app = () => <Screen title="Clipboard Demo" />;
|
|
70
|
-
|
|
71
|
-
const session = mountTerminal(app, {
|
|
72
|
-
clipboard: {
|
|
73
|
-
readText() {
|
|
74
|
-
return "pasted value";
|
|
75
|
-
},
|
|
76
|
-
writeText(value) {
|
|
77
|
-
console.log("copied:", value);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
```tsx
|
|
84
|
-
/** @jsx v */
|
|
85
|
-
/** @jsxFrag v.fragment */
|
|
86
|
-
|
|
87
|
-
import { v } from "valyrian.js";
|
|
88
|
-
import { Screen, mountTerminal } from "valyrianjs-terminal";
|
|
89
|
-
|
|
90
|
-
const app = () => <Screen title="Clipboard Disabled" />;
|
|
91
|
-
|
|
92
|
-
const session = mountTerminal(app, { clipboard: false });
|
|
93
|
-
session.setClipboard("fallback value");
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
### `stdin?`
|
|
97
|
-
|
|
98
|
-
Input stream for keyboard and mouse. The session subscribes to `data`, tries to enable raw mode with `setRawMode(true)` if available, and calls `resume()` if the stream supports it.
|
|
99
|
-
|
|
100
|
-
Expected contract:
|
|
101
|
-
|
|
102
|
-
- `on("data", listener)` is required
|
|
103
|
-
- `off(...)` or `removeListener(...)` is optional for cleanup
|
|
104
|
-
- `setRawMode?(boolean)` is optional
|
|
105
|
-
- `resume?()` and `pause?()` are optional
|
|
106
|
-
|
|
107
|
-
### `stdout?`
|
|
108
|
-
|
|
109
|
-
Write target for the output produced on each rerender.
|
|
110
|
-
|
|
111
|
-
- with `ansi: false`, writes plain text
|
|
112
|
-
- with `ansi: true`, writes incremental ANSI diffs
|
|
113
|
-
|
|
114
|
-
```tsx
|
|
115
|
-
/** @jsx v */
|
|
116
|
-
/** @jsxFrag v.fragment */
|
|
117
|
-
|
|
118
|
-
import { v } from "valyrian.js";
|
|
119
|
-
import { Screen, mountTerminal } from "valyrianjs-terminal";
|
|
120
|
-
|
|
121
|
-
const app = () => <Screen title="ANSI Stream Demo" />;
|
|
122
|
-
|
|
123
|
-
const writes: string[] = [];
|
|
124
|
-
|
|
125
|
-
const session = mountTerminal(app, {
|
|
126
|
-
ansi: true,
|
|
127
|
-
stdout: {
|
|
128
|
-
write(chunk) {
|
|
129
|
-
writes.push(String(chunk));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
});
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
## `TerminalSession` lifecycle
|
|
136
|
-
|
|
137
|
-
The session keeps a current frame, a current output, and the interactive state associated with the focusable tree. In practice, the cycle is:
|
|
62
|
+
The session lifecycle is:
|
|
138
63
|
|
|
139
64
|
1. mount with `mountTerminal()`
|
|
140
|
-
2. read
|
|
141
|
-
3.
|
|
142
|
-
4. rerender with `update()`
|
|
65
|
+
2. read `output()` or `ansiOutput()`
|
|
66
|
+
3. dispatch input or update external state
|
|
67
|
+
4. rerender with `update()` when needed
|
|
143
68
|
5. close with `destroy()`
|
|
144
69
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Reevaluates `input`, recalculates the frame, and returns the current plain-text output.
|
|
148
|
-
|
|
149
|
-
Use it when you changed state outside the session handlers.
|
|
150
|
-
|
|
151
|
-
```tsx
|
|
152
|
-
const state = { count: 0 };
|
|
153
|
-
|
|
154
|
-
const session = mountTerminal(() => (
|
|
155
|
-
<Screen>
|
|
156
|
-
<Text>Count: {state.count}</Text>
|
|
157
|
-
</Screen>
|
|
158
|
-
));
|
|
159
|
-
|
|
160
|
-
state.count += 1;
|
|
161
|
-
session.update();
|
|
162
|
-
|
|
163
|
-
console.log(session.output());
|
|
164
|
-
```
|
|
70
|
+
## Input, output, and cleanup
|
|
165
71
|
|
|
166
72
|
### `output()`
|
|
167
73
|
|
|
168
|
-
Returns the current
|
|
169
|
-
|
|
170
|
-
- inspect the current state
|
|
171
|
-
- validate snapshots
|
|
172
|
-
- test focus and content without ANSI
|
|
74
|
+
Returns the current plain-text frame. Use it for snapshots, examples, and content inspection.
|
|
173
75
|
|
|
174
76
|
### `ansiOutput()`
|
|
175
77
|
|
|
176
|
-
Returns the current frame serialized
|
|
78
|
+
Returns the current frame serialized with ANSI cursor movement and style spans.
|
|
177
79
|
|
|
178
|
-
-
|
|
179
|
-
- validate selection or focus in an ANSI terminal
|
|
180
|
-
- integrate with a layer that consumes full escape sequences
|
|
80
|
+
Related example: [`examples/docs/theme-colors.tsx`](../examples/docs/theme-colors.tsx). Run it with `bun examples/docs/theme-colors.tsx`, press `Tab`, and quit with `Ctrl+C`.
|
|
181
81
|
|
|
182
82
|
```tsx
|
|
183
|
-
|
|
184
|
-
/** @jsxFrag v.fragment */
|
|
185
|
-
|
|
186
|
-
import { v } from "valyrian.js";
|
|
187
|
-
import { Screen, mountTerminal } from "valyrianjs-terminal";
|
|
188
|
-
|
|
189
|
-
const app = () => <Screen title="ANSI Frame Demo" />;
|
|
190
|
-
|
|
191
|
-
const session = mountTerminal(app, { ansi: true });
|
|
192
|
-
const ansi = session.ansiOutput();
|
|
193
|
-
console.log(ansi);
|
|
194
|
-
```
|
|
83
|
+
import { Screen, mountTerminal } from "@valyrianjs/terminal";
|
|
195
84
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
85
|
+
function App() {
|
|
86
|
+
return <Screen title="ANSI frame" />;
|
|
87
|
+
}
|
|
199
88
|
|
|
200
|
-
|
|
89
|
+
const session = mountTerminal(<App />, { runtime: "headless" });
|
|
201
90
|
|
|
202
|
-
|
|
91
|
+
console.log(session.ansiOutput());
|
|
203
92
|
session.destroy();
|
|
204
93
|
```
|
|
205
94
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
Focus lives at the session level. To participate in this flow, interactive nodes need an `id`.
|
|
209
|
-
|
|
210
|
-
### `focus(id)`
|
|
211
|
-
|
|
212
|
-
Focuses a node by `id`. Returns `true` if it found one.
|
|
213
|
-
|
|
214
|
-
### `focusNext()` and `focusPrev()`
|
|
215
|
-
|
|
216
|
-
Walk the current focusable-element order. `dispatchKey("TAB")` and `dispatchKey("SHIFT_TAB")` use the same flow.
|
|
95
|
+
`ansiOutput()` is an explicit inspection/export path. Keep ordinary `Text`, input values, list rows, and log entries as text content. Advanced span serialization tokens are documented in [Advanced: span serialization tokens](./api-reference.md#advanced-span-serialization-tokens) for low-level renderer integrations.
|
|
217
96
|
|
|
218
|
-
### `
|
|
219
|
-
|
|
220
|
-
Looks up the hitbox at the current coordinates, updates semantic hover when applicable, and focuses that node.
|
|
97
|
+
### `update()`
|
|
221
98
|
|
|
222
|
-
|
|
99
|
+
Re-evaluates the session input and returns the current plain-text output.
|
|
223
100
|
|
|
224
|
-
-
|
|
225
|
-
- if it lands on an `Input`, it focuses it and places the cursor based on the coordinate
|
|
226
|
-
- if it lands on another focusable surface, it moves focus there
|
|
101
|
+
Related example: [`examples/docs/component-composition.tsx`](../examples/docs/component-composition.tsx). Run it with `bun examples/docs/component-composition.tsx`, switch cards with `N/P`, and quit with `Ctrl+C`.
|
|
227
102
|
|
|
228
103
|
```tsx
|
|
229
|
-
|
|
230
|
-
<Screen>
|
|
231
|
-
<Input id="name" value="abc" />
|
|
232
|
-
<Button id="save">Save</Button>
|
|
233
|
-
</Screen>
|
|
234
|
-
));
|
|
235
|
-
|
|
236
|
-
session.focusAt(2, 1);
|
|
237
|
-
session.clickAt(3, 2);
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
## Per-session keyboard dispatch
|
|
104
|
+
import { Screen, Text, mountTerminal } from "@valyrianjs/terminal";
|
|
241
105
|
|
|
242
|
-
|
|
106
|
+
const state = { count: 0 };
|
|
243
107
|
|
|
244
|
-
|
|
108
|
+
function App() {
|
|
109
|
+
return (
|
|
110
|
+
<Screen title="Update demo">
|
|
111
|
+
<Text>Count: {state.count}</Text>
|
|
112
|
+
</Screen>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
245
115
|
|
|
246
|
-
|
|
247
|
-
- `Input`: single-byte characters, `ENTER`, `LEFT`, `RIGHT`, `SHIFT_LEFT`, `SHIFT_RIGHT`, `ALT_LEFT`, `ALT_RIGHT`, `HOME`, `END`, `CTRL_A`, `CTRL_C`, `CTRL_X`, `CTRL_V`, `BACKSPACE`, `DELETE`
|
|
248
|
-
- `Button`: `ENTER`, `SPACE`
|
|
249
|
-
- `List`: `UP`, `DOWN`, `LEFT`, `RIGHT`, `ENTER`
|
|
250
|
-
- `ScrollView`: `UP`, `DOWN`
|
|
116
|
+
const session = mountTerminal(<App />);
|
|
251
117
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const session = mountTerminal(() => (
|
|
256
|
-
<Screen>
|
|
257
|
-
<Input
|
|
258
|
-
id="name"
|
|
259
|
-
value={state.value}
|
|
260
|
-
onchange={(event) => {
|
|
261
|
-
state.value = event.value;
|
|
262
|
-
}}
|
|
263
|
-
onsubmit={(event) => {
|
|
264
|
-
state.saved = event.value;
|
|
265
|
-
}}
|
|
266
|
-
/>
|
|
267
|
-
</Screen>
|
|
268
|
-
));
|
|
118
|
+
state.count += 1;
|
|
119
|
+
session.update();
|
|
269
120
|
|
|
270
|
-
session.
|
|
271
|
-
session.
|
|
272
|
-
session.dispatchKey("B");
|
|
273
|
-
session.dispatchKey("LEFT");
|
|
274
|
-
session.dispatchKey("C");
|
|
275
|
-
session.dispatchKey("ENTER");
|
|
121
|
+
console.log(session.output());
|
|
122
|
+
session.destroy();
|
|
276
123
|
```
|
|
277
124
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
The session separates two concepts:
|
|
281
|
-
|
|
282
|
-
- the session clipboard buffer: `clipboard()` and `setClipboard()`
|
|
283
|
-
- system integration: `options.clipboard`
|
|
125
|
+
### `destroy()`
|
|
284
126
|
|
|
285
|
-
|
|
127
|
+
Removes listeners and restores supported terminal state when a session connected to streams changed it. Always call it when the session owns real input or output resources.
|
|
286
128
|
|
|
287
|
-
|
|
288
|
-
const state = { value: "abcd" };
|
|
289
|
-
|
|
290
|
-
const session = mountTerminal(() => (
|
|
291
|
-
<Screen>
|
|
292
|
-
<Input
|
|
293
|
-
id="name"
|
|
294
|
-
value={state.value}
|
|
295
|
-
onchange={(event) => {
|
|
296
|
-
state.value = event.value;
|
|
297
|
-
}}
|
|
298
|
-
/>
|
|
299
|
-
</Screen>
|
|
300
|
-
), { clipboard: false });
|
|
129
|
+
## Advanced host integration
|
|
301
130
|
|
|
302
|
-
|
|
303
|
-
session.setClipboard("XY");
|
|
304
|
-
session.dispatchKey("END");
|
|
305
|
-
session.dispatchKey("CTRL_V");
|
|
306
|
-
```
|
|
131
|
+
### Mount options
|
|
307
132
|
|
|
308
|
-
|
|
133
|
+
`mountTerminal(input, options?)` accepts options for host integration. `mountTerminal(<App />)` is the normal interactive path; it uses process stdio, ANSI diffs, alternate screen, and a hidden cursor only when called from a real interactive TTY. Imports and non-TTY/test runs do not auto-attach stdio.
|
|
309
134
|
|
|
310
|
-
|
|
135
|
+
- `runtime?: "app" | "headless"` - selects interactive app behavior or scriptable plain output. Omitted runtime uses app mode only for interactive TTY hosts and stays headless in non-TTY/CI contexts.
|
|
136
|
+
- `alternateScreen?: boolean` - enters the terminal alternate screen and restores it on destroy by default. Real interactive app mode enables it by default unless overridden.
|
|
137
|
+
- `hideCursor?: boolean` - expresses that the session should hide the terminal cursor while it owns the screen when the host supports it. Real interactive app mode enables it by default unless overridden.
|
|
138
|
+
- `restoreOnDestroy?: boolean` - controls whether destroy should restore supported cursor and alternate-screen state.
|
|
139
|
+
- `clipboard?: TerminalClipboardAdapter | false` - connects a custom clipboard adapter or disables external clipboard integration.
|
|
140
|
+
- `cols?: number` and `rows?: number` - set the initial terminal size.
|
|
141
|
+
- `stdin?` - receives keyboard, paste, and mouse input.
|
|
142
|
+
- `stdout?` - receives rendered output.
|
|
311
143
|
|
|
312
|
-
|
|
144
|
+
### Clipboard adapter
|
|
313
145
|
|
|
314
|
-
-
|
|
315
|
-
- if `stdin.setRawMode` exists, it tries `setRawMode(true)`
|
|
316
|
-
- if `stdin.resume` exists, it tries `resume()`
|
|
317
|
-
- on each rerender, if `stdout.write` exists, the session writes the current frame
|
|
318
|
-
- on destroy, it tries to unsubscribe and restore raw mode
|
|
146
|
+
Related example: [`examples/docs/interactive-note.tsx`](../examples/docs/interactive-note.tsx). Run it with `bun examples/docs/interactive-note.tsx`, type a note, press `Enter`, and quit with `Ctrl+C`.
|
|
319
147
|
|
|
320
148
|
```tsx
|
|
321
|
-
|
|
322
|
-
/** @jsxFrag v.fragment */
|
|
149
|
+
import { Screen, mountTerminal } from "@valyrianjs/terminal";
|
|
323
150
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
151
|
+
function App() {
|
|
152
|
+
return <Screen title="Clipboard" />;
|
|
153
|
+
}
|
|
327
154
|
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const writes: string[] = [];
|
|
337
|
-
|
|
338
|
-
const session = mountTerminal(app, {
|
|
339
|
-
stdin,
|
|
340
|
-
stdout: {
|
|
341
|
-
write(chunk) {
|
|
342
|
-
writes.push(String(chunk));
|
|
155
|
+
const session = mountTerminal(<App />, {
|
|
156
|
+
clipboard: {
|
|
157
|
+
readText() {
|
|
158
|
+
return "pasted value";
|
|
159
|
+
},
|
|
160
|
+
writeText(value) {
|
|
161
|
+
console.log("copied:", value);
|
|
343
162
|
}
|
|
344
163
|
}
|
|
345
164
|
});
|
|
346
165
|
|
|
347
|
-
stdin.emit("data", "A");
|
|
348
|
-
stdin.emit("data", "\r");
|
|
349
166
|
session.destroy();
|
|
350
167
|
```
|
|
351
168
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
There are two practical ways to work with ANSI:
|
|
355
|
-
|
|
356
|
-
- `ansi: true` + `stdout`: the session emits incremental diffs on each rerender
|
|
357
|
-
- `session.ansiOutput()`: retrieves the full ANSI frame for the current state
|
|
169
|
+
Passing `clipboard: false` disables external clipboard integration while preserving the session clipboard buffer through `clipboard()` and `setClipboard()`.
|
|
358
170
|
|
|
359
|
-
|
|
171
|
+
### Streams
|
|
360
172
|
|
|
361
|
-
|
|
362
|
-
const state = { value: "AB" };
|
|
363
|
-
|
|
364
|
-
const session = mountTerminal(() => (
|
|
365
|
-
<Screen title="ANSI Demo">
|
|
366
|
-
<Input
|
|
367
|
-
id="name"
|
|
368
|
-
value={state.value}
|
|
369
|
-
onchange={(event) => {
|
|
370
|
-
state.value = event.value;
|
|
371
|
-
}}
|
|
372
|
-
/>
|
|
373
|
-
</Screen>
|
|
374
|
-
), { ansi: true });
|
|
375
|
-
|
|
376
|
-
session.focus("name");
|
|
377
|
-
session.dispatchKey("LEFT");
|
|
378
|
-
|
|
379
|
-
console.log(session.ansiOutput());
|
|
380
|
-
```
|
|
173
|
+
`stdin` should provide `on("data", listener)`. Cleanup methods such as `off(...)` or `removeListener(...)` are used when available. `setRawMode`, `resume`, and `pause` are optional.
|
|
381
174
|
|
|
382
|
-
|
|
175
|
+
`stdout` should provide `write(chunk: string)`. Optional `columns` and `rows` can provide initial dimensions. Optional `resize` and `drain` events are used only when the stream also provides listener cleanup.
|
|
383
176
|
|
|
384
|
-
|
|
177
|
+
### Size and resize
|
|
385
178
|
|
|
386
|
-
|
|
179
|
+
`size()` returns `{ cols, rows }`. `resize(cols, rows)` validates positive integer dimensions, stores them, and rerenders once.
|
|
387
180
|
|
|
388
|
-
- `
|
|
389
|
-
- `drag`: if an `Input` is active through the mouse, it extends selection; in `List` and `ScrollView`, it updates row hover
|
|
390
|
-
- `release`: ends mouse selection and closes capture when applicable
|
|
391
|
-
|
|
392
|
-
```ts
|
|
393
|
-
stdin.emit("data", "\u001b[<0;4;1M");
|
|
394
|
-
stdin.emit("data", "\u001b[<32;7;1M");
|
|
395
|
-
stdin.emit("data", "\u001b[<0;7;1m");
|
|
396
|
-
```
|
|
181
|
+
Related example: [`examples/docs/background-fill.tsx`](../examples/docs/background-fill.tsx). Run it with `bun examples/docs/background-fill.tsx`, switch surfaces with `B`, and quit with `Ctrl+C`.
|
|
397
182
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
### Wheel
|
|
183
|
+
```tsx
|
|
184
|
+
import { Screen, mountTerminal } from "@valyrianjs/terminal";
|
|
401
185
|
|
|
402
|
-
|
|
186
|
+
function App() {
|
|
187
|
+
return <Screen title="Resizable" />;
|
|
188
|
+
}
|
|
403
189
|
|
|
404
|
-
|
|
405
|
-
- `wheel-down` is equivalent to `dispatchKey("DOWN")`
|
|
190
|
+
const session = mountTerminal(<App />, { cols: 80, rows: 24 });
|
|
406
191
|
|
|
407
|
-
|
|
408
|
-
|
|
192
|
+
console.log(session.size());
|
|
193
|
+
session.resize(100, 30);
|
|
194
|
+
console.log(session.size());
|
|
195
|
+
session.destroy();
|
|
409
196
|
```
|
|
410
197
|
|
|
411
|
-
|
|
198
|
+
Automatic resize works with compatible output sources that emit supported resize events after updating their `columns` and `rows` values.
|
|
412
199
|
|
|
413
|
-
###
|
|
200
|
+
### Keyboard, paste, and mouse input
|
|
414
201
|
|
|
415
|
-
`
|
|
202
|
+
`dispatchKey(key)` injects a normalized key directly. Connected `stdin` can also deliver keyboard input, terminal paste input, and supported SGR mouse input.
|
|
416
203
|
|
|
417
|
-
|
|
204
|
+
Bracketed paste is handled as terminal input: pasted text is inserted into the focused `Input` or `Editor` as content instead of being treated as separate key commands. Multiline `Editor` cursor columns are reported as JavaScript UTF-16 string columns, not as a complete terminal grapheme-width model.
|
|
418
205
|
|
|
419
|
-
|
|
420
|
-
- `oncaptureend`
|
|
206
|
+
Mouse input can focus or activate hitboxes, update hover state, and map wheel input to vertical navigation. Keep keyboard alternatives for important actions so every terminal workflow has a reliable input path.
|
|
421
207
|
|
|
422
|
-
|
|
208
|
+
## Host integration behavior
|
|
423
209
|
|
|
424
|
-
-
|
|
425
|
-
-
|
|
210
|
+
- Modified keys follow sequences reported by the connected terminal.
|
|
211
|
+
- Clipboard behavior uses the provided adapter or the session clipboard buffer.
|
|
212
|
+
- Mouse behavior uses supported terminal input sequences.
|
|
213
|
+
- Automatic resize works with compatible output sources and cleanup-capable listeners.
|
|
214
|
+
- Use session runtime for terminal rendering, focus, input, output, and cleanup. Keep routing, persistence, command orchestration, and process management in the app layer.
|
|
426
215
|
|
|
427
|
-
|
|
428
|
-
const session = mountTerminal(() => (
|
|
429
|
-
<Screen>
|
|
430
|
-
<List
|
|
431
|
-
id="menu"
|
|
432
|
-
pointerCapture
|
|
433
|
-
items={["Open", "Save", "Exit"]}
|
|
434
|
-
oncapturestart={(event) => console.log(event)}
|
|
435
|
-
oncaptureend={(event) => console.log(event)}
|
|
436
|
-
/>
|
|
437
|
-
</Screen>
|
|
438
|
-
), { stdin, clipboard: false });
|
|
439
|
-
|
|
440
|
-
stdin.emit("data", "\u001b[<0;2;1M");
|
|
441
|
-
stdin.emit("data", "\u001b[<32;99;99M");
|
|
442
|
-
stdin.emit("data", "\u001b[<0;99;99m");
|
|
443
|
-
```
|
|
444
|
-
|
|
445
|
-
## Practical recommendations
|
|
216
|
+
## Where to go next
|
|
446
217
|
|
|
447
|
-
-
|
|
448
|
-
-
|
|
449
|
-
-
|
|
450
|
-
- always call `destroy()` when mounting with `stdin`
|
|
451
|
-
- use `clipboard: false` in tests or environments where you do not want to depend on the system clipboard
|
|
218
|
+
- [Getting Started](./getting-started.md) for the first project.
|
|
219
|
+
- [Interaction Model](./interaction-model.md) for per-primitive behavior.
|
|
220
|
+
- [API Reference](./api-reference.md) for exact methods, props, and payloads.
|