@opentui/react 0.1.19-snapshot5-5ed8b651 → 0.1.19

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 CHANGED
@@ -4,6 +4,14 @@ A React renderer for building terminal user interfaces using [OpenTUI core](http
4
4
 
5
5
  ## Installation
6
6
 
7
+ Quick start with [bun](https://bun.sh) and [create-tui](https://github.com/msmps/create-tui):
8
+
9
+ ```bash
10
+ bun create tui --template react
11
+ ```
12
+
13
+ Manual installation:
14
+
7
15
  ```bash
8
16
  bun install @opentui/react @opentui/core react
9
17
  ```
@@ -14,14 +22,7 @@ bun install @opentui/react @opentui/core react
14
22
  import { render } from "@opentui/react"
15
23
 
16
24
  function App() {
17
- return (
18
- <box>
19
- <text fg="#00FF00">Hello, Terminal!</text>
20
- <box title="Welcome" padding={2}>
21
- <text>Welcome to OpenTUI with React!</text>
22
- </box>
23
- </box>
24
- )
25
+ return <text>Hello, world!</text>
25
26
  }
26
27
 
27
28
  render(<App />)
@@ -56,6 +57,7 @@ OpenTUI React provides several built-in components that map to OpenTUI core rend
56
57
  - **`<box>`** - Container with borders and layout
57
58
  - **`<input>`** - Text input field
58
59
  - **`<select>`** - Selection dropdown
60
+ - **`<scrollbox>`** - A scrollable box
59
61
  - **`<tab-select>`** - Tab-based selection
60
62
  - **`<ascii-font>`** - Display ASCII art text with different font styles
61
63
 
@@ -65,11 +67,13 @@ Components can be styled using props or the `style` prop:
65
67
 
66
68
  ```tsx
67
69
  // Direct props
68
- <text fg="#FF0000">Hello</text>
70
+ <box backgroundColor="blue" padding={2}>
71
+ <text>Hello, world!</text>
72
+ </box>
69
73
 
70
74
  // Style prop
71
75
  <box style={{ backgroundColor: "blue", padding: 2 }}>
72
- <text>Styled content</text>
76
+ <text>Hello, world!</text>
73
77
  </box>
74
78
  ```
75
79
 
@@ -102,14 +106,15 @@ Access the OpenTUI renderer instance.
102
106
  ```tsx
103
107
  import { useRenderer } from "@opentui/react"
104
108
 
105
- function MyComponent() {
109
+ function App() {
106
110
  const renderer = useRenderer()
107
111
 
108
112
  useEffect(() => {
109
- renderer.toggleDebugOverlay()
113
+ renderer.console.show()
114
+ console.log("Hello, from the console!")
110
115
  }, [])
111
116
 
112
- return <text>Debug available</text>
117
+ return <box />
113
118
  }
114
119
  ```
115
120
 
@@ -120,7 +125,7 @@ Handle keyboard events.
120
125
  ```tsx
121
126
  import { useKeyboard } from "@opentui/react"
122
127
 
123
- function MyComponent() {
128
+ function App() {
124
129
  useKeyboard((key) => {
125
130
  if (key.name === "escape") {
126
131
  process.exit(0)
@@ -139,7 +144,7 @@ Handle terminal resize events.
139
144
  import { useOnResize, useRenderer } from "@opentui/react"
140
145
  import { useEffect } from "react"
141
146
 
142
- function MyComponent() {
147
+ function App() {
143
148
  const renderer = useRenderer()
144
149
 
145
150
  useEffect(() => {
@@ -161,7 +166,7 @@ Get current terminal dimensions and automatically update when the terminal is re
161
166
  ```tsx
162
167
  import { useTerminalDimensions } from "@opentui/react"
163
168
 
164
- function MyComponent() {
169
+ function App() {
165
170
  const { width, height } = useTerminalDimensions()
166
171
 
167
172
  return (
@@ -188,7 +193,7 @@ Display text with rich formatting.
188
193
  ```tsx
189
194
  import { bold, fg, t } from "@opentui/core"
190
195
 
191
- function TextExample() {
196
+ function App() {
192
197
  return (
193
198
  <box>
194
199
  {/* Simple text */}
@@ -209,22 +214,23 @@ function TextExample() {
209
214
  Container with borders and layout capabilities.
210
215
 
211
216
  ```tsx
212
- function BoxExample() {
217
+ function App() {
213
218
  return (
214
219
  <box flexDirection="column">
215
220
  {/* Basic box */}
216
- <box>
221
+ <box border>
217
222
  <text>Simple box</text>
218
223
  </box>
219
224
 
220
225
  {/* Box with title and styling */}
221
- <box title="Settings" borderStyle="double" padding={2} backgroundColor="blue">
226
+ <box title="Settings" border borderStyle="double" padding={2} backgroundColor="blue">
222
227
  <text>Box content</text>
223
228
  </box>
224
229
 
225
230
  {/* Styled box */}
226
231
  <box
227
232
  style={{
233
+ border: true,
228
234
  width: 40,
229
235
  height: 10,
230
236
  margin: 1,
@@ -246,20 +252,16 @@ Text input field with event handling.
246
252
  ```tsx
247
253
  import { useState } from "react"
248
254
 
249
- function InputExample() {
255
+ function App() {
250
256
  const [value, setValue] = useState("")
251
- const [focused, setFocused] = useState(true)
252
257
 
253
258
  return (
254
- <box title="Enter your name" style={{ height: 3 }}>
259
+ <box title="Enter your name" style={{ border: true, height: 3 }}>
255
260
  <input
256
261
  placeholder="Type here..."
257
- focused={focused}
262
+ focused
258
263
  onInput={setValue}
259
264
  onSubmit={(value) => console.log("Submitted:", value)}
260
- style={{
261
- focusedBackgroundColor: "#333333",
262
- }}
263
265
  />
264
266
  </box>
265
267
  )
@@ -274,7 +276,7 @@ Dropdown selection component.
274
276
  import type { SelectOption } from "@opentui/core"
275
277
  import { useState } from "react"
276
278
 
277
- function SelectExample() {
279
+ function App() {
278
280
  const [selectedIndex, setSelectedIndex] = useState(0)
279
281
 
280
282
  const options: SelectOption[] = [
@@ -284,7 +286,7 @@ function SelectExample() {
284
286
  ]
285
287
 
286
288
  return (
287
- <box style={{ height: 24 }}>
289
+ <box style={{ border: true, height: 24 }}>
288
290
  <select
289
291
  style={{ height: 22 }}
290
292
  options={options}
@@ -299,6 +301,50 @@ function SelectExample() {
299
301
  }
300
302
  ```
301
303
 
304
+ ### Scrollbox Component
305
+
306
+ A scrollable box.
307
+
308
+ ```tsx
309
+ function App() {
310
+ return (
311
+ <scrollbox
312
+ style={{
313
+ rootOptions: {
314
+ backgroundColor: "#24283b",
315
+ },
316
+ wrapperOptions: {
317
+ backgroundColor: "#1f2335",
318
+ },
319
+ viewportOptions: {
320
+ backgroundColor: "#1a1b26",
321
+ },
322
+ contentOptions: {
323
+ backgroundColor: "#16161e",
324
+ },
325
+ scrollbarOptions: {
326
+ showArrows: true,
327
+ trackOptions: {
328
+ foregroundColor: "#7aa2f7",
329
+ backgroundColor: "#414868",
330
+ },
331
+ },
332
+ }}
333
+ focused
334
+ >
335
+ {Array.from({ length: 1000 }).map((_, i) => (
336
+ <box
337
+ key={i}
338
+ style={{ width: "100%", padding: 1, marginBottom: 1, backgroundColor: i % 2 === 0 ? "#292e42" : "#2f3449" }}
339
+ >
340
+ <text content={`Box ${i}`} />
341
+ </box>
342
+ ))}
343
+ </scrollbox>
344
+ )
345
+ }
346
+ ```
347
+
302
348
  ### ASCII Font Component
303
349
 
304
350
  Display ASCII art text with different font styles.
@@ -307,7 +353,7 @@ Display ASCII art text with different font styles.
307
353
  import { measureText } from "@opentui/core"
308
354
  import { useState } from "react"
309
355
 
310
- function ASCIIFontExample() {
356
+ function App() {
311
357
  const text = "ASCII"
312
358
  const [font, setFont] = useState<"block" | "shade" | "slick" | "tiny">("tiny")
313
359
 
@@ -317,10 +363,11 @@ function ASCIIFontExample() {
317
363
  })
318
364
 
319
365
  return (
320
- <box style={{ paddingLeft: 1, paddingRight: 1 }}>
366
+ <box style={{ border: true, paddingLeft: 1, paddingRight: 1 }}>
321
367
  <box
322
368
  style={{
323
369
  height: 8,
370
+ border: true,
324
371
  marginBottom: 1,
325
372
  }}
326
373
  >
@@ -365,10 +412,10 @@ function ASCIIFontExample() {
365
412
  ### Login Form
366
413
 
367
414
  ```tsx
368
- import { useState, useCallback } from "react"
369
415
  import { render, useKeyboard } from "@opentui/react"
416
+ import { useCallback, useState } from "react"
370
417
 
371
- function LoginForm() {
418
+ function App() {
372
419
  const [username, setUsername] = useState("")
373
420
  const [password, setPassword] = useState("")
374
421
  const [focused, setFocused] = useState<"username" | "password">("username")
@@ -389,10 +436,10 @@ function LoginForm() {
389
436
  }, [username, password])
390
437
 
391
438
  return (
392
- <box style={{ padding: 2, flexDirection: "column" }}>
439
+ <box style={{ border: true, padding: 2, flexDirection: "column", gap: 1 }}>
393
440
  <text fg="#FFFF00">Login Form</text>
394
441
 
395
- <box title="Username" style={{ width: 40, height: 3, marginTop: 1 }}>
442
+ <box title="Username" style={{ border: true, width: 40, height: 3 }}>
396
443
  <input
397
444
  placeholder="Enter username..."
398
445
  onInput={setUsername}
@@ -401,7 +448,7 @@ function LoginForm() {
401
448
  />
402
449
  </box>
403
450
 
404
- <box title="Password" style={{ width: 40, height: 3, marginTop: 1 }}>
451
+ <box title="Password" style={{ border: true, width: 40, height: 3 }}>
405
452
  <input
406
453
  placeholder="Enter password..."
407
454
  onInput={setPassword}
@@ -421,16 +468,16 @@ function LoginForm() {
421
468
  )
422
469
  }
423
470
 
424
- render(<LoginForm />)
471
+ render(<App />)
425
472
  ```
426
473
 
427
474
  ### Counter with Timer
428
475
 
429
476
  ```tsx
430
- import { useState, useEffect } from "react"
431
477
  import { render } from "@opentui/react"
478
+ import { useEffect, useState } from "react"
432
479
 
433
- function Counter() {
480
+ function App() {
434
481
  const [count, setCount] = useState(0)
435
482
 
436
483
  useEffect(() => {
@@ -448,7 +495,7 @@ function Counter() {
448
495
  )
449
496
  }
450
497
 
451
- render(<Counter />)
498
+ render(<App />)
452
499
  ```
453
500
 
454
501
  ### Styled Text Showcase
@@ -457,7 +504,7 @@ render(<Counter />)
457
504
  import { blue, bold, red, t, underline } from "@opentui/core"
458
505
  import { render } from "@opentui/react"
459
506
 
460
- function StyledTextShowcase() {
507
+ function App() {
461
508
  return (
462
509
  <box style={{ flexDirection: "column" }}>
463
510
  <text>Simple text</text>
@@ -471,25 +518,32 @@ function StyledTextShowcase() {
471
518
  )
472
519
  }
473
520
 
474
- render(<StyledTextShowcase />)
521
+ render(<App />)
475
522
  ```
476
523
 
477
524
  ## Component Extension
478
525
 
479
- You can create custom components by extending OpenTUI's base renderables:
526
+ You can create custom components by extending OpenTUIs base renderables:
480
527
 
481
528
  ```tsx
482
- import { BoxRenderable, OptimizedBuffer, RGBA } from "@opentui/core"
529
+ import { BoxRenderable, OptimizedBuffer, RGBA, type BoxOptions, type RenderContext } from "@opentui/core"
483
530
  import { extend, render } from "@opentui/react"
484
531
 
485
532
  // Create custom component class
486
533
  class ButtonRenderable extends BoxRenderable {
487
534
  private _label: string = "Button"
488
535
 
489
- constructor(id: string, options: any) {
490
- super(id, options)
491
- this.borderStyle = "single"
492
- this.padding = 1
536
+ constructor(ctx: RenderContext, options: BoxOptions & { label?: string }) {
537
+ super(ctx, {
538
+ border: true,
539
+ borderStyle: "single",
540
+ minHeight: 3,
541
+ ...options,
542
+ })
543
+
544
+ if (options.label) {
545
+ this._label = options.label
546
+ }
493
547
  }
494
548
 
495
549
  protected renderSelf(buffer: OptimizedBuffer): void {
@@ -510,19 +564,19 @@ class ButtonRenderable extends BoxRenderable {
510
564
  // Add TypeScript support
511
565
  declare module "@opentui/react" {
512
566
  interface OpenTUIComponents {
513
- button: typeof ButtonRenderable
567
+ consoleButton: typeof ButtonRenderable
514
568
  }
515
569
  }
516
570
 
517
571
  // Register the component
518
- extend({ button: ButtonRenderable })
572
+ extend({ consoleButton: ButtonRenderable })
519
573
 
520
574
  // Use in JSX
521
575
  function App() {
522
576
  return (
523
577
  <box>
524
- <button label="Click me!" style={{ backgroundColor: "blue" }} />
525
- <button label="Another button" style={{ backgroundColor: "green" }} />
578
+ <consoleButton label="Click me!" style={{ backgroundColor: "blue" }} />
579
+ <consoleButton label="Another button" style={{ backgroundColor: "green" }} />
526
580
  </box>
527
581
  )
528
582
  }
package/index.js CHANGED
@@ -149,8 +149,7 @@ function handleTextChildren(textInstance, children) {
149
149
  if (typeof child === "string") {
150
150
  chunks.push({
151
151
  __isChunk: true,
152
- text: new TextEncoder().encode(child),
153
- plainText: child
152
+ text: child
154
153
  });
155
154
  } else if (child && typeof child === "object" && "__isChunk" in child) {
156
155
  chunks.push(child);
@@ -160,8 +159,7 @@ function handleTextChildren(textInstance, children) {
160
159
  const stringValue = String(child);
161
160
  chunks.push({
162
161
  __isChunk: true,
163
- text: new TextEncoder().encode(stringValue),
164
- plainText: stringValue
162
+ text: stringValue
165
163
  });
166
164
  }
167
165
  }
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "index.js",
5
5
  "types": "src/index.d.ts",
6
6
  "type": "module",
7
- "version": "0.1.19-snapshot5-5ed8b651",
7
+ "version": "0.1.19",
8
8
  "description": "React renderer for building terminal user interfaces using OpenTUI core",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -35,7 +35,7 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@opentui/core": "0.1.19-snapshot5-5ed8b651",
38
+ "@opentui/core": "0.1.19",
39
39
  "react-reconciler": "^0.32.0"
40
40
  },
41
41
  "devDependencies": {
@@ -84,8 +84,7 @@ function handleTextChildren(textInstance, children) {
84
84
  if (typeof child === "string") {
85
85
  chunks.push({
86
86
  __isChunk: true,
87
- text: new TextEncoder().encode(child),
88
- plainText: child
87
+ text: child
89
88
  });
90
89
  } else if (child && typeof child === "object" && "__isChunk" in child) {
91
90
  chunks.push(child);
@@ -95,8 +94,7 @@ function handleTextChildren(textInstance, children) {
95
94
  const stringValue = String(child);
96
95
  chunks.push({
97
96
  __isChunk: true,
98
- text: new TextEncoder().encode(stringValue),
99
- plainText: stringValue
97
+ text: stringValue
100
98
  });
101
99
  }
102
100
  }