@valyrianjs/terminal 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/README.md +71 -0
- package/dist/ansi.d.ts +6 -0
- package/dist/ansi.d.ts.map +1 -0
- package/dist/ansi.js +105 -0
- package/dist/ansi.js.map +1 -0
- package/dist/clipboard.d.ts +3 -0
- package/dist/clipboard.d.ts.map +1 -0
- package/dist/clipboard.js +71 -0
- package/dist/clipboard.js.map +1 -0
- package/dist/events.d.ts +62 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +189 -0
- package/dist/events.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/layout.d.ts +16 -0
- package/dist/layout.d.ts.map +1 -0
- package/dist/layout.js +153 -0
- package/dist/layout.js.map +1 -0
- package/dist/mouse.d.ts +5 -0
- package/dist/mouse.d.ts.map +1 -0
- package/dist/mouse.js +34 -0
- package/dist/mouse.js.map +1 -0
- package/dist/primitives.d.ts +15 -0
- package/dist/primitives.d.ts.map +1 -0
- package/dist/primitives.js +18 -0
- package/dist/primitives.js.map +1 -0
- package/dist/render.d.ts +5 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +230 -0
- package/dist/render.js.map +1 -0
- package/dist/session.d.ts +3 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +599 -0
- package/dist/session.js.map +1 -0
- package/dist/tree.d.ts +9 -0
- package/dist/tree.d.ts.map +1 -0
- package/dist/tree.js +103 -0
- package/dist/tree.js.map +1 -0
- package/dist/types.d.ts +224 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/docs/api-reference.md +273 -0
- package/docs/cookbook.md +286 -0
- package/docs/core-concepts.md +93 -0
- package/docs/getting-started.md +148 -0
- package/docs/interaction-model.md +115 -0
- package/docs/local-demo.md +28 -0
- package/docs/session-runtime.md +451 -0
- package/package.json +44 -0
- package/tsconfig.build.json +15 -0
- package/tsconfig.json +25 -0
package/docs/cookbook.md
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
# Cookbook
|
|
2
|
+
|
|
3
|
+
Recetas cortas y copiables para `valyrianjs-terminal`. Esta guia complementa `docs/getting-started.md`, `docs/interaction-model.md` y `docs/api-reference.md`.
|
|
4
|
+
|
|
5
|
+
## Render estatico para snapshots
|
|
6
|
+
|
|
7
|
+
Usalo cuando quieras validar layout o generar texto plano sin abrir una sesion interactiva.
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
/** @jsx v */
|
|
11
|
+
/** @jsxFrag v.fragment */
|
|
12
|
+
|
|
13
|
+
import { v } from "valyrian.js";
|
|
14
|
+
import { Screen, Text, renderTerminal } from "valyrianjs-terminal";
|
|
15
|
+
|
|
16
|
+
const output = renderTerminal(
|
|
17
|
+
<Screen title="Snapshot Demo">
|
|
18
|
+
<Text>Hello terminal</Text>
|
|
19
|
+
<Text>Status: ok</Text>
|
|
20
|
+
</Screen>
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
console.log(output);
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Input controlado con submit
|
|
27
|
+
|
|
28
|
+
Usalo cuando necesites un campo editable que guarde el valor final al presionar `Enter`.
|
|
29
|
+
|
|
30
|
+
```tsx
|
|
31
|
+
/** @jsx v */
|
|
32
|
+
/** @jsxFrag v.fragment */
|
|
33
|
+
|
|
34
|
+
import { v } from "valyrian.js";
|
|
35
|
+
import { Input, Screen, Text, mountTerminal } from "valyrianjs-terminal";
|
|
36
|
+
|
|
37
|
+
const state = { value: "", submitted: "" };
|
|
38
|
+
|
|
39
|
+
const session = mountTerminal(() => (
|
|
40
|
+
<Screen>
|
|
41
|
+
<Input
|
|
42
|
+
id="name"
|
|
43
|
+
value={state.value}
|
|
44
|
+
placeholder="Write a quick note"
|
|
45
|
+
onchange={(event) => {
|
|
46
|
+
state.value = event.value;
|
|
47
|
+
}}
|
|
48
|
+
onsubmit={(event) => {
|
|
49
|
+
state.submitted = event.value;
|
|
50
|
+
}}
|
|
51
|
+
/>
|
|
52
|
+
<Text>{state.submitted || "Nothing submitted yet"}</Text>
|
|
53
|
+
</Screen>
|
|
54
|
+
));
|
|
55
|
+
|
|
56
|
+
session.focus("name");
|
|
57
|
+
session.dispatchKey("H");
|
|
58
|
+
session.dispatchKey("i");
|
|
59
|
+
session.dispatchKey("ENTER");
|
|
60
|
+
|
|
61
|
+
console.log(state.submitted);
|
|
62
|
+
console.log(session.output());
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Boton que muta estado
|
|
66
|
+
|
|
67
|
+
Usalo cuando quieras activar acciones simples desde teclado o click programatico.
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
/** @jsx v */
|
|
71
|
+
/** @jsxFrag v.fragment */
|
|
72
|
+
|
|
73
|
+
import { v } from "valyrian.js";
|
|
74
|
+
import { Button, Screen, Text, mountTerminal } from "valyrianjs-terminal";
|
|
75
|
+
|
|
76
|
+
const state = { count: 0 };
|
|
77
|
+
|
|
78
|
+
const session = mountTerminal(() => (
|
|
79
|
+
<Screen>
|
|
80
|
+
<Button
|
|
81
|
+
id="increment"
|
|
82
|
+
onpress={() => {
|
|
83
|
+
state.count += 1;
|
|
84
|
+
}}
|
|
85
|
+
>
|
|
86
|
+
Increment
|
|
87
|
+
</Button>
|
|
88
|
+
<Text>{`Count: ${state.count}`}</Text>
|
|
89
|
+
</Screen>
|
|
90
|
+
));
|
|
91
|
+
|
|
92
|
+
session.click("increment");
|
|
93
|
+
session.click("increment");
|
|
94
|
+
|
|
95
|
+
console.log(session.output());
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Lista interactiva con `onchange` y `onpress`
|
|
99
|
+
|
|
100
|
+
Usalo cuando quieras separar el item actualmente seleccionado del item activado con `Enter`.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
/** @jsx v */
|
|
104
|
+
/** @jsxFrag v.fragment */
|
|
105
|
+
|
|
106
|
+
import { v } from "valyrian.js";
|
|
107
|
+
import { List, Screen, Text, mountTerminal } from "valyrianjs-terminal";
|
|
108
|
+
|
|
109
|
+
const state = { selected: "", opened: "" };
|
|
110
|
+
|
|
111
|
+
const session = mountTerminal(() => (
|
|
112
|
+
<Screen>
|
|
113
|
+
<List
|
|
114
|
+
id="menu"
|
|
115
|
+
items={["Inbox", "Today", "Done"]}
|
|
116
|
+
onchange={(event) => {
|
|
117
|
+
state.selected = event.value;
|
|
118
|
+
}}
|
|
119
|
+
onpress={(event) => {
|
|
120
|
+
state.opened = event.value;
|
|
121
|
+
}}
|
|
122
|
+
/>
|
|
123
|
+
<Text>{`Selected: ${state.selected || "-"}`}</Text>
|
|
124
|
+
<Text>{`Opened: ${state.opened || "-"}`}</Text>
|
|
125
|
+
</Screen>
|
|
126
|
+
));
|
|
127
|
+
|
|
128
|
+
session.focus("menu");
|
|
129
|
+
session.dispatchKey("DOWN");
|
|
130
|
+
session.dispatchKey("DOWN");
|
|
131
|
+
session.dispatchKey("ENTER");
|
|
132
|
+
|
|
133
|
+
console.log(state.selected);
|
|
134
|
+
console.log(state.opened);
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Scroll view con `height` y `highlightRows`
|
|
138
|
+
|
|
139
|
+
Usalo para recortar contenido vertical y marcar filas relevantes dentro del viewport.
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
/** @jsx v */
|
|
143
|
+
/** @jsxFrag v.fragment */
|
|
144
|
+
|
|
145
|
+
import { v } from "valyrian.js";
|
|
146
|
+
import { Screen, ScrollView, Text, mountTerminal } from "valyrianjs-terminal";
|
|
147
|
+
|
|
148
|
+
const session = mountTerminal(() => (
|
|
149
|
+
<Screen>
|
|
150
|
+
<ScrollView id="activity" height={3} highlightRows={[2]}>
|
|
151
|
+
<Text>Alpha</Text>
|
|
152
|
+
<Text>Beta</Text>
|
|
153
|
+
<Text>Gamma</Text>
|
|
154
|
+
<Text>Delta</Text>
|
|
155
|
+
</ScrollView>
|
|
156
|
+
</Screen>
|
|
157
|
+
), { ansi: true });
|
|
158
|
+
|
|
159
|
+
session.focus("activity");
|
|
160
|
+
console.log(session.output());
|
|
161
|
+
|
|
162
|
+
session.dispatchKey("DOWN");
|
|
163
|
+
console.log(session.output());
|
|
164
|
+
console.log(session.ansiOutput());
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## Clipboard adapter custom
|
|
168
|
+
|
|
169
|
+
Usalo cuando quieras conectar copiar y pegar con un portapapeles propio en lugar del estado interno de la sesion.
|
|
170
|
+
|
|
171
|
+
```tsx
|
|
172
|
+
/** @jsx v */
|
|
173
|
+
/** @jsxFrag v.fragment */
|
|
174
|
+
|
|
175
|
+
import { v } from "valyrian.js";
|
|
176
|
+
import { Input, Screen, mountTerminal } from "valyrianjs-terminal";
|
|
177
|
+
|
|
178
|
+
const state = { value: "abcd" };
|
|
179
|
+
const clipboard = {
|
|
180
|
+
value: "",
|
|
181
|
+
readText() {
|
|
182
|
+
return this.value;
|
|
183
|
+
},
|
|
184
|
+
writeText(value: string) {
|
|
185
|
+
this.value = value;
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const session = mountTerminal(() => (
|
|
190
|
+
<Screen>
|
|
191
|
+
<Input
|
|
192
|
+
id="name"
|
|
193
|
+
value={state.value}
|
|
194
|
+
onchange={(event) => {
|
|
195
|
+
state.value = event.value;
|
|
196
|
+
}}
|
|
197
|
+
/>
|
|
198
|
+
</Screen>
|
|
199
|
+
), { clipboard });
|
|
200
|
+
|
|
201
|
+
session.focus("name");
|
|
202
|
+
session.dispatchKey("HOME");
|
|
203
|
+
session.dispatchKey("SHIFT_RIGHT");
|
|
204
|
+
session.dispatchKey("SHIFT_RIGHT");
|
|
205
|
+
session.dispatchKey("CTRL_C");
|
|
206
|
+
|
|
207
|
+
clipboard.value = "XY";
|
|
208
|
+
session.dispatchKey("END");
|
|
209
|
+
session.dispatchKey("CTRL_V");
|
|
210
|
+
|
|
211
|
+
console.log(session.clipboard());
|
|
212
|
+
console.log(state.value);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Interaccion por coordenadas con `focusAt` y `clickAt`
|
|
216
|
+
|
|
217
|
+
Usalo en pruebas o adapters donde el punto de entrada real son coordenadas del frame y no ids conocidos.
|
|
218
|
+
|
|
219
|
+
```tsx
|
|
220
|
+
/** @jsx v */
|
|
221
|
+
/** @jsxFrag v.fragment */
|
|
222
|
+
|
|
223
|
+
import { v } from "valyrian.js";
|
|
224
|
+
import { Button, Input, Screen, mountTerminal } from "valyrianjs-terminal";
|
|
225
|
+
|
|
226
|
+
const state = { clicks: 0 };
|
|
227
|
+
|
|
228
|
+
const session = mountTerminal(() => (
|
|
229
|
+
<Screen>
|
|
230
|
+
<Input id="name" value="abc" />
|
|
231
|
+
<Button id="save" onpress={() => {
|
|
232
|
+
state.clicks += 1;
|
|
233
|
+
}}>
|
|
234
|
+
Save
|
|
235
|
+
</Button>
|
|
236
|
+
</Screen>
|
|
237
|
+
));
|
|
238
|
+
|
|
239
|
+
session.focusAt(2, 1);
|
|
240
|
+
session.clickAt(3, 2);
|
|
241
|
+
|
|
242
|
+
console.log(session.output());
|
|
243
|
+
console.log(state.clicks);
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
## Inspeccion de salida ANSI con `ansiOutput`
|
|
247
|
+
|
|
248
|
+
Usalo cuando necesites verificar cursor, estilos o secuencias ANSI en pruebas e integraciones.
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
/** @jsx v */
|
|
252
|
+
/** @jsxFrag v.fragment */
|
|
253
|
+
|
|
254
|
+
import { v } from "valyrian.js";
|
|
255
|
+
import { Input, Screen, mountTerminal } from "valyrianjs-terminal";
|
|
256
|
+
|
|
257
|
+
const state = { value: "AB" };
|
|
258
|
+
|
|
259
|
+
const session = mountTerminal(() => (
|
|
260
|
+
<Screen title="ANSI Demo">
|
|
261
|
+
<Input
|
|
262
|
+
id="name"
|
|
263
|
+
value={state.value}
|
|
264
|
+
onchange={(event) => {
|
|
265
|
+
state.value = event.value;
|
|
266
|
+
}}
|
|
267
|
+
/>
|
|
268
|
+
</Screen>
|
|
269
|
+
), { ansi: true });
|
|
270
|
+
|
|
271
|
+
session.focus("name");
|
|
272
|
+
session.dispatchKey("LEFT");
|
|
273
|
+
|
|
274
|
+
const ansi = session.ansiOutput();
|
|
275
|
+
|
|
276
|
+
console.log(ansi);
|
|
277
|
+
console.log(/\u001b\[[0-9]+;[0-9]+H/.test(ansi));
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
## Ver tambien
|
|
281
|
+
|
|
282
|
+
- `docs/getting-started.md` para el flujo minimo de montaje
|
|
283
|
+
- `docs/core-concepts.md` para el modelo mental del paquete
|
|
284
|
+
- `docs/interaction-model.md` para foco, teclado, mouse y clipboard
|
|
285
|
+
- `docs/session-runtime.md` para lifecycle, streams y runtime de sesion
|
|
286
|
+
- `docs/api-reference.md` para la superficie publica completa
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Core Concepts
|
|
2
|
+
|
|
3
|
+
Esta guia explica el modelo mental del paquete antes de entrar al detalle de runtime o referencia.
|
|
4
|
+
|
|
5
|
+
## 1. Primitivas terminal-first
|
|
6
|
+
|
|
7
|
+
`valyrianjs-terminal` no renderiza HTML ni componentes de navegador. Sus primitivas generan nodos terminales propios que luego se convierten en texto plano o en un frame ANSI.
|
|
8
|
+
|
|
9
|
+
Las piezas base se agrupan asi:
|
|
10
|
+
|
|
11
|
+
- layout: `Screen`, `Box`, `View`, `Text`, `Table`, `Row`, `Td`
|
|
12
|
+
- interaccion: `Input`, `Button`, `List`, `ScrollView`
|
|
13
|
+
|
|
14
|
+
Piensalo como un DSL de interfaces de texto, no como una adaptacion visual del DOM.
|
|
15
|
+
|
|
16
|
+
## 2. Dos modos de trabajo
|
|
17
|
+
|
|
18
|
+
Hay dos entrypoints principales:
|
|
19
|
+
|
|
20
|
+
- `renderTerminal()` para generar texto plano a partir de un arbol terminal
|
|
21
|
+
- `mountTerminal()` para crear una sesion interactiva con foco, teclado, mouse y escritura opcional a streams
|
|
22
|
+
|
|
23
|
+
Regla simple:
|
|
24
|
+
|
|
25
|
+
- si solo quieres inspeccionar contenido, usa `renderTerminal()`
|
|
26
|
+
- si necesitas interaccion o runtime vivo, usa `mountTerminal()`
|
|
27
|
+
|
|
28
|
+
## 3. El estado vive fuera de la libreria
|
|
29
|
+
|
|
30
|
+
La libreria renderiza lo que tu funcion de UI devuelve con el estado actual. No guarda un store de aplicacion por ti.
|
|
31
|
+
|
|
32
|
+
Eso significa que:
|
|
33
|
+
|
|
34
|
+
- tus handlers mutan estado externo
|
|
35
|
+
- `mountTerminal()` vuelve a evaluar la funcion de render
|
|
36
|
+
- `session.update()` solo hace falta cuando mutaste estado fuera de un handler que ya detona rerender
|
|
37
|
+
|
|
38
|
+
## 4. `id` es la llave de la interaccion
|
|
39
|
+
|
|
40
|
+
Los nodos interactivos pueden renderizarse sin `id`, pero pierdes gran parte del control programatico.
|
|
41
|
+
|
|
42
|
+
Necesitas `id` estable si quieres:
|
|
43
|
+
|
|
44
|
+
- enfocar con `session.focus(id)`
|
|
45
|
+
- activar con `session.click(id)`
|
|
46
|
+
- participar de hitboxes por coordenadas
|
|
47
|
+
- recibir bien flujos de foco y navegacion
|
|
48
|
+
|
|
49
|
+
Recomendacion practica: da `id` a todo `Input`, `Button`, `List` y `ScrollView` que vaya a vivir mas de una prueba trivial.
|
|
50
|
+
|
|
51
|
+
## 5. Foco e hitboxes
|
|
52
|
+
|
|
53
|
+
La sesion calcula hitboxes a partir del frame renderizado actual. Eso permite dos clases de interaccion:
|
|
54
|
+
|
|
55
|
+
- por identificador: `focus(id)`, `click(id)`
|
|
56
|
+
- por coordenadas: `focusAt(x, y)`, `clickAt(x, y)`
|
|
57
|
+
|
|
58
|
+
El foco secuencial tambien sale del arbol actual:
|
|
59
|
+
|
|
60
|
+
- `focusNext()`
|
|
61
|
+
- `focusPrev()`
|
|
62
|
+
- `dispatchKey("TAB")`
|
|
63
|
+
- `dispatchKey("SHIFT_TAB")`
|
|
64
|
+
|
|
65
|
+
Si cambia el arbol, cambia tambien el orden y la geometria del foco.
|
|
66
|
+
|
|
67
|
+
## 6. Salida plana vs salida ANSI
|
|
68
|
+
|
|
69
|
+
El paquete maneja dos representaciones utiles:
|
|
70
|
+
|
|
71
|
+
- salida plana: la devuelven `renderTerminal()` y `session.output()`
|
|
72
|
+
- salida ANSI: la devuelve `session.ansiOutput()` y, si activas `ansi: true`, tambien se escribe a `stdout`
|
|
73
|
+
|
|
74
|
+
Usa salida plana para snapshots y pruebas. Usa ANSI cuando necesitas cursor, spans visuales, foco o integracion con una terminal real.
|
|
75
|
+
|
|
76
|
+
## 7. Streams y cleanup
|
|
77
|
+
|
|
78
|
+
`mountTerminal()` puede trabajar sin streams, pero si conectas `stdin` y `stdout` la sesion entra en modo runtime real.
|
|
79
|
+
|
|
80
|
+
Cuando montas con `stdin`:
|
|
81
|
+
|
|
82
|
+
- la sesion escucha `data`
|
|
83
|
+
- intenta activar raw mode si el stream lo soporta
|
|
84
|
+
- intenta restaurarlo al llamar `destroy()`
|
|
85
|
+
|
|
86
|
+
Por eso `destroy()` no es opcional cuando conectaste streams reales.
|
|
87
|
+
|
|
88
|
+
## 8. Donde seguir
|
|
89
|
+
|
|
90
|
+
- `docs/interaction-model.md` explica que hace cada primitiva interactiva
|
|
91
|
+
- `docs/session-runtime.md` explica lifecycle, streams, clipboard, coordenadas y mouse
|
|
92
|
+
- `docs/cookbook.md` muestra recetas cortas para tareas comunes
|
|
93
|
+
- `docs/api-reference.md` sirve como consulta puntual
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
Esta guia cubre el happy path minimo para empezar con `valyrianjs-terminal`.
|
|
4
|
+
|
|
5
|
+
## 1. Instala los paquetes
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install valyrianjs-terminal valyrian.js
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## 2. Empieza con render estatico
|
|
12
|
+
|
|
13
|
+
Primero valida layout y contenido sin streams ni runtime interactivo.
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
/** @jsx v */
|
|
17
|
+
/** @jsxFrag v.fragment */
|
|
18
|
+
|
|
19
|
+
import { v } from "valyrian.js";
|
|
20
|
+
import { Screen, Text, renderTerminal } from "valyrianjs-terminal";
|
|
21
|
+
|
|
22
|
+
const output = renderTerminal(
|
|
23
|
+
<Screen title="Status">
|
|
24
|
+
<Text>Hello terminal</Text>
|
|
25
|
+
<Text>Status: ok</Text>
|
|
26
|
+
</Screen>
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
console.log(output);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Usa este camino cuando quieras:
|
|
33
|
+
|
|
34
|
+
- generar snapshots
|
|
35
|
+
- validar layout
|
|
36
|
+
- probar salida plana sin conectar `stdin` ni `stdout`
|
|
37
|
+
|
|
38
|
+
## 3. Sube a una sesion interactiva minima
|
|
39
|
+
|
|
40
|
+
Cuando ya necesitas editar texto, manejar foco o reaccionar a teclado, cambia a `mountTerminal()`.
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
/** @jsx v */
|
|
44
|
+
/** @jsxFrag v.fragment */
|
|
45
|
+
|
|
46
|
+
import { v } from "valyrian.js";
|
|
47
|
+
import { Button, Input, Screen, Text, mountTerminal } from "valyrianjs-terminal";
|
|
48
|
+
|
|
49
|
+
const state = { value: "", saved: "" };
|
|
50
|
+
|
|
51
|
+
const session = mountTerminal(() => (
|
|
52
|
+
<Screen title="Quick Note">
|
|
53
|
+
<Input
|
|
54
|
+
id="note"
|
|
55
|
+
value={state.value}
|
|
56
|
+
placeholder="Write a quick note"
|
|
57
|
+
onchange={(event) => {
|
|
58
|
+
state.value = event.value;
|
|
59
|
+
}}
|
|
60
|
+
onsubmit={(event) => {
|
|
61
|
+
state.saved = event.value;
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
<Button
|
|
65
|
+
id="save"
|
|
66
|
+
onpress={() => {
|
|
67
|
+
state.saved = state.value;
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
Save
|
|
71
|
+
</Button>
|
|
72
|
+
<Text>{state.saved || "Nothing saved yet"}</Text>
|
|
73
|
+
</Screen>
|
|
74
|
+
));
|
|
75
|
+
|
|
76
|
+
session.focus("note");
|
|
77
|
+
session.dispatchKey("H");
|
|
78
|
+
session.dispatchKey("i");
|
|
79
|
+
|
|
80
|
+
console.log(session.output());
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Regla practica: da `id` estables a `Input`, `Button`, `List` y `ScrollView` si quieres usar foco, coordenadas o interaccion programatica.
|
|
84
|
+
|
|
85
|
+
## 4. Entiende el lifecycle minimo
|
|
86
|
+
|
|
87
|
+
La sesion no guarda tu estado de negocio; vuelve a evaluar la funcion de render con el estado externo actual.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
/** @jsx v */
|
|
91
|
+
/** @jsxFrag v.fragment */
|
|
92
|
+
|
|
93
|
+
import { v } from "valyrian.js";
|
|
94
|
+
import { Screen, Text, mountTerminal } from "valyrianjs-terminal";
|
|
95
|
+
|
|
96
|
+
const state = { count: 0 };
|
|
97
|
+
|
|
98
|
+
const session = mountTerminal(() => (
|
|
99
|
+
<Screen>
|
|
100
|
+
<Text>Count: {state.count}</Text>
|
|
101
|
+
</Screen>
|
|
102
|
+
));
|
|
103
|
+
|
|
104
|
+
state.count += 1;
|
|
105
|
+
session.update();
|
|
106
|
+
|
|
107
|
+
console.log(session.output());
|
|
108
|
+
session.destroy();
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Resumen rapido:
|
|
112
|
+
|
|
113
|
+
- `output()` devuelve el frame actual en texto plano
|
|
114
|
+
- `ansiOutput()` devuelve el frame actual como ANSI completo
|
|
115
|
+
- `update()` reevalua la funcion de render
|
|
116
|
+
- `destroy()` desmonta listeners y limpia `stdin` cuando aplica
|
|
117
|
+
|
|
118
|
+
## 5. Cuando conectar streams
|
|
119
|
+
|
|
120
|
+
Conecta `stdin`, `stdout` y `ansi: true` solo cuando ya vas a correr una UI real en terminal.
|
|
121
|
+
|
|
122
|
+
```tsx
|
|
123
|
+
/** @jsx v */
|
|
124
|
+
/** @jsxFrag v.fragment */
|
|
125
|
+
|
|
126
|
+
import { v } from "valyrian.js";
|
|
127
|
+
import { Screen, Text, mountTerminal } from "valyrianjs-terminal";
|
|
128
|
+
|
|
129
|
+
const session = mountTerminal(() => (
|
|
130
|
+
<Screen title="Live App">
|
|
131
|
+
<Text>Streaming to the terminal</Text>
|
|
132
|
+
</Screen>
|
|
133
|
+
), {
|
|
134
|
+
stdin: process.stdin,
|
|
135
|
+
stdout: process.stdout,
|
|
136
|
+
ansi: true
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Para pruebas o snapshots, normalmente basta con `renderTerminal()` o `mountTerminal()` sin streams.
|
|
141
|
+
|
|
142
|
+
## Siguiente lectura
|
|
143
|
+
|
|
144
|
+
- `docs/core-concepts.md`
|
|
145
|
+
- `docs/interaction-model.md`
|
|
146
|
+
- `docs/session-runtime.md`
|
|
147
|
+
- `docs/cookbook.md`
|
|
148
|
+
- `docs/api-reference.md`
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Interaction Model
|
|
2
|
+
|
|
3
|
+
Esta guia describe como responden las primitivas interactivas del paquete. Si todavia no tienes claro el modelo base, empieza por `docs/core-concepts.md`. Si necesitas lifecycle, streams o runtime de sesion, sigue con `docs/session-runtime.md`.
|
|
4
|
+
|
|
5
|
+
## Reglas compartidas
|
|
6
|
+
|
|
7
|
+
- `Tab` avanza al siguiente elemento enfocable.
|
|
8
|
+
- `Shift+Tab` regresa al anterior.
|
|
9
|
+
- Los nodos interactivos necesitan `id` si quieres enfocarlos o activarlos programaticamente.
|
|
10
|
+
- `focusAt(x, y)` y `clickAt(x, y)` dependen de los hitboxes del frame actual.
|
|
11
|
+
|
|
12
|
+
## `Input`
|
|
13
|
+
|
|
14
|
+
`Input` es una primitiva de edicion de una sola linea con cursor, seleccion y submit.
|
|
15
|
+
|
|
16
|
+
Comportamiento observable:
|
|
17
|
+
|
|
18
|
+
- texto normal: inserta caracteres
|
|
19
|
+
- `Enter`: dispara `onsubmit`
|
|
20
|
+
- `Left` / `Right`: mueve cursor
|
|
21
|
+
- `Shift+Left` / `Shift+Right`: extiende seleccion
|
|
22
|
+
- `Alt+Left` / `Alt+Right`: navega por palabra
|
|
23
|
+
- `Home` / `End`: salta al inicio o final
|
|
24
|
+
- `Ctrl+A`: selecciona todo
|
|
25
|
+
- `Ctrl+C`: copia seleccion
|
|
26
|
+
- `Ctrl+X`: corta seleccion
|
|
27
|
+
- `Ctrl+V`: pega desde clipboard
|
|
28
|
+
- `Backspace` / `Delete`: elimina contenido
|
|
29
|
+
|
|
30
|
+
Handlers principales:
|
|
31
|
+
|
|
32
|
+
- `onchange`
|
|
33
|
+
- `oninput`
|
|
34
|
+
- `onsubmit`
|
|
35
|
+
- `onChangeText`
|
|
36
|
+
|
|
37
|
+
## `Button`
|
|
38
|
+
|
|
39
|
+
`Button` representa una accion discreta.
|
|
40
|
+
|
|
41
|
+
Comportamiento observable:
|
|
42
|
+
|
|
43
|
+
- responde a `Enter` y `Space` cuando tiene foco
|
|
44
|
+
- responde a `session.click(id)`
|
|
45
|
+
- responde a `session.clickAt(x, y)` cuando el hitbox cae en el boton
|
|
46
|
+
|
|
47
|
+
Handlers principales:
|
|
48
|
+
|
|
49
|
+
- `onpress`
|
|
50
|
+
- `onclick`
|
|
51
|
+
- `action`
|
|
52
|
+
- `onPress`
|
|
53
|
+
|
|
54
|
+
## `List`
|
|
55
|
+
|
|
56
|
+
`List` modela seleccion y activacion por fila.
|
|
57
|
+
|
|
58
|
+
Comportamiento observable:
|
|
59
|
+
|
|
60
|
+
- `Up` / `Left`: mueve seleccion hacia arriba
|
|
61
|
+
- `Down` / `Right`: mueve seleccion hacia abajo
|
|
62
|
+
- `Enter`: dispara `onpress`
|
|
63
|
+
- el hover por mouse expone fila, indice y valor actual
|
|
64
|
+
|
|
65
|
+
Handlers principales:
|
|
66
|
+
|
|
67
|
+
- `onchange`
|
|
68
|
+
- `onpress`
|
|
69
|
+
- `onhover`
|
|
70
|
+
- `onrowenter`
|
|
71
|
+
- `onrowleave`
|
|
72
|
+
- `oncapturestart`
|
|
73
|
+
- `oncaptureend`
|
|
74
|
+
|
|
75
|
+
Con `pointerCapture`, la lista conserva la interaccion durante drag aunque el puntero salga del hitbox inicial.
|
|
76
|
+
|
|
77
|
+
## `ScrollView`
|
|
78
|
+
|
|
79
|
+
`ScrollView` recorta contenido vertical y expone interaccion por viewport.
|
|
80
|
+
|
|
81
|
+
Comportamiento observable:
|
|
82
|
+
|
|
83
|
+
- `Up`: desplaza hacia arriba
|
|
84
|
+
- `Down`: desplaza hacia abajo
|
|
85
|
+
- `height`: define el viewport visible
|
|
86
|
+
- `highlightRows`: resalta filas visibles concretas
|
|
87
|
+
- el hover por mouse expone fila visible y texto renderizado
|
|
88
|
+
|
|
89
|
+
Handlers principales:
|
|
90
|
+
|
|
91
|
+
- `onhover`
|
|
92
|
+
- `onrowenter`
|
|
93
|
+
- `onrowleave`
|
|
94
|
+
- `oncapturestart`
|
|
95
|
+
- `oncaptureend`
|
|
96
|
+
|
|
97
|
+
Con `pointerCapture`, el scroll mantiene la interaccion durante drag aunque el puntero salga del viewport.
|
|
98
|
+
|
|
99
|
+
## Mouse y pointer capture
|
|
100
|
+
|
|
101
|
+
Cuando la sesion recibe eventos SGR de mouse:
|
|
102
|
+
|
|
103
|
+
- `press`: enfoca y activa el hitbox correspondiente
|
|
104
|
+
- `drag`: extiende seleccion en `Input` o actualiza hover en `List` y `ScrollView`
|
|
105
|
+
- `release`: cierra captura y limpia hover cuando corresponde
|
|
106
|
+
- `wheel-up` / `wheel-down`: se traducen a navegacion vertical
|
|
107
|
+
|
|
108
|
+
`pointerCapture` solo aplica hoy a `List` y `ScrollView`.
|
|
109
|
+
|
|
110
|
+
## Donde seguir
|
|
111
|
+
|
|
112
|
+
- `docs/core-concepts.md` para el modelo mental del paquete
|
|
113
|
+
- `docs/session-runtime.md` para lifecycle, streams, clipboard y runtime
|
|
114
|
+
- `docs/cookbook.md` para recetas practicas
|
|
115
|
+
- `docs/api-reference.md` para consulta puntual de props, payloads y tipos
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Local Demo
|
|
2
|
+
|
|
3
|
+
`examples/` existe solo como apoyo de desarrollo local. No forma parte del paquete publicado por npm, no aparece en `exports` y no hay CLI publicada por `valyrianjs-terminal`.
|
|
4
|
+
|
|
5
|
+
## Archivos
|
|
6
|
+
|
|
7
|
+
- `examples/demo.tsx`: define `createDemoState()` y `DemoApp`; valida composicion de primitivas (`Screen`, `Box`, `View`, `Text`, `Input`, `Button`, `List`, `ScrollView`, `Table`, `Row`, `Td`) y payloads basicos de eventos.
|
|
8
|
+
- `examples/basic.tsx`: valida el camino minimo para usar `renderTerminal()` en snapshot y `mountTerminal()` con `stdin`/`stdout` en local.
|
|
9
|
+
- `examples/cli.tsx`: valida el launcher local con `--help`, `--snapshot`, foco inicial, salida por `Esc` o `Ctrl+C` y cleanup ANSI al cerrar.
|
|
10
|
+
|
|
11
|
+
## Comandos
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
bun run examples/basic.tsx
|
|
15
|
+
bun run examples/basic.tsx --snapshot
|
|
16
|
+
bun run examples/cli.tsx
|
|
17
|
+
bun run examples/cli.tsx --snapshot
|
|
18
|
+
bun run examples/cli.tsx --help
|
|
19
|
+
bun run test
|
|
20
|
+
bun run typecheck
|
|
21
|
+
bun run build
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Alcance
|
|
25
|
+
|
|
26
|
+
- `--snapshot` imprime la UI de ejemplo como texto plano.
|
|
27
|
+
- sin `--snapshot`, los ejemplos montan una sesion local conectada a `process.stdin` y `process.stdout`.
|
|
28
|
+
- estos archivos no son una interfaz distribuida a consumidores del paquete; son fixtures manuales para validar el adapter en la maquina local.
|