@suds-cli/tea 0.0.0 → 0.1.0-alpha.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 +260 -0
- package/dist/index.cjs +1125 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +337 -0
- package/dist/index.d.ts +337 -12
- package/dist/index.js +1084 -51
- package/dist/index.js.map +1 -1
- package/package.json +30 -18
- package/dist/commands.d.ts +0 -27
- package/dist/commands.d.ts.map +0 -1
- package/dist/commands.js +0 -106
- package/dist/commands.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/input.d.ts +0 -8
- package/dist/input.d.ts.map +0 -1
- package/dist/input.js +0 -98
- package/dist/input.js.map +0 -1
- package/dist/keys.d.ts +0 -86
- package/dist/keys.d.ts.map +0 -1
- package/dist/keys.js +0 -317
- package/dist/keys.js.map +0 -1
- package/dist/messages.d.ts +0 -78
- package/dist/messages.d.ts.map +0 -1
- package/dist/messages.js +0 -104
- package/dist/messages.js.map +0 -1
- package/dist/mouse.d.ts +0 -46
- package/dist/mouse.d.ts.map +0 -1
- package/dist/mouse.js +0 -167
- package/dist/mouse.js.map +0 -1
- package/dist/program.d.ts +0 -39
- package/dist/program.d.ts.map +0 -1
- package/dist/program.js +0 -231
- package/dist/program.js.map +0 -1
- package/dist/renderer.d.ts +0 -19
- package/dist/renderer.d.ts.map +0 -1
- package/dist/renderer.js +0 -49
- package/dist/renderer.js.map +0 -1
- package/dist/screen.d.ts +0 -22
- package/dist/screen.d.ts.map +0 -1
- package/dist/screen.js +0 -35
- package/dist/screen.js.map +0 -1
- package/dist/terminal.d.ts +0 -32
- package/dist/terminal.d.ts.map +0 -1
- package/dist/terminal.js +0 -119
- package/dist/terminal.js.map +0 -1
- package/dist/types.d.ts +0 -36
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -3
- package/dist/types.js.map +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# @suds-cli/tea
|
|
2
|
+
|
|
3
|
+
TypeScript port of [Bubble Tea](https://github.com/charmbracelet/bubbletea), the Elm-inspired terminal UI framework from Charm. Build interactive CLIs using a simple, functional architecture: Model-Update-View.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @suds-cli/tea
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quickstart
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createNodePlatform } from '@suds-cli/machine/node'
|
|
15
|
+
import {
|
|
16
|
+
Program,
|
|
17
|
+
quit,
|
|
18
|
+
KeyMsg,
|
|
19
|
+
KeyType,
|
|
20
|
+
type Model,
|
|
21
|
+
type Cmd,
|
|
22
|
+
type Msg,
|
|
23
|
+
} from '@suds-cli/tea'
|
|
24
|
+
|
|
25
|
+
// Define your message types
|
|
26
|
+
type AppMsg = Msg | { _tag: 'increment' } | { _tag: 'decrement' }
|
|
27
|
+
|
|
28
|
+
// Create a model implementing the Model interface
|
|
29
|
+
class Counter implements Model<AppMsg, Counter> {
|
|
30
|
+
constructor(public readonly count: number = 0) {}
|
|
31
|
+
|
|
32
|
+
init(): Cmd<AppMsg> {
|
|
33
|
+
return null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
update(msg: AppMsg): [Counter, Cmd<AppMsg>] {
|
|
37
|
+
if (msg instanceof KeyMsg) {
|
|
38
|
+
switch (msg.key.type) {
|
|
39
|
+
case KeyType.Up:
|
|
40
|
+
return [new Counter(this.count + 1), null]
|
|
41
|
+
case KeyType.Down:
|
|
42
|
+
return [new Counter(this.count - 1), null]
|
|
43
|
+
case KeyType.Esc:
|
|
44
|
+
return [this, quit()]
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return [this, null]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
view(): string {
|
|
51
|
+
return `Count: ${this.count}\n\nPress ↑/↓ to change, Esc to quit`
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Create platform adapter and run the program
|
|
56
|
+
const platform = createNodePlatform()
|
|
57
|
+
const program = new Program(new Counter(), {
|
|
58
|
+
platform,
|
|
59
|
+
altScreen: true,
|
|
60
|
+
})
|
|
61
|
+
await program.run()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Architecture
|
|
65
|
+
|
|
66
|
+
Tea follows the [Elm Architecture](https://guide.elm-lang.org/architecture/):
|
|
67
|
+
|
|
68
|
+
- **Model** — Your application state, implementing `init()`, `update()`, and `view()`
|
|
69
|
+
- **Msg** — Discriminated union of messages that trigger state changes
|
|
70
|
+
- **Cmd** — Async side effects that produce messages
|
|
71
|
+
- **Program** — Runtime that orchestrates the event loop
|
|
72
|
+
|
|
73
|
+
### Messages
|
|
74
|
+
|
|
75
|
+
All messages must include a `_tag` discriminant for type-safe matching:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
type MyMsg =
|
|
79
|
+
| { _tag: 'tick'; time: Date }
|
|
80
|
+
| { _tag: 'data-loaded'; items: string[] }
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Commands
|
|
84
|
+
|
|
85
|
+
Commands are async functions that return messages. Use the built-in helpers:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import { batch, sequence, tick, every, msg, quit } from '@suds-cli/tea'
|
|
89
|
+
|
|
90
|
+
// Lift a value into a command
|
|
91
|
+
const notify = msg({ _tag: 'notify', text: 'Hello' })
|
|
92
|
+
|
|
93
|
+
// Emit after delay
|
|
94
|
+
const delayed = tick(1000, (time) => ({ _tag: 'tick', time }))
|
|
95
|
+
|
|
96
|
+
// Emit aligned to interval boundary (for clocks, animations)
|
|
97
|
+
const interval = every(1000, (time) => ({ _tag: 'tick', time }))
|
|
98
|
+
|
|
99
|
+
// Run commands concurrently
|
|
100
|
+
const parallel = batch(cmd1, cmd2, cmd3)
|
|
101
|
+
|
|
102
|
+
// Run commands sequentially
|
|
103
|
+
const sequential = sequence(cmd1, cmd2, cmd3)
|
|
104
|
+
|
|
105
|
+
// Graceful exit
|
|
106
|
+
const exit = quit()
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Input Handling
|
|
110
|
+
|
|
111
|
+
### Keyboard
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { KeyMsg, KeyType, keyToString } from "@suds-cli/tea";
|
|
115
|
+
|
|
116
|
+
update(msg: Msg): [Model, Cmd<Msg>] {
|
|
117
|
+
if (msg instanceof KeyMsg) {
|
|
118
|
+
const { key } = msg;
|
|
119
|
+
|
|
120
|
+
// Check key type
|
|
121
|
+
if (key.type === KeyType.Enter) { /* ... */ }
|
|
122
|
+
if (key.type === KeyType.Tab) { /* ... */ }
|
|
123
|
+
if (key.type === KeyType.Up) { /* ... */ }
|
|
124
|
+
|
|
125
|
+
// Check for character input
|
|
126
|
+
if (key.type === KeyType.Runes) {
|
|
127
|
+
console.log(key.runes); // The typed character(s)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Check modifiers
|
|
131
|
+
if (key.alt) { /* Alt+key pressed */ }
|
|
132
|
+
if (key.paste) { /* Pasted text */ }
|
|
133
|
+
|
|
134
|
+
// Get human-readable representation
|
|
135
|
+
console.log(keyToString(key)); // "ctrl+c", "alt+enter", "a"
|
|
136
|
+
}
|
|
137
|
+
return [this, null];
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Mouse
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
import { MouseMsg, MouseAction, MouseButton } from "@suds-cli/tea";
|
|
145
|
+
|
|
146
|
+
update(msg: Msg): [Model, Cmd<Msg>] {
|
|
147
|
+
if (msg instanceof MouseMsg) {
|
|
148
|
+
const { event } = msg;
|
|
149
|
+
|
|
150
|
+
if (event.action === MouseAction.Press && event.button === MouseButton.Left) {
|
|
151
|
+
// Left click at event.x, event.y
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (event.button === MouseButton.WheelUp) {
|
|
155
|
+
// Scroll up
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Modifier keys
|
|
159
|
+
if (event.ctrl || event.alt || event.shift) { /* ... */ }
|
|
160
|
+
}
|
|
161
|
+
return [this, null];
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Enable mouse with program options:
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { createNodePlatform } from '@suds-cli/machine/node'
|
|
169
|
+
|
|
170
|
+
const platform = createNodePlatform()
|
|
171
|
+
const program = new Program(model, {
|
|
172
|
+
platform,
|
|
173
|
+
mouseMode: 'cell', // Track clicks and drags
|
|
174
|
+
// mouseMode: "all", // Track all motion
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Terminal Control
|
|
179
|
+
|
|
180
|
+
Commands for terminal manipulation:
|
|
181
|
+
|
|
182
|
+
```ts
|
|
183
|
+
import {
|
|
184
|
+
clearScreen,
|
|
185
|
+
hideCursor,
|
|
186
|
+
showCursor,
|
|
187
|
+
setWindowTitle,
|
|
188
|
+
enableMouseCellMotion,
|
|
189
|
+
enableMouseAllMotion,
|
|
190
|
+
disableMouse,
|
|
191
|
+
windowSize,
|
|
192
|
+
} from '@suds-cli/tea'
|
|
193
|
+
|
|
194
|
+
// In your update function
|
|
195
|
+
return [newModel, clearScreen()]
|
|
196
|
+
return [newModel, setWindowTitle('My App')]
|
|
197
|
+
return [newModel, batch(hideCursor(), clearScreen())]
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## System Messages
|
|
201
|
+
|
|
202
|
+
The program emits these messages automatically:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import {
|
|
206
|
+
WindowSizeMsg, // Terminal resized: { width, height }
|
|
207
|
+
FocusMsg, // Terminal gained focus
|
|
208
|
+
BlurMsg, // Terminal lost focus
|
|
209
|
+
InterruptMsg, // Ctrl+C pressed
|
|
210
|
+
QuitMsg, // Graceful shutdown requested
|
|
211
|
+
} from "@suds-cli/tea";
|
|
212
|
+
|
|
213
|
+
update(msg: Msg): [Model, Cmd<Msg>] {
|
|
214
|
+
if (msg instanceof WindowSizeMsg) {
|
|
215
|
+
return [this.withSize(msg.width, msg.height), null];
|
|
216
|
+
}
|
|
217
|
+
return [this, null];
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## Program Options
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
import { createNodePlatform } from '@suds-cli/machine/node'
|
|
225
|
+
|
|
226
|
+
// Create platform adapter with custom streams (optional)
|
|
227
|
+
const platform = createNodePlatform({
|
|
228
|
+
input: customInputStream,
|
|
229
|
+
output: customOutputStream,
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
const program = new Program(model, {
|
|
233
|
+
platform, // Platform adapter for terminal I/O and system signals
|
|
234
|
+
altScreen: true, // Use alternate screen buffer
|
|
235
|
+
mouseMode: 'cell', // "cell" | "all" | false
|
|
236
|
+
fps: 60, // Render frame rate
|
|
237
|
+
reportFocus: true, // Receive focus/blur events
|
|
238
|
+
bracketedPaste: true, // Distinguish pasted text
|
|
239
|
+
})
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
### Platform Adapters
|
|
243
|
+
|
|
244
|
+
Tea uses `@suds-cli/machine` for platform abstraction, making it possible to run terminal UIs in different environments:
|
|
245
|
+
|
|
246
|
+
- **Node.js**: `createNodePlatform()` from `@suds-cli/machine/node`
|
|
247
|
+
- **Browser** (with xterm.js): `createBrowserPlatform()` from `@suds-cli/machine/browser`
|
|
248
|
+
|
|
249
|
+
The platform adapter provides terminal I/O, signal handling, environment access, and more.
|
|
250
|
+
|
|
251
|
+
## Scripts
|
|
252
|
+
|
|
253
|
+
- `pnpm -C packages/tea build`
|
|
254
|
+
- `pnpm -C packages/tea test`
|
|
255
|
+
- `pnpm -C packages/tea lint`
|
|
256
|
+
- `pnpm -C packages/tea generate:api-report`
|
|
257
|
+
|
|
258
|
+
## License
|
|
259
|
+
|
|
260
|
+
MIT
|