@dealdeploy/skl 0.1.7 → 0.1.8
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/.agents/skills/opentui/SKILL.md +198 -0
- package/.agents/skills/opentui/references/animation/REFERENCE.md +431 -0
- package/.agents/skills/opentui/references/components/REFERENCE.md +143 -0
- package/.agents/skills/opentui/references/components/code-diff.md +496 -0
- package/.agents/skills/opentui/references/components/containers.md +412 -0
- package/.agents/skills/opentui/references/components/inputs.md +531 -0
- package/.agents/skills/opentui/references/components/text-display.md +384 -0
- package/.agents/skills/opentui/references/core/REFERENCE.md +145 -0
- package/.agents/skills/opentui/references/core/api.md +506 -0
- package/.agents/skills/opentui/references/core/configuration.md +166 -0
- package/.agents/skills/opentui/references/core/gotchas.md +393 -0
- package/.agents/skills/opentui/references/core/patterns.md +448 -0
- package/.agents/skills/opentui/references/keyboard/REFERENCE.md +511 -0
- package/.agents/skills/opentui/references/layout/REFERENCE.md +337 -0
- package/.agents/skills/opentui/references/layout/patterns.md +444 -0
- package/.agents/skills/opentui/references/react/REFERENCE.md +174 -0
- package/.agents/skills/opentui/references/react/api.md +435 -0
- package/.agents/skills/opentui/references/react/configuration.md +301 -0
- package/.agents/skills/opentui/references/react/gotchas.md +443 -0
- package/.agents/skills/opentui/references/react/patterns.md +501 -0
- package/.agents/skills/opentui/references/solid/REFERENCE.md +201 -0
- package/.agents/skills/opentui/references/solid/api.md +543 -0
- package/.agents/skills/opentui/references/solid/configuration.md +315 -0
- package/.agents/skills/opentui/references/solid/gotchas.md +415 -0
- package/.agents/skills/opentui/references/solid/patterns.md +558 -0
- package/.agents/skills/opentui/references/testing/REFERENCE.md +614 -0
- package/.claude/settings.local.json +11 -0
- package/.claude/skills/opentui/SKILL.md +198 -0
- package/.claude/skills/opentui/references/animation/REFERENCE.md +431 -0
- package/.claude/skills/opentui/references/components/REFERENCE.md +143 -0
- package/.claude/skills/opentui/references/components/code-diff.md +496 -0
- package/.claude/skills/opentui/references/components/containers.md +412 -0
- package/.claude/skills/opentui/references/components/inputs.md +531 -0
- package/.claude/skills/opentui/references/components/text-display.md +384 -0
- package/.claude/skills/opentui/references/core/REFERENCE.md +145 -0
- package/.claude/skills/opentui/references/core/api.md +506 -0
- package/.claude/skills/opentui/references/core/configuration.md +166 -0
- package/.claude/skills/opentui/references/core/gotchas.md +393 -0
- package/.claude/skills/opentui/references/core/patterns.md +448 -0
- package/.claude/skills/opentui/references/keyboard/REFERENCE.md +511 -0
- package/.claude/skills/opentui/references/layout/REFERENCE.md +337 -0
- package/.claude/skills/opentui/references/layout/patterns.md +444 -0
- package/.claude/skills/opentui/references/react/REFERENCE.md +174 -0
- package/.claude/skills/opentui/references/react/api.md +435 -0
- package/.claude/skills/opentui/references/react/configuration.md +301 -0
- package/.claude/skills/opentui/references/react/gotchas.md +443 -0
- package/.claude/skills/opentui/references/react/patterns.md +501 -0
- package/.claude/skills/opentui/references/solid/REFERENCE.md +201 -0
- package/.claude/skills/opentui/references/solid/api.md +543 -0
- package/.claude/skills/opentui/references/solid/configuration.md +315 -0
- package/.claude/skills/opentui/references/solid/gotchas.md +415 -0
- package/.claude/skills/opentui/references/solid/patterns.md +558 -0
- package/.claude/skills/opentui/references/testing/REFERENCE.md +614 -0
- package/bun.lock +0 -1
- package/index.ts +163 -38
- package/package.json +1 -1
- package/update.ts +87 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
# Layout Patterns
|
|
2
|
+
|
|
3
|
+
Common layout recipes for terminal user interfaces.
|
|
4
|
+
|
|
5
|
+
## Full-Screen App
|
|
6
|
+
|
|
7
|
+
Fill the entire terminal:
|
|
8
|
+
|
|
9
|
+
```tsx
|
|
10
|
+
function App() {
|
|
11
|
+
return (
|
|
12
|
+
<box width="100%" height="100%">
|
|
13
|
+
{/* Content fills terminal */}
|
|
14
|
+
</box>
|
|
15
|
+
)
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Header/Content/Footer
|
|
20
|
+
|
|
21
|
+
Classic app layout:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
function AppLayout() {
|
|
25
|
+
return (
|
|
26
|
+
<box flexDirection="column" width="100%" height="100%">
|
|
27
|
+
{/* Header - fixed height */}
|
|
28
|
+
<box height={3} borderStyle="single" borderBottom>
|
|
29
|
+
<text>Header</text>
|
|
30
|
+
</box>
|
|
31
|
+
|
|
32
|
+
{/* Content - fills remaining space */}
|
|
33
|
+
<box flexGrow={1}>
|
|
34
|
+
<text>Main Content</text>
|
|
35
|
+
</box>
|
|
36
|
+
|
|
37
|
+
{/* Footer - fixed height */}
|
|
38
|
+
<box height={1}>
|
|
39
|
+
<text>Status: Ready</text>
|
|
40
|
+
</box>
|
|
41
|
+
</box>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Sidebar Layout
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
function SidebarLayout() {
|
|
50
|
+
return (
|
|
51
|
+
<box flexDirection="row" width="100%" height="100%">
|
|
52
|
+
{/* Sidebar - fixed width */}
|
|
53
|
+
<box width={25} borderStyle="single" borderRight>
|
|
54
|
+
<text>Sidebar</text>
|
|
55
|
+
</box>
|
|
56
|
+
|
|
57
|
+
{/* Main - fills remaining space */}
|
|
58
|
+
<box flexGrow={1}>
|
|
59
|
+
<text>Main Content</text>
|
|
60
|
+
</box>
|
|
61
|
+
</box>
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Resizable Sidebar
|
|
67
|
+
|
|
68
|
+
Responsive based on terminal width:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
function ResponsiveSidebar() {
|
|
72
|
+
const dims = useTerminalDimensions() // React: useTerminalDimensions()
|
|
73
|
+
const showSidebar = dims.width > 60
|
|
74
|
+
const sidebarWidth = Math.min(30, Math.floor(dims.width * 0.3))
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<box flexDirection="row" width="100%" height="100%">
|
|
78
|
+
{showSidebar && (
|
|
79
|
+
<box width={sidebarWidth} border>
|
|
80
|
+
<text>Sidebar</text>
|
|
81
|
+
</box>
|
|
82
|
+
)}
|
|
83
|
+
<box flexGrow={1}>
|
|
84
|
+
<text>Main</text>
|
|
85
|
+
</box>
|
|
86
|
+
</box>
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Centered Content
|
|
92
|
+
|
|
93
|
+
### Horizontally Centered
|
|
94
|
+
|
|
95
|
+
```tsx
|
|
96
|
+
<box width="100%" justifyContent="center">
|
|
97
|
+
<box width={40}>
|
|
98
|
+
<text>Centered horizontally</text>
|
|
99
|
+
</box>
|
|
100
|
+
</box>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Vertically Centered
|
|
104
|
+
|
|
105
|
+
```tsx
|
|
106
|
+
<box height="100%" alignItems="center">
|
|
107
|
+
<text>Centered vertically</text>
|
|
108
|
+
</box>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Both Axes
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
<box
|
|
115
|
+
width="100%"
|
|
116
|
+
height="100%"
|
|
117
|
+
justifyContent="center"
|
|
118
|
+
alignItems="center"
|
|
119
|
+
>
|
|
120
|
+
<box width={40} height={10} border>
|
|
121
|
+
<text>Centered both ways</text>
|
|
122
|
+
</box>
|
|
123
|
+
</box>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Modal/Dialog
|
|
127
|
+
|
|
128
|
+
Centered overlay:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
function Modal({ children, visible }) {
|
|
132
|
+
if (!visible) return null
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<box
|
|
136
|
+
position="absolute"
|
|
137
|
+
left={0}
|
|
138
|
+
top={0}
|
|
139
|
+
width="100%"
|
|
140
|
+
height="100%"
|
|
141
|
+
justifyContent="center"
|
|
142
|
+
alignItems="center"
|
|
143
|
+
backgroundColor="rgba(0,0,0,0.5)"
|
|
144
|
+
>
|
|
145
|
+
<box
|
|
146
|
+
width={50}
|
|
147
|
+
height={15}
|
|
148
|
+
border
|
|
149
|
+
borderStyle="double"
|
|
150
|
+
backgroundColor="#1a1a2e"
|
|
151
|
+
padding={2}
|
|
152
|
+
>
|
|
153
|
+
{children}
|
|
154
|
+
</box>
|
|
155
|
+
</box>
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
## Grid Layout
|
|
161
|
+
|
|
162
|
+
Using flexWrap:
|
|
163
|
+
|
|
164
|
+
```tsx
|
|
165
|
+
function Grid({ items, columns = 3 }) {
|
|
166
|
+
const itemWidth = `${Math.floor(100 / columns)}%`
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<box flexDirection="row" flexWrap="wrap" width="100%">
|
|
170
|
+
{items.map((item, i) => (
|
|
171
|
+
<box key={i} width={itemWidth} padding={1}>
|
|
172
|
+
<text>{item}</text>
|
|
173
|
+
</box>
|
|
174
|
+
))}
|
|
175
|
+
</box>
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Split Panels
|
|
181
|
+
|
|
182
|
+
### Horizontal Split
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
function HorizontalSplit({ ratio = 0.5 }) {
|
|
186
|
+
return (
|
|
187
|
+
<box flexDirection="row" width="100%" height="100%">
|
|
188
|
+
<box width={`${ratio * 100}%`} border>
|
|
189
|
+
<text>Left Panel</text>
|
|
190
|
+
</box>
|
|
191
|
+
<box flexGrow={1} border>
|
|
192
|
+
<text>Right Panel</text>
|
|
193
|
+
</box>
|
|
194
|
+
</box>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Vertical Split
|
|
200
|
+
|
|
201
|
+
```tsx
|
|
202
|
+
function VerticalSplit({ ratio = 0.5 }) {
|
|
203
|
+
return (
|
|
204
|
+
<box flexDirection="column" width="100%" height="100%">
|
|
205
|
+
<box height={`${ratio * 100}%`} border>
|
|
206
|
+
<text>Top Panel</text>
|
|
207
|
+
</box>
|
|
208
|
+
<box flexGrow={1} border>
|
|
209
|
+
<text>Bottom Panel</text>
|
|
210
|
+
</box>
|
|
211
|
+
</box>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## Form Layout
|
|
217
|
+
|
|
218
|
+
Label + Input pairs:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
function FormField({ label, children }) {
|
|
222
|
+
return (
|
|
223
|
+
<box flexDirection="row" marginBottom={1}>
|
|
224
|
+
<box width={15}>
|
|
225
|
+
<text>{label}:</text>
|
|
226
|
+
</box>
|
|
227
|
+
<box flexGrow={1}>
|
|
228
|
+
{children}
|
|
229
|
+
</box>
|
|
230
|
+
</box>
|
|
231
|
+
)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function LoginForm() {
|
|
235
|
+
return (
|
|
236
|
+
<box flexDirection="column" padding={2} border width={50}>
|
|
237
|
+
<FormField label="Username">
|
|
238
|
+
<input placeholder="Enter username" />
|
|
239
|
+
</FormField>
|
|
240
|
+
<FormField label="Password">
|
|
241
|
+
<input placeholder="Enter password" />
|
|
242
|
+
</FormField>
|
|
243
|
+
<box marginTop={2} justifyContent="flex-end">
|
|
244
|
+
<box border padding={1}>
|
|
245
|
+
<text>Login</text>
|
|
246
|
+
</box>
|
|
247
|
+
</box>
|
|
248
|
+
</box>
|
|
249
|
+
)
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
## Navigation Tabs
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
function TabBar({ tabs, activeIndex, onSelect }) {
|
|
257
|
+
return (
|
|
258
|
+
<box flexDirection="row" borderBottom>
|
|
259
|
+
{tabs.map((tab, i) => (
|
|
260
|
+
<box
|
|
261
|
+
key={i}
|
|
262
|
+
padding={1}
|
|
263
|
+
backgroundColor={i === activeIndex ? "#333" : "transparent"}
|
|
264
|
+
onMouseDown={() => onSelect(i)}
|
|
265
|
+
>
|
|
266
|
+
<text fg={i === activeIndex ? "#fff" : "#888"}>
|
|
267
|
+
{tab}
|
|
268
|
+
</text>
|
|
269
|
+
</box>
|
|
270
|
+
))}
|
|
271
|
+
</box>
|
|
272
|
+
)
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
## Sticky Footer
|
|
277
|
+
|
|
278
|
+
Footer always at bottom:
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
function StickyFooterLayout() {
|
|
282
|
+
return (
|
|
283
|
+
<box flexDirection="column" width="100%" height="100%">
|
|
284
|
+
{/* Content area */}
|
|
285
|
+
<box flexGrow={1} flexDirection="column">
|
|
286
|
+
{/* Your content here */}
|
|
287
|
+
<text>Content that might be short</text>
|
|
288
|
+
</box>
|
|
289
|
+
|
|
290
|
+
{/* Footer pushed to bottom */}
|
|
291
|
+
<box height={1}>
|
|
292
|
+
<text fg="#888">Press ? for help | q to quit</text>
|
|
293
|
+
</box>
|
|
294
|
+
</box>
|
|
295
|
+
)
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Absolute Positioning Overlay
|
|
300
|
+
|
|
301
|
+
Tooltip or popup:
|
|
302
|
+
|
|
303
|
+
```tsx
|
|
304
|
+
function Tooltip({ x, y, children }) {
|
|
305
|
+
return (
|
|
306
|
+
<box
|
|
307
|
+
position="absolute"
|
|
308
|
+
left={x}
|
|
309
|
+
top={y}
|
|
310
|
+
border
|
|
311
|
+
backgroundColor="#333"
|
|
312
|
+
padding={1}
|
|
313
|
+
zIndex={100}
|
|
314
|
+
>
|
|
315
|
+
{children}
|
|
316
|
+
</box>
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Responsive Breakpoints
|
|
322
|
+
|
|
323
|
+
Different layouts based on terminal size:
|
|
324
|
+
|
|
325
|
+
```tsx
|
|
326
|
+
function ResponsiveApp() {
|
|
327
|
+
const { width, height } = useTerminalDimensions()
|
|
328
|
+
|
|
329
|
+
// Define breakpoints
|
|
330
|
+
const isSmall = width < 60
|
|
331
|
+
const isMedium = width >= 60 && width < 100
|
|
332
|
+
const isLarge = width >= 100
|
|
333
|
+
|
|
334
|
+
if (isSmall) {
|
|
335
|
+
// Mobile-like: stacked layout
|
|
336
|
+
return (
|
|
337
|
+
<box flexDirection="column">
|
|
338
|
+
<Navigation />
|
|
339
|
+
<Content />
|
|
340
|
+
</box>
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (isMedium) {
|
|
345
|
+
// Tablet-like: sidebar + content
|
|
346
|
+
return (
|
|
347
|
+
<box flexDirection="row">
|
|
348
|
+
<box width={20}><Navigation /></box>
|
|
349
|
+
<box flexGrow={1}><Content /></box>
|
|
350
|
+
</box>
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Large: full layout
|
|
355
|
+
return (
|
|
356
|
+
<box flexDirection="row">
|
|
357
|
+
<box width={25}><Navigation /></box>
|
|
358
|
+
<box flexGrow={1}><Content /></box>
|
|
359
|
+
<box width={30}><Sidebar /></box>
|
|
360
|
+
</box>
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
## Equal Height Columns
|
|
366
|
+
|
|
367
|
+
```tsx
|
|
368
|
+
function EqualColumns() {
|
|
369
|
+
return (
|
|
370
|
+
<box flexDirection="row" alignItems="stretch" height={20}>
|
|
371
|
+
<box flexGrow={1} border>
|
|
372
|
+
<text>Short content</text>
|
|
373
|
+
</box>
|
|
374
|
+
<box flexGrow={1} border>
|
|
375
|
+
<text>
|
|
376
|
+
Longer content that
|
|
377
|
+
spans multiple lines
|
|
378
|
+
and takes up space
|
|
379
|
+
</text>
|
|
380
|
+
</box>
|
|
381
|
+
<box flexGrow={1} border>
|
|
382
|
+
<text>Medium content</text>
|
|
383
|
+
</box>
|
|
384
|
+
</box>
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
## Spacing Utilities
|
|
390
|
+
|
|
391
|
+
Consistent spacing patterns:
|
|
392
|
+
|
|
393
|
+
```tsx
|
|
394
|
+
// Spacer component
|
|
395
|
+
function Spacer({ size = 1 }) {
|
|
396
|
+
return <box height={size} width={size} />
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// Divider component
|
|
400
|
+
function Divider() {
|
|
401
|
+
return <box height={1} width="100%" backgroundColor="#333" />
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Usage
|
|
405
|
+
<box flexDirection="column">
|
|
406
|
+
<text>Section 1</text>
|
|
407
|
+
<Spacer size={2} />
|
|
408
|
+
<Divider />
|
|
409
|
+
<Spacer size={2} />
|
|
410
|
+
<text>Section 2</text>
|
|
411
|
+
</box>
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Axis Shorthand Props
|
|
415
|
+
|
|
416
|
+
Use `paddingX`/`paddingY` and `marginX`/`marginY` for horizontal/vertical spacing:
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
// Horizontal padding (left + right)
|
|
420
|
+
<box paddingX={4}>
|
|
421
|
+
<text>4 chars padding left and right</text>
|
|
422
|
+
</box>
|
|
423
|
+
|
|
424
|
+
// Vertical padding (top + bottom)
|
|
425
|
+
<box paddingY={2}>
|
|
426
|
+
<text>2 lines padding top and bottom</text>
|
|
427
|
+
</box>
|
|
428
|
+
|
|
429
|
+
// Horizontal margin for centering-like effect
|
|
430
|
+
<box marginX={10}>
|
|
431
|
+
<text>Indented content</text>
|
|
432
|
+
</box>
|
|
433
|
+
|
|
434
|
+
// Combined for card-like spacing
|
|
435
|
+
<box paddingX={3} paddingY={1} marginY={1} border>
|
|
436
|
+
<text>Nicely spaced card</text>
|
|
437
|
+
</box>
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
These are shorthand for:
|
|
441
|
+
- `paddingX={n}` = `paddingLeft={n}` + `paddingRight={n}`
|
|
442
|
+
- `paddingY={n}` = `paddingTop={n}` + `paddingBottom={n}`
|
|
443
|
+
- `marginX={n}` = `marginLeft={n}` + `marginRight={n}`
|
|
444
|
+
- `marginY={n}` = `marginTop={n}` + `marginBottom={n}`
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# OpenTUI React (@opentui/react)
|
|
2
|
+
|
|
3
|
+
A React reconciler for building terminal user interfaces with familiar React patterns. Write TUIs using JSX, hooks, and component composition.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
OpenTUI React provides:
|
|
8
|
+
- **Custom reconciler**: React components render to OpenTUI renderables
|
|
9
|
+
- **JSX intrinsics**: `<text>`, `<box>`, `<input>`, etc.
|
|
10
|
+
- **Hooks**: `useKeyboard`, `useRenderer`, `useTimeline`, etc.
|
|
11
|
+
- **Full React compatibility**: useState, useEffect, context, and more
|
|
12
|
+
|
|
13
|
+
## When to Use React
|
|
14
|
+
|
|
15
|
+
Use the React reconciler when:
|
|
16
|
+
- You're familiar with React patterns
|
|
17
|
+
- You want declarative UI composition
|
|
18
|
+
- You need React's ecosystem (context, state management libraries)
|
|
19
|
+
- Building applications with complex state
|
|
20
|
+
- Team knows React already
|
|
21
|
+
|
|
22
|
+
## When NOT to Use React
|
|
23
|
+
|
|
24
|
+
| Scenario | Use Instead |
|
|
25
|
+
|----------|-------------|
|
|
26
|
+
| Maximum performance critical | `@opentui/core` (imperative) |
|
|
27
|
+
| Fine-grained reactivity | `@opentui/solid` |
|
|
28
|
+
| Smallest bundle size | `@opentui/core` |
|
|
29
|
+
| Building a framework/library | `@opentui/core` |
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
bunx create-tui@latest -t react my-app
|
|
35
|
+
cd my-app
|
|
36
|
+
bun run src/index.tsx
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
The CLI creates the `my-app` directory for you - it must **not already exist**.
|
|
40
|
+
|
|
41
|
+
**Agent guidance**: Always use autonomous mode with `-t <template>` flag. Never use interactive mode (`bunx create-tui@latest my-app` without `-t`) as it requires user prompts that agents cannot respond to.
|
|
42
|
+
|
|
43
|
+
Or manual setup:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
mkdir my-tui && cd my-tui
|
|
47
|
+
bun init
|
|
48
|
+
bun install @opentui/react @opentui/core react
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { createCliRenderer } from "@opentui/core"
|
|
53
|
+
import { createRoot } from "@opentui/react"
|
|
54
|
+
import { useState } from "react"
|
|
55
|
+
|
|
56
|
+
function App() {
|
|
57
|
+
const [count, setCount] = useState(0)
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<box border padding={2}>
|
|
61
|
+
<text>Count: {count}</text>
|
|
62
|
+
<box
|
|
63
|
+
border
|
|
64
|
+
onMouseDown={() => setCount(c => c + 1)}
|
|
65
|
+
>
|
|
66
|
+
<text>Click me!</text>
|
|
67
|
+
</box>
|
|
68
|
+
</box>
|
|
69
|
+
)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const renderer = await createCliRenderer()
|
|
73
|
+
createRoot(renderer).render(<App />)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Core Concepts
|
|
77
|
+
|
|
78
|
+
### JSX Elements
|
|
79
|
+
|
|
80
|
+
React maps JSX intrinsic elements to OpenTUI renderables:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
// These are not HTML elements!
|
|
84
|
+
<text>Hello</text> // TextRenderable
|
|
85
|
+
<box border>Content</box> // BoxRenderable
|
|
86
|
+
<input placeholder="..." /> // InputRenderable
|
|
87
|
+
<select options={[...]} /> // SelectRenderable
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Text Modifiers
|
|
91
|
+
|
|
92
|
+
Inside `<text>`, use modifier elements:
|
|
93
|
+
|
|
94
|
+
```tsx
|
|
95
|
+
<text>
|
|
96
|
+
<strong>Bold</strong>, <em>italic</em>, and <u>underlined</u>
|
|
97
|
+
<span fg="red">Colored text</span>
|
|
98
|
+
<br />
|
|
99
|
+
New line with <a href="https://example.com">link</a>
|
|
100
|
+
</text>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Styling
|
|
104
|
+
|
|
105
|
+
Two approaches to styling:
|
|
106
|
+
|
|
107
|
+
```tsx
|
|
108
|
+
// Direct props
|
|
109
|
+
<box backgroundColor="blue" padding={2} border>
|
|
110
|
+
<text fg="#00FF00">Green text</text>
|
|
111
|
+
</box>
|
|
112
|
+
|
|
113
|
+
// Style prop
|
|
114
|
+
<box style={{ backgroundColor: "blue", padding: 2, border: true }}>
|
|
115
|
+
<text style={{ fg: "#00FF00" }}>Green text</text>
|
|
116
|
+
</box>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Available Components
|
|
120
|
+
|
|
121
|
+
### Layout & Display
|
|
122
|
+
- `<text>` - Styled text content
|
|
123
|
+
- `<box>` - Container with borders and layout
|
|
124
|
+
- `<scrollbox>` - Scrollable container
|
|
125
|
+
- `<ascii-font>` - ASCII art text
|
|
126
|
+
|
|
127
|
+
### Input
|
|
128
|
+
- `<input>` - Single-line text input
|
|
129
|
+
- `<textarea>` - Multi-line text input
|
|
130
|
+
- `<select>` - List selection
|
|
131
|
+
- `<tab-select>` - Tab-based selection
|
|
132
|
+
|
|
133
|
+
### Code & Diff
|
|
134
|
+
- `<code>` - Syntax-highlighted code
|
|
135
|
+
- `<line-number>` - Code with line numbers
|
|
136
|
+
- `<diff>` - Unified or split diff viewer
|
|
137
|
+
|
|
138
|
+
### Text Modifiers (inside `<text>`)
|
|
139
|
+
- `<span>` - Inline styled text
|
|
140
|
+
- `<strong>`, `<b>` - Bold
|
|
141
|
+
- `<em>`, `<i>` - Italic
|
|
142
|
+
- `<u>` - Underline
|
|
143
|
+
- `<br>` - Line break
|
|
144
|
+
- `<a>` - Link
|
|
145
|
+
|
|
146
|
+
## Essential Hooks
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
import {
|
|
150
|
+
useRenderer,
|
|
151
|
+
useKeyboard,
|
|
152
|
+
useOnResize,
|
|
153
|
+
useTerminalDimensions,
|
|
154
|
+
useTimeline,
|
|
155
|
+
} from "@opentui/react"
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
See [API Reference](./api.md) for detailed hook documentation.
|
|
159
|
+
|
|
160
|
+
## In This Reference
|
|
161
|
+
|
|
162
|
+
- [Configuration](./configuration.md) - Project setup, tsconfig, bundling
|
|
163
|
+
- [API](./api.md) - Components, hooks, createRoot
|
|
164
|
+
- [Patterns](./patterns.md) - State management, keyboard handling, forms
|
|
165
|
+
- [Gotchas](./gotchas.md) - Common issues, debugging, limitations
|
|
166
|
+
|
|
167
|
+
## See Also
|
|
168
|
+
|
|
169
|
+
- [Core](../core/REFERENCE.md) - Underlying imperative API
|
|
170
|
+
- [Solid](../solid/REFERENCE.md) - Alternative declarative approach
|
|
171
|
+
- [Components](../components/REFERENCE.md) - Component reference by category
|
|
172
|
+
- [Layout](../layout/REFERENCE.md) - Flexbox layout system
|
|
173
|
+
- [Keyboard](../keyboard/REFERENCE.md) - Input handling and shortcuts
|
|
174
|
+
- [Testing](../testing/REFERENCE.md) - Test renderer and snapshots
|