Skip to content

Commit 10c686f

Browse files
committed
📃update README
1 parent f0b508b commit 10c686f

3 files changed

Lines changed: 97 additions & 18 deletions

File tree

README.md

Lines changed: 97 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,48 @@
11
# clayterm
22

3-
A terminal rendering backend for [Clay](https://github.com/nicbarker/clay), and
4-
a terminal input event parser compiled to WebAssembly.
3+
A low-level, platform-independent terminal renderer and event parser for JavaScript. You can use clayterm directly, or as the foundation for your own framework.
4+
5+
## Features
6+
7+
**Declarative terminal UI** — Build terminal interfaces the same way you'd
8+
build a web page. Clayterm uses [Clay](https://github.com/nicbarker/clay) under
9+
the hood, giving you flexbox-like layout, pointer detection, and scroll
10+
containers — all rendered to the terminal as box-drawing characters and ANSI
11+
escape sequences.
12+
13+
**Zero I/O** — Clayterm never reads stdin or writes stdout. You feed it bytes
14+
and get bytes back. This makes it trivially embeddable in any framework, any
15+
runtime, any event loop. There are no opinions about how you do I/O, just pure
16+
computation.
17+
18+
**Runs everywhere** — The entire engine is compiled to WebAssembly, so
19+
clayterm will run anywhere JavaScript runs with no native
20+
dependencies, and no build step for consumers.
21+
22+
### Demo
23+
24+
The application in this demo uses Clayterm for all layout and input parsing
25+
26+
#### Keyboard Events
27+
28+
The input parser decodes raw terminal bytes into structured events.
29+
Here you can see each key event as the string "hello world" is typed.
30+
31+
![Keyboard events demo](demo/keyboard-key-events.gif)
32+
33+
#### Pointer Events
34+
35+
Here we see hover styles applied to UI elements in response to the
36+
pointer state. Clay drives the hit testing; no manual coordinate math
37+
required.
38+
39+
![Pointer events demo](demo/keyboard-pointer-events.gif)
40+
541

642
## Architecture
743

44+
Clayterm does not do any I/O itself. On the ouput side, it converts UI elements into a raw sequence of bytes and pointer events, and on the input side, it converts a stream of raw bytes into structured events.
45+
846
### Output
947

1048
With every frame, the entire UI tree is packed into a flat byte array and sent
@@ -54,36 +92,41 @@ multi-byte sequences time to arrive.
5492
| |
5593
+---------------+ | |
5694
| | events[] | |
57-
| CharEvent | <============= | |
58-
| KeyEvent | | |
59-
| MouseEvent | +---------------------------+
60-
| DragEvent |
95+
| KeyEvent | <============= | |
96+
| MouseDownEvent| | |
97+
| MouseUpEvent | +---------------------------+
98+
| MouseMoveEvent|
6199
| WheelEvent |
62100
| ResizeEvent |
63101
+---------------+
64102
```
65103

66104
## Usage
67105

68-
### Output
106+
### Rendering
107+
108+
To render this:
109+
110+
```
111+
╭───────────────╮
112+
│ Hello, World! │
113+
╰───────────────╯
114+
```
69115

70116
```typescript
71117
import { close, createTerm, grow, open, rgba, text } from "clayterm";
72118

73-
const term = await createTerm({ width: 80, height: 24 });
119+
let term = await createTerm({ width: 80, height: 24 });
74120

75-
const ansi = term.render([
121+
let { output } = term.render([
76122
open("root", {
77123
layout: { width: grow(), height: grow(), direction: "ttb" },
78124
}),
79-
open("box", {
80-
layout: { padding: { left: 2, top: 1 } },
125+
open("greeting", {
126+
layout: { padding: { left: 1, right: 1 } },
81127
border: {
82128
color: rgba(0, 255, 0),
83-
left: 1,
84-
right: 1,
85-
top: 1,
86-
bottom: 1,
129+
left: 1, right: 1, top: 1, bottom: 1,
87130
},
88131
cornerRadius: { tl: 1, tr: 1, bl: 1, br: 1 },
89132
}),
@@ -92,15 +135,51 @@ const ansi = term.render([
92135
close(),
93136
]);
94137

95-
process.stdout.write(ansi);
138+
process.stdout.write(output);
96139
```
97140

98-
### Input
141+
### Pointer detection
142+
143+
Pass pointer state to `render()` to have clayterm do hit detection and return
144+
pointer events in addition to the byte sequence.
145+
146+
```typescript
147+
let { output, events } = term.render([
148+
open("root", {
149+
layout: { width: grow(), height: grow(), direction: "ltr" },
150+
}),
151+
open("sidebar", {
152+
layout: { width: fixed(20), height: grow() },
153+
bg: rgba(30, 30, 40),
154+
}),
155+
text("Sidebar"),
156+
close(),
157+
open("main", {
158+
layout: { width: grow(), height: grow() },
159+
}),
160+
text("Main content"),
161+
close(),
162+
close(),
163+
], {
164+
pointer: { x: mouseX, y: mouseY, down: mouseDown },
165+
});
166+
167+
for (let event of events) {
168+
// { type: "pointerenter", id: "sidebar" }
169+
// { type: "pointerleave", id: "sidebar" }
170+
// { type: "pointerclick", id: "main" }
171+
console.log(event);
172+
}
173+
174+
process.stdout.write(output);
175+
```
176+
177+
### Input parsing
99178

100179
```typescript
101180
import { createInput } from "clayterm/input";
102181

103-
const input = await createInput({ escLatency: 25 });
182+
let input = await createInput({ escLatency: 25 });
104183

105184
process.stdin.setRawMode(true);
106185
let timer: ReturnType<typeof setTimeout> | undefined;

demo/keyboard-key-events.gif

713 KB
Loading

demo/keyboard-pointer-events.gif

1.03 MB
Loading

0 commit comments

Comments
 (0)