@opentui/react 0.1.52 → 0.1.54
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 +272 -119
- package/index.js +12 -2
- package/jsx-namespace.d.ts +4 -0
- package/package.json +2 -2
- package/src/components/index.d.ts +3 -1
- package/src/hooks/use-keyboard.d.ts +21 -1
- package/src/reconciler/renderer.js +4 -0
- package/src/test-utils/test-utils.js +4 -0
- package/src/types/components.d.ts +5 -1
package/README.md
CHANGED
|
@@ -49,23 +49,68 @@ For optimal TypeScript support, configure your `tsconfig.json`:
|
|
|
49
49
|
}
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
+
## Table of Contents
|
|
53
|
+
|
|
54
|
+
- [Core Concepts](#core-concepts)
|
|
55
|
+
- [Components](#components)
|
|
56
|
+
- [Styling](#styling)
|
|
57
|
+
- [API Reference](#api-reference)
|
|
58
|
+
- [createRoot(renderer)](#createrootrenderer)
|
|
59
|
+
- [render(element, config?)](#renderelement-config-deprecated)
|
|
60
|
+
- [Hooks](#hooks)
|
|
61
|
+
- [useRenderer()](#userenderer)
|
|
62
|
+
- [useKeyboard(handler, options?)](#usekeyboardhandler-options)
|
|
63
|
+
- [useOnResize(callback)](#useonresizecallback)
|
|
64
|
+
- [useTerminalDimensions()](#useterminaldimensions)
|
|
65
|
+
- [useTimeline(options?)](#usetimelineoptions)
|
|
66
|
+
- [Components](#components-1)
|
|
67
|
+
- [Layout & Display Components](#layout--display-components)
|
|
68
|
+
- [Text Component](#text-component)
|
|
69
|
+
- [Box Component](#box-component)
|
|
70
|
+
- [Scrollbox Component](#scrollbox-component)
|
|
71
|
+
- [ASCII Font Component](#ascii-font-component)
|
|
72
|
+
- [Input Components](#input-components)
|
|
73
|
+
- [Input Component](#input-component)
|
|
74
|
+
- [Textarea Component](#textarea-component)
|
|
75
|
+
- [Select Component](#select-component)
|
|
76
|
+
- [Code & Diff Components](#code--diff-components)
|
|
77
|
+
- [Code Component](#code-component)
|
|
78
|
+
- [Line Number Component](#line-number-component)
|
|
79
|
+
- [Diff Component](#diff-component)
|
|
80
|
+
- [Examples](#examples)
|
|
81
|
+
- [Login Form](#login-form)
|
|
82
|
+
- [Counter with Timer](#counter-with-timer)
|
|
83
|
+
- [System Monitor Animation](#system-monitor-animation)
|
|
84
|
+
- [Styled Text Showcase](#styled-text-showcase)
|
|
85
|
+
- [Component Extension](#component-extension)
|
|
86
|
+
|
|
52
87
|
## Core Concepts
|
|
53
88
|
|
|
54
89
|
### Components
|
|
55
90
|
|
|
56
91
|
OpenTUI React provides several built-in components that map to OpenTUI core renderables:
|
|
57
92
|
|
|
93
|
+
**Layout & Display:**
|
|
94
|
+
|
|
58
95
|
- **`<text>`** - Display text with styling
|
|
59
96
|
- **`<box>`** - Container with borders and layout
|
|
97
|
+
- **`<scrollbox>`** - A scrollable box
|
|
98
|
+
- **`<ascii-font>`** - Display ASCII art text with different font styles
|
|
99
|
+
|
|
100
|
+
**Input Components:**
|
|
101
|
+
|
|
60
102
|
- **`<input>`** - Text input field
|
|
61
103
|
- **`<textarea>`** - Multi-line text input field
|
|
62
|
-
- **`<code>`** - Code block with syntax highlighting
|
|
63
104
|
- **`<select>`** - Selection dropdown
|
|
64
|
-
- **`<scrollbox>`** - A scrollable box
|
|
65
105
|
- **`<tab-select>`** - Tab-based selection
|
|
66
|
-
- **`<ascii-font>`** - Display ASCII art text with different font styles
|
|
67
106
|
|
|
68
|
-
|
|
107
|
+
**Code & Diff Components:**
|
|
108
|
+
|
|
109
|
+
- **`<code>`** - Code block with syntax highlighting
|
|
110
|
+
- **`<line-number>`** - Code display with line numbers, diff highlights, and diagnostics
|
|
111
|
+
- **`<diff>`** - Unified or split diff viewer with syntax highlighting
|
|
112
|
+
|
|
113
|
+
**Helpers:**
|
|
69
114
|
|
|
70
115
|
- **`<span>`, `<strong>`, `<em>`, `<u>`, `<b>`, `<i>`, `<br>`** - Text modifiers (_must be used inside of the text component_)
|
|
71
116
|
|
|
@@ -135,7 +180,7 @@ function App() {
|
|
|
135
180
|
}
|
|
136
181
|
```
|
|
137
182
|
|
|
138
|
-
#### `useKeyboard(handler)`
|
|
183
|
+
#### `useKeyboard(handler, options?)`
|
|
139
184
|
|
|
140
185
|
Handle keyboard events.
|
|
141
186
|
|
|
@@ -153,6 +198,46 @@ function App() {
|
|
|
153
198
|
}
|
|
154
199
|
```
|
|
155
200
|
|
|
201
|
+
**Parameters:**
|
|
202
|
+
|
|
203
|
+
- `handler`: Callback function that receives a `KeyEvent` object
|
|
204
|
+
- `options?`: Optional configuration object:
|
|
205
|
+
- `release?`: Boolean to include key release events (default: `false`)
|
|
206
|
+
|
|
207
|
+
By default, only receives press events (including key repeats with `repeated: true`). Set `options.release` to `true` to also receive release events.
|
|
208
|
+
|
|
209
|
+
**Example with release events:**
|
|
210
|
+
|
|
211
|
+
```tsx
|
|
212
|
+
import { useKeyboard } from "@opentui/react"
|
|
213
|
+
import { useState } from "react"
|
|
214
|
+
|
|
215
|
+
function App() {
|
|
216
|
+
const [pressedKeys, setPressedKeys] = useState<Set<string>>(new Set())
|
|
217
|
+
|
|
218
|
+
useKeyboard(
|
|
219
|
+
(event) => {
|
|
220
|
+
setPressedKeys((keys) => {
|
|
221
|
+
const newKeys = new Set(keys)
|
|
222
|
+
if (event.eventType === "release") {
|
|
223
|
+
newKeys.delete(event.name)
|
|
224
|
+
} else {
|
|
225
|
+
newKeys.add(event.name)
|
|
226
|
+
}
|
|
227
|
+
return newKeys
|
|
228
|
+
})
|
|
229
|
+
},
|
|
230
|
+
{ release: true },
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<box>
|
|
235
|
+
<text>Currently pressed: {Array.from(pressedKeys).join(", ") || "none"}</text>
|
|
236
|
+
</box>
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
156
241
|
#### `useOnResize(callback)`
|
|
157
242
|
|
|
158
243
|
Handle terminal resize events.
|
|
@@ -255,7 +340,9 @@ function App() {
|
|
|
255
340
|
|
|
256
341
|
## Components
|
|
257
342
|
|
|
258
|
-
###
|
|
343
|
+
### Layout & Display Components
|
|
344
|
+
|
|
345
|
+
#### Text Component
|
|
259
346
|
|
|
260
347
|
Display text with rich formatting.
|
|
261
348
|
|
|
@@ -280,7 +367,7 @@ function App() {
|
|
|
280
367
|
}
|
|
281
368
|
```
|
|
282
369
|
|
|
283
|
-
|
|
370
|
+
#### Box Component
|
|
284
371
|
|
|
285
372
|
Container with borders and layout capabilities.
|
|
286
373
|
|
|
@@ -316,7 +403,109 @@ function App() {
|
|
|
316
403
|
}
|
|
317
404
|
```
|
|
318
405
|
|
|
319
|
-
|
|
406
|
+
#### Scrollbox Component
|
|
407
|
+
|
|
408
|
+
A scrollable box.
|
|
409
|
+
|
|
410
|
+
```tsx
|
|
411
|
+
function App() {
|
|
412
|
+
return (
|
|
413
|
+
<scrollbox
|
|
414
|
+
style={{
|
|
415
|
+
rootOptions: {
|
|
416
|
+
backgroundColor: "#24283b",
|
|
417
|
+
},
|
|
418
|
+
wrapperOptions: {
|
|
419
|
+
backgroundColor: "#1f2335",
|
|
420
|
+
},
|
|
421
|
+
viewportOptions: {
|
|
422
|
+
backgroundColor: "#1a1b26",
|
|
423
|
+
},
|
|
424
|
+
contentOptions: {
|
|
425
|
+
backgroundColor: "#16161e",
|
|
426
|
+
},
|
|
427
|
+
scrollbarOptions: {
|
|
428
|
+
showArrows: true,
|
|
429
|
+
trackOptions: {
|
|
430
|
+
foregroundColor: "#7aa2f7",
|
|
431
|
+
backgroundColor: "#414868",
|
|
432
|
+
},
|
|
433
|
+
},
|
|
434
|
+
}}
|
|
435
|
+
focused
|
|
436
|
+
>
|
|
437
|
+
{Array.from({ length: 1000 }).map((_, i) => (
|
|
438
|
+
<box
|
|
439
|
+
key={i}
|
|
440
|
+
style={{ width: "100%", padding: 1, marginBottom: 1, backgroundColor: i % 2 === 0 ? "#292e42" : "#2f3449" }}
|
|
441
|
+
>
|
|
442
|
+
<text content={`Box ${i}`} />
|
|
443
|
+
</box>
|
|
444
|
+
))}
|
|
445
|
+
</scrollbox>
|
|
446
|
+
)
|
|
447
|
+
}
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
#### ASCII Font Component
|
|
451
|
+
|
|
452
|
+
Display ASCII art text with different font styles.
|
|
453
|
+
|
|
454
|
+
```tsx
|
|
455
|
+
import { useState } from "react"
|
|
456
|
+
|
|
457
|
+
function App() {
|
|
458
|
+
const text = "ASCII"
|
|
459
|
+
const [font, setFont] = useState<"block" | "shade" | "slick" | "tiny">("tiny")
|
|
460
|
+
|
|
461
|
+
return (
|
|
462
|
+
<box style={{ border: true, paddingLeft: 1, paddingRight: 1 }}>
|
|
463
|
+
<box
|
|
464
|
+
style={{
|
|
465
|
+
height: 8,
|
|
466
|
+
border: true,
|
|
467
|
+
marginBottom: 1,
|
|
468
|
+
}}
|
|
469
|
+
>
|
|
470
|
+
<select
|
|
471
|
+
focused
|
|
472
|
+
onChange={(_, option) => setFont(option?.value)}
|
|
473
|
+
showScrollIndicator
|
|
474
|
+
options={[
|
|
475
|
+
{
|
|
476
|
+
name: "Tiny",
|
|
477
|
+
description: "Tiny font",
|
|
478
|
+
value: "tiny",
|
|
479
|
+
},
|
|
480
|
+
{
|
|
481
|
+
name: "Block",
|
|
482
|
+
description: "Block font",
|
|
483
|
+
value: "block",
|
|
484
|
+
},
|
|
485
|
+
{
|
|
486
|
+
name: "Slick",
|
|
487
|
+
description: "Slick font",
|
|
488
|
+
value: "slick",
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
name: "Shade",
|
|
492
|
+
description: "Shade font",
|
|
493
|
+
value: "shade",
|
|
494
|
+
},
|
|
495
|
+
]}
|
|
496
|
+
style={{ flexGrow: 1 }}
|
|
497
|
+
/>
|
|
498
|
+
</box>
|
|
499
|
+
|
|
500
|
+
<ascii-font text={text} font={font} />
|
|
501
|
+
</box>
|
|
502
|
+
)
|
|
503
|
+
}
|
|
504
|
+
```
|
|
505
|
+
|
|
506
|
+
### Input Components
|
|
507
|
+
|
|
508
|
+
#### Input Component
|
|
320
509
|
|
|
321
510
|
Text input field with event handling.
|
|
322
511
|
|
|
@@ -339,7 +528,7 @@ function App() {
|
|
|
339
528
|
}
|
|
340
529
|
```
|
|
341
530
|
|
|
342
|
-
|
|
531
|
+
#### Textarea Component
|
|
343
532
|
|
|
344
533
|
```tsx
|
|
345
534
|
import type { TextareaRenderable } from "@opentui/core"
|
|
@@ -368,38 +557,7 @@ function App() {
|
|
|
368
557
|
}
|
|
369
558
|
```
|
|
370
559
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
```tsx
|
|
374
|
-
import { RGBA, SyntaxStyle } from "@opentui/core"
|
|
375
|
-
|
|
376
|
-
const syntaxStyle = SyntaxStyle.fromStyles({
|
|
377
|
-
keyword: { fg: RGBA.fromHex("#ff6b6b"), bold: true }, // red, bold
|
|
378
|
-
string: { fg: RGBA.fromHex("#51cf66") }, // green
|
|
379
|
-
comment: { fg: RGBA.fromHex("#868e96"), italic: true }, // gray, italic
|
|
380
|
-
number: { fg: RGBA.fromHex("#ffd43b") }, // yellow
|
|
381
|
-
default: { fg: RGBA.fromHex("#ffffff") }, // white
|
|
382
|
-
})
|
|
383
|
-
|
|
384
|
-
const codeExample = `function hello() {
|
|
385
|
-
// This is a comment
|
|
386
|
-
|
|
387
|
-
const message = "Hello, world!"
|
|
388
|
-
const count = 42
|
|
389
|
-
|
|
390
|
-
return message + " " + count
|
|
391
|
-
}`
|
|
392
|
-
|
|
393
|
-
function App() {
|
|
394
|
-
return (
|
|
395
|
-
<box style={{ border: true, flexGrow: 1 }}>
|
|
396
|
-
<code content={codeExample} filetype="javascript" syntaxStyle={syntaxStyle} />
|
|
397
|
-
</box>
|
|
398
|
-
)
|
|
399
|
-
}
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
### Select Component
|
|
560
|
+
#### Select Component
|
|
403
561
|
|
|
404
562
|
Dropdown selection component.
|
|
405
563
|
|
|
@@ -432,106 +590,101 @@ function App() {
|
|
|
432
590
|
}
|
|
433
591
|
```
|
|
434
592
|
|
|
435
|
-
###
|
|
593
|
+
### Code & Diff Components
|
|
436
594
|
|
|
437
|
-
|
|
595
|
+
#### Code Component
|
|
438
596
|
|
|
439
597
|
```tsx
|
|
598
|
+
import { RGBA, SyntaxStyle } from "@opentui/core"
|
|
599
|
+
|
|
600
|
+
const syntaxStyle = SyntaxStyle.fromStyles({
|
|
601
|
+
keyword: { fg: RGBA.fromHex("#ff6b6b"), bold: true }, // red, bold
|
|
602
|
+
string: { fg: RGBA.fromHex("#51cf66") }, // green
|
|
603
|
+
comment: { fg: RGBA.fromHex("#868e96"), italic: true }, // gray, italic
|
|
604
|
+
number: { fg: RGBA.fromHex("#ffd43b") }, // yellow
|
|
605
|
+
default: { fg: RGBA.fromHex("#ffffff") }, // white
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
const codeExample = `function hello() {
|
|
609
|
+
// This is a comment
|
|
610
|
+
|
|
611
|
+
const message = "Hello, world!"
|
|
612
|
+
const count = 42
|
|
613
|
+
|
|
614
|
+
return message + " " + count
|
|
615
|
+
}`
|
|
616
|
+
|
|
440
617
|
function App() {
|
|
441
618
|
return (
|
|
442
|
-
<
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
backgroundColor: "#24283b",
|
|
446
|
-
},
|
|
447
|
-
wrapperOptions: {
|
|
448
|
-
backgroundColor: "#1f2335",
|
|
449
|
-
},
|
|
450
|
-
viewportOptions: {
|
|
451
|
-
backgroundColor: "#1a1b26",
|
|
452
|
-
},
|
|
453
|
-
contentOptions: {
|
|
454
|
-
backgroundColor: "#16161e",
|
|
455
|
-
},
|
|
456
|
-
scrollbarOptions: {
|
|
457
|
-
showArrows: true,
|
|
458
|
-
trackOptions: {
|
|
459
|
-
foregroundColor: "#7aa2f7",
|
|
460
|
-
backgroundColor: "#414868",
|
|
461
|
-
},
|
|
462
|
-
},
|
|
463
|
-
}}
|
|
464
|
-
focused
|
|
465
|
-
>
|
|
466
|
-
{Array.from({ length: 1000 }).map((_, i) => (
|
|
467
|
-
<box
|
|
468
|
-
key={i}
|
|
469
|
-
style={{ width: "100%", padding: 1, marginBottom: 1, backgroundColor: i % 2 === 0 ? "#292e42" : "#2f3449" }}
|
|
470
|
-
>
|
|
471
|
-
<text content={`Box ${i}`} />
|
|
472
|
-
</box>
|
|
473
|
-
))}
|
|
474
|
-
</scrollbox>
|
|
619
|
+
<box style={{ border: true, flexGrow: 1 }}>
|
|
620
|
+
<code content={codeExample} filetype="javascript" syntaxStyle={syntaxStyle} />
|
|
621
|
+
</box>
|
|
475
622
|
)
|
|
476
623
|
}
|
|
477
624
|
```
|
|
478
625
|
|
|
479
|
-
|
|
626
|
+
#### Line Number Component
|
|
480
627
|
|
|
481
|
-
Display
|
|
628
|
+
Display code with line numbers, and optionally add diff highlights or diagnostic indicators.
|
|
482
629
|
|
|
483
630
|
```tsx
|
|
484
|
-
import {
|
|
631
|
+
import type { LineNumberRenderable } from "@opentui/core"
|
|
632
|
+
import { RGBA, SyntaxStyle } from "@opentui/core"
|
|
633
|
+
import { useEffect, useRef } from "react"
|
|
485
634
|
|
|
486
635
|
function App() {
|
|
487
|
-
const
|
|
488
|
-
|
|
636
|
+
const lineNumberRef = useRef<LineNumberRenderable>(null)
|
|
637
|
+
|
|
638
|
+
const syntaxStyle = SyntaxStyle.fromStyles({
|
|
639
|
+
keyword: { fg: RGBA.fromHex("#C792EA") },
|
|
640
|
+
string: { fg: RGBA.fromHex("#C3E88D") },
|
|
641
|
+
number: { fg: RGBA.fromHex("#F78C6C") },
|
|
642
|
+
default: { fg: RGBA.fromHex("#A6ACCD") },
|
|
643
|
+
})
|
|
644
|
+
|
|
645
|
+
const codeContent = `function fibonacci(n: number): number {
|
|
646
|
+
if (n <= 1) return n
|
|
647
|
+
return fibonacci(n - 1) + fibonacci(n - 2)
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
console.log(fibonacci(10))`
|
|
651
|
+
|
|
652
|
+
useEffect(() => {
|
|
653
|
+
// Add diff highlight - line was added
|
|
654
|
+
lineNumberRef.current?.setLineColor(1, "#1a4d1a")
|
|
655
|
+
lineNumberRef.current?.setLineSign(1, { after: " +", afterColor: "#22c55e" })
|
|
656
|
+
|
|
657
|
+
// Add diagnostic indicator
|
|
658
|
+
lineNumberRef.current?.setLineSign(4, { before: "⚠️", beforeColor: "#f59e0b" })
|
|
659
|
+
}, [])
|
|
489
660
|
|
|
490
661
|
return (
|
|
491
|
-
<box style={{ border: true,
|
|
492
|
-
<
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
662
|
+
<box style={{ border: true, flexGrow: 1 }}>
|
|
663
|
+
<line-number
|
|
664
|
+
ref={lineNumberRef}
|
|
665
|
+
fg="#6b7280"
|
|
666
|
+
bg="#161b22"
|
|
667
|
+
minWidth={3}
|
|
668
|
+
paddingRight={1}
|
|
669
|
+
showLineNumbers={true}
|
|
670
|
+
width="100%"
|
|
671
|
+
height="100%"
|
|
498
672
|
>
|
|
499
|
-
<
|
|
500
|
-
|
|
501
|
-
onChange={(_, option) => setFont(option?.value)}
|
|
502
|
-
showScrollIndicator
|
|
503
|
-
options={[
|
|
504
|
-
{
|
|
505
|
-
name: "Tiny",
|
|
506
|
-
description: "Tiny font",
|
|
507
|
-
value: "tiny",
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
name: "Block",
|
|
511
|
-
description: "Block font",
|
|
512
|
-
value: "block",
|
|
513
|
-
},
|
|
514
|
-
{
|
|
515
|
-
name: "Slick",
|
|
516
|
-
description: "Slick font",
|
|
517
|
-
value: "slick",
|
|
518
|
-
},
|
|
519
|
-
{
|
|
520
|
-
name: "Shade",
|
|
521
|
-
description: "Shade font",
|
|
522
|
-
value: "shade",
|
|
523
|
-
},
|
|
524
|
-
]}
|
|
525
|
-
style={{ flexGrow: 1 }}
|
|
526
|
-
/>
|
|
527
|
-
</box>
|
|
528
|
-
|
|
529
|
-
<ascii-font text={text} font={font} />
|
|
673
|
+
<code content={codeContent} filetype="typescript" syntaxStyle={syntaxStyle} width="100%" height="100%" />
|
|
674
|
+
</line-number>
|
|
530
675
|
</box>
|
|
531
676
|
)
|
|
532
677
|
}
|
|
533
678
|
```
|
|
534
679
|
|
|
680
|
+
For a more complete example with interactive diff highlights and diagnostics, see [`examples/line-number.tsx`](examples/line-number.tsx).
|
|
681
|
+
|
|
682
|
+
#### Diff Component
|
|
683
|
+
|
|
684
|
+
Display unified or split-view diffs with syntax highlighting, customizable themes, and line number support. Supports multiple view modes (unified/split), word wrapping, and theme customization.
|
|
685
|
+
|
|
686
|
+
For a complete interactive example with theme switching and keybindings, see [`examples/diff.tsx`](examples/diff.tsx).
|
|
687
|
+
|
|
535
688
|
## Examples
|
|
536
689
|
|
|
537
690
|
### Login Form
|
package/index.js
CHANGED
|
@@ -4,7 +4,9 @@ import {
|
|
|
4
4
|
ASCIIFontRenderable,
|
|
5
5
|
BoxRenderable,
|
|
6
6
|
CodeRenderable,
|
|
7
|
+
DiffRenderable,
|
|
7
8
|
InputRenderable,
|
|
9
|
+
LineNumberRenderable,
|
|
8
10
|
ScrollBoxRenderable,
|
|
9
11
|
SelectRenderable,
|
|
10
12
|
TabSelectRenderable,
|
|
@@ -71,12 +73,14 @@ var baseComponents = {
|
|
|
71
73
|
box: BoxRenderable,
|
|
72
74
|
text: TextRenderable,
|
|
73
75
|
code: CodeRenderable,
|
|
76
|
+
diff: DiffRenderable,
|
|
74
77
|
input: InputRenderable,
|
|
75
78
|
select: SelectRenderable,
|
|
76
79
|
textarea: TextareaRenderable,
|
|
77
80
|
scrollbox: ScrollBoxRenderable,
|
|
78
81
|
"ascii-font": ASCIIFontRenderable,
|
|
79
82
|
"tab-select": TabSelectRenderable,
|
|
83
|
+
"line-number": LineNumberRenderable,
|
|
80
84
|
span: SpanRenderable,
|
|
81
85
|
br: LineBreakRenderable,
|
|
82
86
|
b: BoldSpanRenderable,
|
|
@@ -118,15 +122,21 @@ function useEffectEvent(handler) {
|
|
|
118
122
|
}
|
|
119
123
|
|
|
120
124
|
// src/hooks/use-keyboard.ts
|
|
121
|
-
var useKeyboard = (handler) => {
|
|
125
|
+
var useKeyboard = (handler, options = { release: false }) => {
|
|
122
126
|
const { keyHandler } = useAppContext();
|
|
123
127
|
const stableHandler = useEffectEvent(handler);
|
|
124
128
|
useEffect(() => {
|
|
125
129
|
keyHandler?.on("keypress", stableHandler);
|
|
130
|
+
if (options?.release) {
|
|
131
|
+
keyHandler?.on("keyrelease", stableHandler);
|
|
132
|
+
}
|
|
126
133
|
return () => {
|
|
127
134
|
keyHandler?.off("keypress", stableHandler);
|
|
135
|
+
if (options?.release) {
|
|
136
|
+
keyHandler?.off("keyrelease", stableHandler);
|
|
137
|
+
}
|
|
128
138
|
};
|
|
129
|
-
}, [keyHandler]);
|
|
139
|
+
}, [keyHandler, options.release]);
|
|
130
140
|
};
|
|
131
141
|
// src/hooks/use-renderer.ts
|
|
132
142
|
var useRenderer = () => {
|
package/jsx-namespace.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ import type {
|
|
|
3
3
|
AsciiFontProps,
|
|
4
4
|
BoxProps,
|
|
5
5
|
CodeProps,
|
|
6
|
+
DiffProps,
|
|
6
7
|
ExtendedIntrinsicElements,
|
|
7
8
|
InputProps,
|
|
8
9
|
LineBreakProps,
|
|
10
|
+
LineNumberProps,
|
|
9
11
|
OpenTUIComponents,
|
|
10
12
|
ScrollBoxProps,
|
|
11
13
|
SelectProps,
|
|
@@ -37,12 +39,14 @@ export namespace JSX {
|
|
|
37
39
|
text: TextProps
|
|
38
40
|
span: SpanProps
|
|
39
41
|
code: CodeProps
|
|
42
|
+
diff: DiffProps
|
|
40
43
|
input: InputProps
|
|
41
44
|
textarea: TextareaProps
|
|
42
45
|
select: SelectProps
|
|
43
46
|
scrollbox: ScrollBoxProps
|
|
44
47
|
"ascii-font": AsciiFontProps
|
|
45
48
|
"tab-select": TabSelectProps
|
|
49
|
+
"line-number": LineNumberProps
|
|
46
50
|
// Text modifiers
|
|
47
51
|
b: SpanProps
|
|
48
52
|
i: SpanProps
|
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.
|
|
7
|
+
"version": "0.1.54",
|
|
8
8
|
"description": "React renderer for building terminal user interfaces using OpenTUI core",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
}
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@opentui/core": "0.1.
|
|
43
|
+
"@opentui/core": "0.1.54",
|
|
44
44
|
"react-reconciler": "^0.32.0"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
@@ -1,16 +1,18 @@
|
|
|
1
|
-
import { ASCIIFontRenderable, BoxRenderable, CodeRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextareaRenderable, TextRenderable } from "@opentui/core";
|
|
1
|
+
import { ASCIIFontRenderable, BoxRenderable, CodeRenderable, DiffRenderable, InputRenderable, LineNumberRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextareaRenderable, TextRenderable } from "@opentui/core";
|
|
2
2
|
import type { RenderableConstructor } from "../types/components";
|
|
3
3
|
import { BoldSpanRenderable, ItalicSpanRenderable, LineBreakRenderable, SpanRenderable, UnderlineSpanRenderable } from "./text";
|
|
4
4
|
export declare const baseComponents: {
|
|
5
5
|
box: typeof BoxRenderable;
|
|
6
6
|
text: typeof TextRenderable;
|
|
7
7
|
code: typeof CodeRenderable;
|
|
8
|
+
diff: typeof DiffRenderable;
|
|
8
9
|
input: typeof InputRenderable;
|
|
9
10
|
select: typeof SelectRenderable;
|
|
10
11
|
textarea: typeof TextareaRenderable;
|
|
11
12
|
scrollbox: typeof ScrollBoxRenderable;
|
|
12
13
|
"ascii-font": typeof ASCIIFontRenderable;
|
|
13
14
|
"tab-select": typeof TabSelectRenderable;
|
|
15
|
+
"line-number": typeof LineNumberRenderable;
|
|
14
16
|
span: typeof SpanRenderable;
|
|
15
17
|
br: typeof LineBreakRenderable;
|
|
16
18
|
b: typeof BoldSpanRenderable;
|
|
@@ -1,2 +1,22 @@
|
|
|
1
1
|
import type { KeyEvent } from "@opentui/core";
|
|
2
|
-
export
|
|
2
|
+
export interface UseKeyboardOptions {
|
|
3
|
+
/** Include release events - callback receives events with eventType: "release" */
|
|
4
|
+
release?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Subscribe to keyboard events.
|
|
8
|
+
*
|
|
9
|
+
* By default, only receives press events (including key repeats with `repeated: true`).
|
|
10
|
+
* Use `options.release` to also receive release events.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* // Basic press handling (includes repeats)
|
|
14
|
+
* useKeyboard((e) => console.log(e.name, e.repeated ? "(repeat)" : ""))
|
|
15
|
+
*
|
|
16
|
+
* // With release events
|
|
17
|
+
* useKeyboard((e) => {
|
|
18
|
+
* if (e.eventType === "release") keys.delete(e.name)
|
|
19
|
+
* else keys.add(e.name)
|
|
20
|
+
* }, { release: true })
|
|
21
|
+
*/
|
|
22
|
+
export declare const useKeyboard: (handler: (key: KeyEvent) => void, options?: UseKeyboardOptions) => void;
|
|
@@ -53,7 +53,9 @@ import {
|
|
|
53
53
|
ASCIIFontRenderable,
|
|
54
54
|
BoxRenderable,
|
|
55
55
|
CodeRenderable,
|
|
56
|
+
DiffRenderable,
|
|
56
57
|
InputRenderable,
|
|
58
|
+
LineNumberRenderable,
|
|
57
59
|
ScrollBoxRenderable,
|
|
58
60
|
SelectRenderable,
|
|
59
61
|
TabSelectRenderable,
|
|
@@ -120,12 +122,14 @@ var baseComponents = {
|
|
|
120
122
|
box: BoxRenderable,
|
|
121
123
|
text: TextRenderable,
|
|
122
124
|
code: CodeRenderable,
|
|
125
|
+
diff: DiffRenderable,
|
|
123
126
|
input: InputRenderable,
|
|
124
127
|
select: SelectRenderable,
|
|
125
128
|
textarea: TextareaRenderable,
|
|
126
129
|
scrollbox: ScrollBoxRenderable,
|
|
127
130
|
"ascii-font": ASCIIFontRenderable,
|
|
128
131
|
"tab-select": TabSelectRenderable,
|
|
132
|
+
"line-number": LineNumberRenderable,
|
|
129
133
|
span: SpanRenderable,
|
|
130
134
|
br: LineBreakRenderable,
|
|
131
135
|
b: BoldSpanRenderable,
|
|
@@ -57,7 +57,9 @@ import {
|
|
|
57
57
|
ASCIIFontRenderable,
|
|
58
58
|
BoxRenderable,
|
|
59
59
|
CodeRenderable,
|
|
60
|
+
DiffRenderable,
|
|
60
61
|
InputRenderable,
|
|
62
|
+
LineNumberRenderable,
|
|
61
63
|
ScrollBoxRenderable,
|
|
62
64
|
SelectRenderable,
|
|
63
65
|
TabSelectRenderable,
|
|
@@ -124,12 +126,14 @@ var baseComponents = {
|
|
|
124
126
|
box: BoxRenderable,
|
|
125
127
|
text: TextRenderable,
|
|
126
128
|
code: CodeRenderable,
|
|
129
|
+
diff: DiffRenderable,
|
|
127
130
|
input: InputRenderable,
|
|
128
131
|
select: SelectRenderable,
|
|
129
132
|
textarea: TextareaRenderable,
|
|
130
133
|
scrollbox: ScrollBoxRenderable,
|
|
131
134
|
"ascii-font": ASCIIFontRenderable,
|
|
132
135
|
"tab-select": TabSelectRenderable,
|
|
136
|
+
"line-number": LineNumberRenderable,
|
|
133
137
|
span: SpanRenderable,
|
|
134
138
|
br: LineBreakRenderable,
|
|
135
139
|
b: BoldSpanRenderable,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, CodeOptions, CodeRenderable, InputRenderable, InputRenderableOptions, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextareaOptions, TextareaRenderable, TextNodeOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
|
|
1
|
+
import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, CodeOptions, CodeRenderable, DiffRenderable, DiffRenderableOptions, InputRenderable, InputRenderableOptions, LineNumberOptions, LineNumberRenderable, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextareaOptions, TextareaRenderable, TextNodeOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
|
|
2
2
|
import type React from "react";
|
|
3
3
|
/** Properties that should not be included in the style prop */
|
|
4
4
|
export type NonStyledProps = "id" | "buffered" | "live" | "enableLayout" | "selectable" | "renderAfter" | "renderBefore" | `on${string}`;
|
|
@@ -43,6 +43,7 @@ export type TextareaProps = ComponentProps<TextareaOptions, TextareaRenderable>
|
|
|
43
43
|
focused?: boolean;
|
|
44
44
|
};
|
|
45
45
|
export type CodeProps = ComponentProps<CodeOptions, CodeRenderable>;
|
|
46
|
+
export type DiffProps = ComponentProps<DiffRenderableOptions, DiffRenderable>;
|
|
46
47
|
export type SelectProps = ComponentProps<SelectRenderableOptions, SelectRenderable> & {
|
|
47
48
|
focused?: boolean;
|
|
48
49
|
onChange?: (index: number, option: SelectOption | null) => void;
|
|
@@ -57,6 +58,9 @@ export type TabSelectProps = ComponentProps<TabSelectRenderableOptions, TabSelec
|
|
|
57
58
|
onChange?: (index: number, option: TabSelectOption | null) => void;
|
|
58
59
|
onSelect?: (index: number, option: TabSelectOption | null) => void;
|
|
59
60
|
};
|
|
61
|
+
export type LineNumberProps = ComponentProps<ContainerProps<LineNumberOptions>, LineNumberRenderable> & {
|
|
62
|
+
focused?: boolean;
|
|
63
|
+
};
|
|
60
64
|
/** Convert renderable constructor to component props with proper style exclusions */
|
|
61
65
|
export type ExtendedComponentProps<TConstructor extends RenderableConstructor, TOptions = ExtractRenderableOptions<TConstructor>> = TOptions & {
|
|
62
66
|
children?: React.ReactNode;
|