@opentui/react 0.1.34 → 0.1.36
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 +107 -21
- package/index.js +42 -29
- package/jsx-namespace.d.ts +4 -0
- package/package.json +2 -2
- package/src/components/index.d.ts +3 -1
- package/src/reconciler/renderer.d.ts +17 -1
- package/src/reconciler/renderer.js +43 -30
- package/src/types/components.d.ts +6 -2
package/README.md
CHANGED
|
@@ -19,13 +19,15 @@ bun install @opentui/react @opentui/core react
|
|
|
19
19
|
## Quick Start
|
|
20
20
|
|
|
21
21
|
```tsx
|
|
22
|
-
import {
|
|
22
|
+
import { createCliRenderer } from "@opentui/core"
|
|
23
|
+
import { createRoot } from "@opentui/react"
|
|
23
24
|
|
|
24
25
|
function App() {
|
|
25
26
|
return <text>Hello, world!</text>
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
const renderer = await createCliRenderer()
|
|
30
|
+
createRoot(renderer).render(<App />)
|
|
29
31
|
```
|
|
30
32
|
|
|
31
33
|
## TypeScript Configuration
|
|
@@ -83,23 +85,32 @@ Components can be styled using props or the `style` prop:
|
|
|
83
85
|
|
|
84
86
|
## API Reference
|
|
85
87
|
|
|
86
|
-
### `
|
|
88
|
+
### `createRoot(renderer)`
|
|
87
89
|
|
|
88
|
-
|
|
90
|
+
Creates a root for rendering a React tree with the given CLI renderer.
|
|
89
91
|
|
|
90
92
|
```tsx
|
|
91
|
-
import {
|
|
93
|
+
import { createCliRenderer } from "@opentui/core"
|
|
94
|
+
import { createRoot } from "@opentui/react"
|
|
92
95
|
|
|
93
|
-
await
|
|
96
|
+
const renderer = await createCliRenderer({
|
|
94
97
|
// Optional renderer configuration
|
|
95
98
|
exitOnCtrlC: false,
|
|
96
99
|
})
|
|
100
|
+
createRoot(renderer).render(<App />)
|
|
97
101
|
```
|
|
98
102
|
|
|
99
103
|
**Parameters:**
|
|
100
104
|
|
|
101
|
-
- `
|
|
102
|
-
|
|
105
|
+
- `renderer`: A `CliRenderer` instance (typically created with `createCliRenderer()`)
|
|
106
|
+
|
|
107
|
+
**Returns:** An object with a `render` method that accepts a React element.
|
|
108
|
+
|
|
109
|
+
### `render(element, config?)` (Deprecated)
|
|
110
|
+
|
|
111
|
+
> **Deprecated:** Use `createRoot(renderer).render(node)` instead.
|
|
112
|
+
|
|
113
|
+
Renders a React element to the terminal. This function is deprecated in favor of `createRoot`.
|
|
103
114
|
|
|
104
115
|
### Hooks
|
|
105
116
|
|
|
@@ -193,7 +204,7 @@ function App() {
|
|
|
193
204
|
Create and manage animations using OpenTUI's timeline system. This hook automatically registers and unregisters the timeline with the animation engine.
|
|
194
205
|
|
|
195
206
|
```tsx
|
|
196
|
-
import {
|
|
207
|
+
import { useTimeline } from "@opentui/react"
|
|
197
208
|
import { useEffect, useState } from "react"
|
|
198
209
|
|
|
199
210
|
function App() {
|
|
@@ -326,6 +337,66 @@ function App() {
|
|
|
326
337
|
}
|
|
327
338
|
```
|
|
328
339
|
|
|
340
|
+
### Textarea Component
|
|
341
|
+
|
|
342
|
+
```tsx
|
|
343
|
+
import type { TextareaRenderable } from "@opentui/core"
|
|
344
|
+
import { useKeyboard, useRenderer } from "@opentui/react"
|
|
345
|
+
import { useEffect, useRef } from "react"
|
|
346
|
+
|
|
347
|
+
function App() {
|
|
348
|
+
const renderer = useRenderer()
|
|
349
|
+
const textareaRef = useRef<TextareaRenderable>(null)
|
|
350
|
+
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
renderer.console.show()
|
|
353
|
+
}, [renderer])
|
|
354
|
+
|
|
355
|
+
useKeyboard((key) => {
|
|
356
|
+
if (key.name === "return") {
|
|
357
|
+
console.log(textareaRef.current?.plainText)
|
|
358
|
+
}
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
return (
|
|
362
|
+
<box title="Interactive Editor" style={{ border: true, flexGrow: 1 }}>
|
|
363
|
+
<textarea ref={textareaRef} placeholder="Type here..." focused />
|
|
364
|
+
</box>
|
|
365
|
+
)
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Code Component
|
|
370
|
+
|
|
371
|
+
```tsx
|
|
372
|
+
import { RGBA, SyntaxStyle } from "@opentui/core"
|
|
373
|
+
|
|
374
|
+
const syntaxStyle = SyntaxStyle.fromStyles({
|
|
375
|
+
keyword: { fg: RGBA.fromHex("#ff6b6b"), bold: true }, // red, bold
|
|
376
|
+
string: { fg: RGBA.fromHex("#51cf66") }, // green
|
|
377
|
+
comment: { fg: RGBA.fromHex("#868e96"), italic: true }, // gray, italic
|
|
378
|
+
number: { fg: RGBA.fromHex("#ffd43b") }, // yellow
|
|
379
|
+
default: { fg: RGBA.fromHex("#ffffff") }, // white
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
const codeExample = `function hello() {
|
|
383
|
+
// This is a comment
|
|
384
|
+
|
|
385
|
+
const message = "Hello, world!"
|
|
386
|
+
const count = 42
|
|
387
|
+
|
|
388
|
+
return message + " " + count
|
|
389
|
+
}`
|
|
390
|
+
|
|
391
|
+
function App() {
|
|
392
|
+
return (
|
|
393
|
+
<box style={{ border: true, flexGrow: 1 }}>
|
|
394
|
+
<code content={codeExample} filetype="javascript" syntaxStyle={syntaxStyle} />
|
|
395
|
+
</box>
|
|
396
|
+
)
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
329
400
|
### Select Component
|
|
330
401
|
|
|
331
402
|
Dropdown selection component.
|
|
@@ -470,7 +541,8 @@ function App() {
|
|
|
470
541
|
### Login Form
|
|
471
542
|
|
|
472
543
|
```tsx
|
|
473
|
-
import {
|
|
544
|
+
import { createCliRenderer } from "@opentui/core"
|
|
545
|
+
import { createRoot, useKeyboard } from "@opentui/react"
|
|
474
546
|
import { useCallback, useState } from "react"
|
|
475
547
|
|
|
476
548
|
function App() {
|
|
@@ -526,13 +598,15 @@ function App() {
|
|
|
526
598
|
)
|
|
527
599
|
}
|
|
528
600
|
|
|
529
|
-
|
|
601
|
+
const renderer = await createCliRenderer()
|
|
602
|
+
createRoot(renderer).render(<App />)
|
|
530
603
|
```
|
|
531
604
|
|
|
532
605
|
### Counter with Timer
|
|
533
606
|
|
|
534
607
|
```tsx
|
|
535
|
-
import {
|
|
608
|
+
import { createCliRenderer } from "@opentui/core"
|
|
609
|
+
import { createRoot } from "@opentui/react"
|
|
536
610
|
import { useEffect, useState } from "react"
|
|
537
611
|
|
|
538
612
|
function App() {
|
|
@@ -553,14 +627,15 @@ function App() {
|
|
|
553
627
|
)
|
|
554
628
|
}
|
|
555
629
|
|
|
556
|
-
|
|
630
|
+
const renderer = await createCliRenderer()
|
|
631
|
+
createRoot(renderer).render(<App />)
|
|
557
632
|
```
|
|
558
633
|
|
|
559
634
|
### System Monitor Animation
|
|
560
635
|
|
|
561
636
|
```tsx
|
|
562
|
-
import { TextAttributes } from "@opentui/core"
|
|
563
|
-
import {
|
|
637
|
+
import { createCliRenderer, TextAttributes } from "@opentui/core"
|
|
638
|
+
import { createRoot, useTimeline } from "@opentui/react"
|
|
564
639
|
import { useEffect, useState } from "react"
|
|
565
640
|
|
|
566
641
|
type Stats = {
|
|
@@ -636,13 +711,15 @@ export const App = () => {
|
|
|
636
711
|
)
|
|
637
712
|
}
|
|
638
713
|
|
|
639
|
-
|
|
714
|
+
const renderer = await createCliRenderer()
|
|
715
|
+
createRoot(renderer).render(<App />)
|
|
640
716
|
```
|
|
641
717
|
|
|
642
718
|
### Styled Text Showcase
|
|
643
719
|
|
|
644
720
|
```tsx
|
|
645
|
-
import {
|
|
721
|
+
import { createCliRenderer } from "@opentui/core"
|
|
722
|
+
import { createRoot } from "@opentui/react"
|
|
646
723
|
|
|
647
724
|
function App() {
|
|
648
725
|
return (
|
|
@@ -670,7 +747,8 @@ function App() {
|
|
|
670
747
|
)
|
|
671
748
|
}
|
|
672
749
|
|
|
673
|
-
|
|
750
|
+
const renderer = await createCliRenderer()
|
|
751
|
+
createRoot(renderer).render(<App />)
|
|
674
752
|
```
|
|
675
753
|
|
|
676
754
|
## Component Extension
|
|
@@ -678,8 +756,15 @@ render(<App />)
|
|
|
678
756
|
You can create custom components by extending OpenTUIs base renderables:
|
|
679
757
|
|
|
680
758
|
```tsx
|
|
681
|
-
import {
|
|
682
|
-
|
|
759
|
+
import {
|
|
760
|
+
BoxRenderable,
|
|
761
|
+
createCliRenderer,
|
|
762
|
+
OptimizedBuffer,
|
|
763
|
+
RGBA,
|
|
764
|
+
type BoxOptions,
|
|
765
|
+
type RenderContext,
|
|
766
|
+
} from "@opentui/core"
|
|
767
|
+
import { createRoot, extend } from "@opentui/react"
|
|
683
768
|
|
|
684
769
|
// Create custom component class
|
|
685
770
|
class ButtonRenderable extends BoxRenderable {
|
|
@@ -733,5 +818,6 @@ function App() {
|
|
|
733
818
|
)
|
|
734
819
|
}
|
|
735
820
|
|
|
736
|
-
|
|
821
|
+
const renderer = await createCliRenderer()
|
|
822
|
+
createRoot(renderer).render(<App />)
|
|
737
823
|
```
|
package/index.js
CHANGED
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
import {
|
|
4
4
|
ASCIIFontRenderable,
|
|
5
5
|
BoxRenderable,
|
|
6
|
+
CodeRenderable,
|
|
6
7
|
InputRenderable,
|
|
7
8
|
ScrollBoxRenderable,
|
|
8
9
|
SelectRenderable,
|
|
9
10
|
TabSelectRenderable,
|
|
11
|
+
TextareaRenderable,
|
|
10
12
|
TextRenderable
|
|
11
13
|
} from "@opentui/core";
|
|
12
14
|
|
|
@@ -68,8 +70,10 @@ class LineBreakRenderable extends SpanRenderable {
|
|
|
68
70
|
var baseComponents = {
|
|
69
71
|
box: BoxRenderable,
|
|
70
72
|
text: TextRenderable,
|
|
73
|
+
code: CodeRenderable,
|
|
71
74
|
input: InputRenderable,
|
|
72
75
|
select: SelectRenderable,
|
|
76
|
+
textarea: TextareaRenderable,
|
|
73
77
|
scrollbox: ScrollBoxRenderable,
|
|
74
78
|
"ascii-font": ASCIIFontRenderable,
|
|
75
79
|
"tab-select": TabSelectRenderable,
|
|
@@ -180,6 +184,35 @@ var useTimeline = (options = {}) => {
|
|
|
180
184
|
import { createCliRenderer, engine as engine2 } from "@opentui/core";
|
|
181
185
|
import React2 from "react";
|
|
182
186
|
|
|
187
|
+
// src/components/error-boundary.tsx
|
|
188
|
+
import React from "react";
|
|
189
|
+
|
|
190
|
+
// jsx-dev-runtime.js
|
|
191
|
+
import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
|
|
192
|
+
|
|
193
|
+
// src/components/error-boundary.tsx
|
|
194
|
+
class ErrorBoundary extends React.Component {
|
|
195
|
+
constructor(props) {
|
|
196
|
+
super(props);
|
|
197
|
+
this.state = { hasError: false, error: null };
|
|
198
|
+
}
|
|
199
|
+
static getDerivedStateFromError(error) {
|
|
200
|
+
return { hasError: true, error };
|
|
201
|
+
}
|
|
202
|
+
render() {
|
|
203
|
+
if (this.state.hasError && this.state.error) {
|
|
204
|
+
return /* @__PURE__ */ jsxDEV("box", {
|
|
205
|
+
style: { flexDirection: "column", padding: 2 },
|
|
206
|
+
children: /* @__PURE__ */ jsxDEV("text", {
|
|
207
|
+
fg: "red",
|
|
208
|
+
children: this.state.error.stack || this.state.error.message
|
|
209
|
+
}, undefined, false, undefined, this)
|
|
210
|
+
}, undefined, false, undefined, this);
|
|
211
|
+
}
|
|
212
|
+
return this.props.children;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
183
216
|
// src/reconciler/reconciler.ts
|
|
184
217
|
import ReactReconciler from "react-reconciler";
|
|
185
218
|
import { ConcurrentRoot } from "react-reconciler/constants";
|
|
@@ -473,41 +506,20 @@ function _render(element, root) {
|
|
|
473
506
|
reconciler.updateContainer(element, container, null, () => {});
|
|
474
507
|
}
|
|
475
508
|
|
|
476
|
-
// src/components/error-boundary.tsx
|
|
477
|
-
import React from "react";
|
|
478
|
-
|
|
479
|
-
// jsx-dev-runtime.js
|
|
480
|
-
import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
|
|
481
|
-
|
|
482
|
-
// src/components/error-boundary.tsx
|
|
483
|
-
class ErrorBoundary extends React.Component {
|
|
484
|
-
constructor(props) {
|
|
485
|
-
super(props);
|
|
486
|
-
this.state = { hasError: false, error: null };
|
|
487
|
-
}
|
|
488
|
-
static getDerivedStateFromError(error) {
|
|
489
|
-
return { hasError: true, error };
|
|
490
|
-
}
|
|
491
|
-
render() {
|
|
492
|
-
if (this.state.hasError && this.state.error) {
|
|
493
|
-
return /* @__PURE__ */ jsxDEV("box", {
|
|
494
|
-
style: { flexDirection: "column", padding: 2 },
|
|
495
|
-
children: /* @__PURE__ */ jsxDEV("text", {
|
|
496
|
-
fg: "red",
|
|
497
|
-
children: this.state.error.stack || this.state.error.message
|
|
498
|
-
}, undefined, false, undefined, this)
|
|
499
|
-
}, undefined, false, undefined, this);
|
|
500
|
-
}
|
|
501
|
-
return this.props.children;
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
|
|
505
509
|
// src/reconciler/renderer.ts
|
|
506
510
|
async function render(node, rendererConfig = {}) {
|
|
507
511
|
const renderer = await createCliRenderer(rendererConfig);
|
|
508
512
|
engine2.attach(renderer);
|
|
509
513
|
_render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
|
|
510
514
|
}
|
|
515
|
+
function createRoot(renderer) {
|
|
516
|
+
return {
|
|
517
|
+
render: (node) => {
|
|
518
|
+
engine2.attach(renderer);
|
|
519
|
+
_render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
}
|
|
511
523
|
export {
|
|
512
524
|
useTimeline,
|
|
513
525
|
useTerminalDimensions,
|
|
@@ -518,6 +530,7 @@ export {
|
|
|
518
530
|
render,
|
|
519
531
|
getComponentCatalogue,
|
|
520
532
|
extend,
|
|
533
|
+
createRoot,
|
|
521
534
|
componentCatalogue,
|
|
522
535
|
baseComponents,
|
|
523
536
|
AppContext
|
package/jsx-namespace.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type * as React from "react"
|
|
|
2
2
|
import type {
|
|
3
3
|
AsciiFontProps,
|
|
4
4
|
BoxProps,
|
|
5
|
+
CodeProps,
|
|
5
6
|
ExtendedIntrinsicElements,
|
|
6
7
|
InputProps,
|
|
7
8
|
LineBreakProps,
|
|
@@ -10,6 +11,7 @@ import type {
|
|
|
10
11
|
SelectProps,
|
|
11
12
|
SpanProps,
|
|
12
13
|
TabSelectProps,
|
|
14
|
+
TextareaProps,
|
|
13
15
|
TextProps,
|
|
14
16
|
} from "./src/types/components"
|
|
15
17
|
|
|
@@ -34,7 +36,9 @@ export namespace JSX {
|
|
|
34
36
|
box: BoxProps
|
|
35
37
|
text: TextProps
|
|
36
38
|
span: SpanProps
|
|
39
|
+
code: CodeProps
|
|
37
40
|
input: InputProps
|
|
41
|
+
textarea: TextareaProps
|
|
38
42
|
select: SelectProps
|
|
39
43
|
scrollbox: ScrollBoxProps
|
|
40
44
|
"ascii-font": AsciiFontProps
|
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.36",
|
|
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.
|
|
38
|
+
"@opentui/core": "0.1.36",
|
|
39
39
|
"react-reconciler": "^0.32.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { ASCIIFontRenderable, BoxRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
|
|
1
|
+
import { ASCIIFontRenderable, BoxRenderable, CodeRenderable, InputRenderable, 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
|
+
code: typeof CodeRenderable;
|
|
7
8
|
input: typeof InputRenderable;
|
|
8
9
|
select: typeof SelectRenderable;
|
|
10
|
+
textarea: typeof TextareaRenderable;
|
|
9
11
|
scrollbox: typeof ScrollBoxRenderable;
|
|
10
12
|
"ascii-font": typeof ASCIIFontRenderable;
|
|
11
13
|
"tab-select": typeof TabSelectRenderable;
|
|
@@ -1,3 +1,19 @@
|
|
|
1
|
-
import { type CliRendererConfig } from "@opentui/core";
|
|
1
|
+
import { CliRenderer, type CliRendererConfig } from "@opentui/core";
|
|
2
2
|
import { type ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* @deprecated Use `createRoot(renderer).render(node)` instead
|
|
5
|
+
*/
|
|
3
6
|
export declare function render(node: ReactNode, rendererConfig?: CliRendererConfig): Promise<void>;
|
|
7
|
+
/**
|
|
8
|
+
* Creates a root for rendering a React tree with the given CLI renderer.
|
|
9
|
+
* @param renderer The CLI renderer to use
|
|
10
|
+
* @returns A root object with a `render` method
|
|
11
|
+
* @example
|
|
12
|
+
* ```tsx
|
|
13
|
+
* const renderer = await createCliRenderer()
|
|
14
|
+
* createRoot(renderer).render(<App />)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function createRoot(renderer: CliRenderer): {
|
|
18
|
+
render: (node: ReactNode) => void;
|
|
19
|
+
};
|
|
@@ -10,6 +10,35 @@ var AppContext = createContext({
|
|
|
10
10
|
renderer: null
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
+
// src/components/error-boundary.tsx
|
|
14
|
+
import React from "react";
|
|
15
|
+
|
|
16
|
+
// jsx-dev-runtime.js
|
|
17
|
+
import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
|
|
18
|
+
|
|
19
|
+
// src/components/error-boundary.tsx
|
|
20
|
+
class ErrorBoundary extends React.Component {
|
|
21
|
+
constructor(props) {
|
|
22
|
+
super(props);
|
|
23
|
+
this.state = { hasError: false, error: null };
|
|
24
|
+
}
|
|
25
|
+
static getDerivedStateFromError(error) {
|
|
26
|
+
return { hasError: true, error };
|
|
27
|
+
}
|
|
28
|
+
render() {
|
|
29
|
+
if (this.state.hasError && this.state.error) {
|
|
30
|
+
return /* @__PURE__ */ jsxDEV("box", {
|
|
31
|
+
style: { flexDirection: "column", padding: 2 },
|
|
32
|
+
children: /* @__PURE__ */ jsxDEV("text", {
|
|
33
|
+
fg: "red",
|
|
34
|
+
children: this.state.error.stack || this.state.error.message
|
|
35
|
+
}, undefined, false, undefined, this)
|
|
36
|
+
}, undefined, false, undefined, this);
|
|
37
|
+
}
|
|
38
|
+
return this.props.children;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
13
42
|
// src/reconciler/reconciler.ts
|
|
14
43
|
import ReactReconciler from "react-reconciler";
|
|
15
44
|
import { ConcurrentRoot } from "react-reconciler/constants";
|
|
@@ -23,10 +52,12 @@ import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constant
|
|
|
23
52
|
import {
|
|
24
53
|
ASCIIFontRenderable,
|
|
25
54
|
BoxRenderable,
|
|
55
|
+
CodeRenderable,
|
|
26
56
|
InputRenderable,
|
|
27
57
|
ScrollBoxRenderable,
|
|
28
58
|
SelectRenderable,
|
|
29
59
|
TabSelectRenderable,
|
|
60
|
+
TextareaRenderable,
|
|
30
61
|
TextRenderable
|
|
31
62
|
} from "@opentui/core";
|
|
32
63
|
|
|
@@ -88,8 +119,10 @@ class LineBreakRenderable extends SpanRenderable {
|
|
|
88
119
|
var baseComponents = {
|
|
89
120
|
box: BoxRenderable,
|
|
90
121
|
text: TextRenderable,
|
|
122
|
+
code: CodeRenderable,
|
|
91
123
|
input: InputRenderable,
|
|
92
124
|
select: SelectRenderable,
|
|
125
|
+
textarea: TextareaRenderable,
|
|
93
126
|
scrollbox: ScrollBoxRenderable,
|
|
94
127
|
"ascii-font": ASCIIFontRenderable,
|
|
95
128
|
"tab-select": TabSelectRenderable,
|
|
@@ -390,41 +423,21 @@ function _render(element, root) {
|
|
|
390
423
|
reconciler.updateContainer(element, container, null, () => {});
|
|
391
424
|
}
|
|
392
425
|
|
|
393
|
-
// src/components/error-boundary.tsx
|
|
394
|
-
import React from "react";
|
|
395
|
-
|
|
396
|
-
// jsx-dev-runtime.js
|
|
397
|
-
import { Fragment, jsxDEV } from "react/jsx-dev-runtime";
|
|
398
|
-
|
|
399
|
-
// src/components/error-boundary.tsx
|
|
400
|
-
class ErrorBoundary extends React.Component {
|
|
401
|
-
constructor(props) {
|
|
402
|
-
super(props);
|
|
403
|
-
this.state = { hasError: false, error: null };
|
|
404
|
-
}
|
|
405
|
-
static getDerivedStateFromError(error) {
|
|
406
|
-
return { hasError: true, error };
|
|
407
|
-
}
|
|
408
|
-
render() {
|
|
409
|
-
if (this.state.hasError && this.state.error) {
|
|
410
|
-
return /* @__PURE__ */ jsxDEV("box", {
|
|
411
|
-
style: { flexDirection: "column", padding: 2 },
|
|
412
|
-
children: /* @__PURE__ */ jsxDEV("text", {
|
|
413
|
-
fg: "red",
|
|
414
|
-
children: this.state.error.stack || this.state.error.message
|
|
415
|
-
}, undefined, false, undefined, this)
|
|
416
|
-
}, undefined, false, undefined, this);
|
|
417
|
-
}
|
|
418
|
-
return this.props.children;
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
|
|
422
426
|
// src/reconciler/renderer.ts
|
|
423
427
|
async function render(node, rendererConfig = {}) {
|
|
424
428
|
const renderer = await createCliRenderer(rendererConfig);
|
|
425
429
|
engine.attach(renderer);
|
|
426
430
|
_render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
|
|
427
431
|
}
|
|
432
|
+
function createRoot(renderer) {
|
|
433
|
+
return {
|
|
434
|
+
render: (node) => {
|
|
435
|
+
engine.attach(renderer);
|
|
436
|
+
_render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
}
|
|
428
440
|
export {
|
|
429
|
-
render
|
|
441
|
+
render,
|
|
442
|
+
createRoot
|
|
430
443
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, InputRenderable, InputRenderableOptions, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextNodeOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
|
|
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";
|
|
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}`;
|
|
@@ -14,7 +14,7 @@ type ExtractRenderableOptions<TConstructor> = TConstructor extends new (ctx: Ren
|
|
|
14
14
|
/** Extract the renderable type from a constructor */
|
|
15
15
|
type ExtractRenderable<TConstructor> = TConstructor extends new (ctx: RenderContext, options: any) => infer TRenderable ? TRenderable : never;
|
|
16
16
|
/** Determine which properties should be excluded from styling for different renderable types */
|
|
17
|
-
export type GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : NonStyledProps;
|
|
17
|
+
export type GetNonStyledProperties<TConstructor> = TConstructor extends RenderableConstructor<TextRenderable> ? NonStyledProps | "content" : TConstructor extends RenderableConstructor<BoxRenderable> ? NonStyledProps | "title" : TConstructor extends RenderableConstructor<ASCIIFontRenderable> ? NonStyledProps | "text" | "selectable" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : TConstructor extends RenderableConstructor<TextareaRenderable> ? NonStyledProps | "placeholder" | "initialValue" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" | "conceal" | "drawUnstyledText" : NonStyledProps;
|
|
18
18
|
/** Base props for container components that accept children */
|
|
19
19
|
type ContainerProps<TOptions> = TOptions & {
|
|
20
20
|
children?: React.ReactNode;
|
|
@@ -39,6 +39,10 @@ export type InputProps = ComponentProps<InputRenderableOptions, InputRenderable>
|
|
|
39
39
|
onChange?: (value: string) => void;
|
|
40
40
|
onSubmit?: (value: string) => void;
|
|
41
41
|
};
|
|
42
|
+
export type TextareaProps = ComponentProps<TextareaOptions, TextareaRenderable> & {
|
|
43
|
+
focused?: boolean;
|
|
44
|
+
};
|
|
45
|
+
export type CodeProps = ComponentProps<CodeOptions, CodeRenderable>;
|
|
42
46
|
export type SelectProps = ComponentProps<SelectRenderableOptions, SelectRenderable> & {
|
|
43
47
|
focused?: boolean;
|
|
44
48
|
onChange?: (index: number, option: SelectOption | null) => void;
|