@opentui/react 0.0.0-20260829-c2e7cb1b → 0.0.0-20260929-c138f0f8
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 +43 -15
- package/{chunk-c3qd75wx.js → chunk-tc772gtx.js} +19 -24
- package/{chunk-bdqvmfwv.js → chunk-z62dbhvj.js} +0 -4
- package/index.js +45 -41
- package/package.json +3 -3
- package/test-utils.js +3 -4
- package/chunk-2mx7fq49.js +0 -4
package/README.md
CHANGED
|
@@ -684,16 +684,23 @@ function App() {
|
|
|
684
684
|
|
|
685
685
|
#### Code Component
|
|
686
686
|
|
|
687
|
+
Create a theme once for its renderer, not during a component render. Release it after its consumers are destroyed.
|
|
688
|
+
|
|
687
689
|
```tsx
|
|
688
|
-
import { RGBA, SyntaxStyle } from "@opentui/core"
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
}
|
|
690
|
+
import { createCliRenderer, RGBA, SyntaxStyle } from "@opentui/core"
|
|
691
|
+
import { createRoot } from "@opentui/react"
|
|
692
|
+
|
|
693
|
+
const renderer = await createCliRenderer()
|
|
694
|
+
const syntaxStyle = SyntaxStyle.fromStyles(
|
|
695
|
+
{
|
|
696
|
+
keyword: { fg: RGBA.fromHex("#ff6b6b"), bold: true }, // red, bold
|
|
697
|
+
string: { fg: RGBA.fromHex("#51cf66") }, // green
|
|
698
|
+
comment: { fg: RGBA.fromHex("#868e96"), italic: true }, // gray, italic
|
|
699
|
+
number: { fg: RGBA.fromHex("#ffd43b") }, // yellow
|
|
700
|
+
default: { fg: RGBA.fromHex("#ffffff") }, // white
|
|
701
|
+
},
|
|
702
|
+
renderer.nativeScene!,
|
|
703
|
+
)
|
|
697
704
|
|
|
698
705
|
const codeExample = `function hello() {
|
|
699
706
|
// This is a comment
|
|
@@ -711,6 +718,14 @@ function App() {
|
|
|
711
718
|
</box>
|
|
712
719
|
)
|
|
713
720
|
}
|
|
721
|
+
|
|
722
|
+
try {
|
|
723
|
+
createRoot(renderer).render(<App />)
|
|
724
|
+
await renderer.closed
|
|
725
|
+
} finally {
|
|
726
|
+
renderer.destroy()
|
|
727
|
+
syntaxStyle.destroy()
|
|
728
|
+
}
|
|
714
729
|
```
|
|
715
730
|
|
|
716
731
|
#### Line Number Component
|
|
@@ -719,18 +734,23 @@ Display code with line numbers, and optionally add diff highlights or diagnostic
|
|
|
719
734
|
|
|
720
735
|
```tsx
|
|
721
736
|
import type { LineNumberRenderable } from "@opentui/core"
|
|
722
|
-
import { RGBA, SyntaxStyle } from "@opentui/core"
|
|
737
|
+
import { createCliRenderer, RGBA, SyntaxStyle } from "@opentui/core"
|
|
738
|
+
import { createRoot } from "@opentui/react"
|
|
723
739
|
import { useEffect, useRef } from "react"
|
|
724
740
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
const syntaxStyle = SyntaxStyle.fromStyles({
|
|
741
|
+
const renderer = await createCliRenderer()
|
|
742
|
+
const syntaxStyle = SyntaxStyle.fromStyles(
|
|
743
|
+
{
|
|
729
744
|
keyword: { fg: RGBA.fromHex("#C792EA") },
|
|
730
745
|
string: { fg: RGBA.fromHex("#C3E88D") },
|
|
731
746
|
number: { fg: RGBA.fromHex("#F78C6C") },
|
|
732
747
|
default: { fg: RGBA.fromHex("#A6ACCD") },
|
|
733
|
-
}
|
|
748
|
+
},
|
|
749
|
+
renderer.nativeScene!,
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
function App() {
|
|
753
|
+
const lineNumberRef = useRef<LineNumberRenderable>(null)
|
|
734
754
|
|
|
735
755
|
const codeContent = `function fibonacci(n: number): number {
|
|
736
756
|
if (n <= 1) return n
|
|
@@ -765,6 +785,14 @@ console.log(fibonacci(10))`
|
|
|
765
785
|
</box>
|
|
766
786
|
)
|
|
767
787
|
}
|
|
788
|
+
|
|
789
|
+
try {
|
|
790
|
+
createRoot(renderer).render(<App />)
|
|
791
|
+
await renderer.closed
|
|
792
|
+
} finally {
|
|
793
|
+
renderer.destroy()
|
|
794
|
+
syntaxStyle.destroy()
|
|
795
|
+
}
|
|
768
796
|
```
|
|
769
797
|
|
|
770
798
|
For a more complete example with interactive diff highlights and diagnostics, see [`examples/line-number.tsx`](examples/line-number.tsx).
|
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import {
|
|
3
|
-
__require
|
|
4
|
-
} from "./chunk-2mx7fq49.js";
|
|
5
|
-
|
|
6
2
|
// src/components/index.ts
|
|
7
3
|
import {
|
|
8
4
|
ASCIIFontRenderable,
|
|
@@ -85,7 +81,7 @@ class LinkRenderable extends SpanRenderable {
|
|
|
85
81
|
}
|
|
86
82
|
|
|
87
83
|
// src/components/index.ts
|
|
88
|
-
var
|
|
84
|
+
var baseComponents2 = {
|
|
89
85
|
box: BoxRenderable,
|
|
90
86
|
text: TextRenderable,
|
|
91
87
|
code: CodeRenderable,
|
|
@@ -108,26 +104,26 @@ var baseComponents = {
|
|
|
108
104
|
u: UnderlineSpanRenderable,
|
|
109
105
|
a: LinkRenderable
|
|
110
106
|
};
|
|
111
|
-
var
|
|
112
|
-
function
|
|
113
|
-
Object.assign(
|
|
107
|
+
var componentCatalogue2 = { ...baseComponents2 };
|
|
108
|
+
function extend2(objects) {
|
|
109
|
+
Object.assign(componentCatalogue2, objects);
|
|
114
110
|
}
|
|
115
|
-
function
|
|
116
|
-
return
|
|
111
|
+
function getComponentCatalogue2() {
|
|
112
|
+
return componentCatalogue2;
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
// src/components/app.tsx
|
|
120
116
|
import { createContext, useContext } from "react";
|
|
121
|
-
var
|
|
117
|
+
var AppContext2 = createContext({
|
|
122
118
|
keyHandler: null,
|
|
123
119
|
renderer: null
|
|
124
120
|
});
|
|
125
|
-
var
|
|
126
|
-
return useContext(
|
|
121
|
+
var useAppContext2 = () => {
|
|
122
|
+
return useContext(AppContext2);
|
|
127
123
|
};
|
|
128
124
|
|
|
129
125
|
// src/reconciler/renderer.ts
|
|
130
|
-
import { CliRenderEvents
|
|
126
|
+
import { CliRenderEvents } from "@opentui/core";
|
|
131
127
|
import React2 from "react";
|
|
132
128
|
|
|
133
129
|
// src/components/error-boundary.tsx
|
|
@@ -168,7 +164,7 @@ import { ImageRenderable as ImageRenderable2, TextNodeRenderable as TextNodeRend
|
|
|
168
164
|
// package.json
|
|
169
165
|
var package_default = {
|
|
170
166
|
name: "@opentui/react",
|
|
171
|
-
version: "0.0.0-
|
|
167
|
+
version: "0.0.0-20260929-c138f0f8",
|
|
172
168
|
description: "React renderer for building terminal user interfaces using OpenTUI core",
|
|
173
169
|
license: "MIT",
|
|
174
170
|
repository: {
|
|
@@ -215,7 +211,7 @@ var package_default = {
|
|
|
215
211
|
},
|
|
216
212
|
devDependencies: {
|
|
217
213
|
"@opentui/keymap": "workspace:*",
|
|
218
|
-
"@types/bun": "1.
|
|
214
|
+
"@types/bun": "1.4.1",
|
|
219
215
|
"@types/node": "^24.0.0",
|
|
220
216
|
"@types/react": "^19.2.0",
|
|
221
217
|
"@types/react-reconciler": "^0.33.0",
|
|
@@ -394,7 +390,7 @@ var hostConfig = {
|
|
|
394
390
|
throw new Error(`Component of type "${type}" must be created inside of a text node`);
|
|
395
391
|
}
|
|
396
392
|
const id = getNextId(type);
|
|
397
|
-
const components =
|
|
393
|
+
const components = getComponentCatalogue2();
|
|
398
394
|
if (!components[type]) {
|
|
399
395
|
throw new Error(`Unknown component type: ${type}`);
|
|
400
396
|
}
|
|
@@ -551,7 +547,7 @@ var hostConfig = {
|
|
|
551
547
|
var reconciler = ReactReconciler(hostConfig);
|
|
552
548
|
if (process.env["DEV"] === "true") {
|
|
553
549
|
try {
|
|
554
|
-
await import("./chunk-
|
|
550
|
+
await import("./chunk-z62dbhvj.js");
|
|
555
551
|
} catch (error) {
|
|
556
552
|
if (error.code === "ERR_MODULE_NOT_FOUND") {
|
|
557
553
|
console.warn(`
|
|
@@ -577,9 +573,9 @@ function _render(element, root) {
|
|
|
577
573
|
|
|
578
574
|
// src/reconciler/renderer.ts
|
|
579
575
|
var _r = reconciler;
|
|
580
|
-
var
|
|
581
|
-
var { createPortal } = reconciler;
|
|
582
|
-
function
|
|
576
|
+
var flushSync2 = _r.flushSyncFromReconciler ?? _r.flushSync;
|
|
577
|
+
var { createPortal: createPortal2 } = reconciler;
|
|
578
|
+
function createRoot2(renderer) {
|
|
583
579
|
let container = null;
|
|
584
580
|
const cleanup = () => {
|
|
585
581
|
if (container) {
|
|
@@ -591,11 +587,10 @@ function createRoot(renderer) {
|
|
|
591
587
|
renderer.once(CliRenderEvents.DESTROY, cleanup);
|
|
592
588
|
return {
|
|
593
589
|
render: (node) => {
|
|
594
|
-
|
|
595
|
-
container = _render(React2.createElement(AppContext.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
|
|
590
|
+
container = _render(React2.createElement(AppContext2.Provider, { value: { keyHandler: renderer.keyInput, renderer } }, React2.createElement(ErrorBoundary, null, node)), renderer.root);
|
|
596
591
|
},
|
|
597
592
|
unmount: cleanup
|
|
598
593
|
};
|
|
599
594
|
}
|
|
600
595
|
|
|
601
|
-
export {
|
|
596
|
+
export { baseComponents2, componentCatalogue2, extend2, getComponentCatalogue2, AppContext2, useAppContext2, Fragment, jsxDEV, flushSync2, createPortal2, createRoot2 };
|
package/index.js
CHANGED
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
|
|
3
|
+
baseComponents2,
|
|
4
|
+
componentCatalogue2,
|
|
5
|
+
extend2,
|
|
6
|
+
getComponentCatalogue2,
|
|
7
|
+
AppContext2,
|
|
8
|
+
useAppContext2,
|
|
4
9
|
Fragment,
|
|
5
|
-
baseComponents,
|
|
6
|
-
componentCatalogue,
|
|
7
|
-
createPortal,
|
|
8
|
-
createRoot,
|
|
9
|
-
extend,
|
|
10
|
-
flushSync,
|
|
11
|
-
getComponentCatalogue,
|
|
12
10
|
jsxDEV,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
flushSync2,
|
|
12
|
+
createPortal2,
|
|
13
|
+
createRoot2
|
|
14
|
+
} from "./chunk-tc772gtx.js";
|
|
16
15
|
// src/hooks/use-blur.ts
|
|
17
16
|
import { useEffect } from "react";
|
|
18
17
|
|
|
@@ -31,7 +30,7 @@ function useEffectEvent(handler) {
|
|
|
31
30
|
|
|
32
31
|
// src/hooks/use-renderer.ts
|
|
33
32
|
var useRenderer = () => {
|
|
34
|
-
const { renderer } =
|
|
33
|
+
const { renderer } = useAppContext2();
|
|
35
34
|
if (!renderer) {
|
|
36
35
|
throw new Error("Renderer not found.");
|
|
37
36
|
}
|
|
@@ -64,7 +63,7 @@ var useFocus = (handler) => {
|
|
|
64
63
|
// src/hooks/use-keyboard.ts
|
|
65
64
|
import { useEffect as useEffect3 } from "react";
|
|
66
65
|
var useKeyboard = (handler, options = { release: false }) => {
|
|
67
|
-
const { keyHandler } =
|
|
66
|
+
const { keyHandler } = useAppContext2();
|
|
68
67
|
const stableHandler = useEffectEvent(handler);
|
|
69
68
|
useEffect3(() => {
|
|
70
69
|
keyHandler?.on("keypress", stableHandler);
|
|
@@ -82,7 +81,7 @@ var useKeyboard = (handler, options = { release: false }) => {
|
|
|
82
81
|
// src/hooks/use-paste.ts
|
|
83
82
|
import { useEffect as useEffect4 } from "react";
|
|
84
83
|
var usePaste = (handler) => {
|
|
85
|
-
const { keyHandler } =
|
|
84
|
+
const { keyHandler } = useAppContext2();
|
|
86
85
|
const stableHandler = useEffectEvent(handler);
|
|
87
86
|
useEffect4(() => {
|
|
88
87
|
keyHandler?.on("paste", stableHandler);
|
|
@@ -131,21 +130,26 @@ var useTerminalDimensions = () => {
|
|
|
131
130
|
return dimensions;
|
|
132
131
|
};
|
|
133
132
|
// src/hooks/use-timeline.ts
|
|
134
|
-
import {
|
|
133
|
+
import { getTimelineEngine, Timeline } from "@opentui/core";
|
|
135
134
|
import { useEffect as useEffect7, useState as useState2 } from "react";
|
|
136
135
|
var useTimeline = (options = {}) => {
|
|
136
|
+
const renderer = useRenderer();
|
|
137
137
|
const [timeline] = useState2(() => new Timeline(options));
|
|
138
138
|
const [autoplay] = useState2(options.autoplay !== false);
|
|
139
139
|
useEffect7(() => {
|
|
140
|
+
const engine = getTimelineEngine(renderer);
|
|
140
141
|
if (autoplay) {
|
|
141
142
|
timeline.play();
|
|
142
143
|
}
|
|
143
144
|
engine.register(timeline);
|
|
144
145
|
return () => {
|
|
145
|
-
|
|
146
|
-
|
|
146
|
+
try {
|
|
147
|
+
timeline.pause();
|
|
148
|
+
} finally {
|
|
149
|
+
engine.unregister(timeline);
|
|
150
|
+
}
|
|
147
151
|
};
|
|
148
|
-
}, [autoplay, timeline]);
|
|
152
|
+
}, [autoplay, timeline, renderer]);
|
|
149
153
|
return timeline;
|
|
150
154
|
};
|
|
151
155
|
// src/plugins/slot.tsx
|
|
@@ -358,7 +362,7 @@ function Slot(props) {
|
|
|
358
362
|
// src/time-to-first-draw.tsx
|
|
359
363
|
import { TimeToFirstDrawRenderable } from "@opentui/core";
|
|
360
364
|
import { createElement } from "react";
|
|
361
|
-
|
|
365
|
+
extend2({ "time-to-first-draw": TimeToFirstDrawRenderable });
|
|
362
366
|
var TimeToFirstDraw = (props) => {
|
|
363
367
|
return createElement("time-to-first-draw", props);
|
|
364
368
|
};
|
|
@@ -366,27 +370,27 @@ var TimeToFirstDraw = (props) => {
|
|
|
366
370
|
// src/index.ts
|
|
367
371
|
import { createElement as createElement2 } from "react";
|
|
368
372
|
export {
|
|
369
|
-
|
|
370
|
-
useTerminalDimensions,
|
|
371
|
-
useSelectionHandler,
|
|
372
|
-
useRenderer,
|
|
373
|
-
usePaste,
|
|
374
|
-
useOnResize,
|
|
375
|
-
useKeyboard,
|
|
376
|
-
useFocus,
|
|
377
|
-
useBlur,
|
|
378
|
-
useAppContext,
|
|
379
|
-
getComponentCatalogue,
|
|
380
|
-
flushSync,
|
|
381
|
-
extend,
|
|
382
|
-
createSlot,
|
|
383
|
-
createRoot,
|
|
384
|
-
createReactSlotRegistry,
|
|
385
|
-
createPortal,
|
|
386
|
-
createElement2 as createElement,
|
|
387
|
-
componentCatalogue,
|
|
388
|
-
baseComponents,
|
|
389
|
-
TimeToFirstDraw,
|
|
373
|
+
AppContext2 as AppContext,
|
|
390
374
|
Slot,
|
|
391
|
-
|
|
375
|
+
TimeToFirstDraw,
|
|
376
|
+
baseComponents2 as baseComponents,
|
|
377
|
+
componentCatalogue2 as componentCatalogue,
|
|
378
|
+
createElement2 as createElement,
|
|
379
|
+
createPortal2 as createPortal,
|
|
380
|
+
createReactSlotRegistry,
|
|
381
|
+
createRoot2 as createRoot,
|
|
382
|
+
createSlot,
|
|
383
|
+
extend2 as extend,
|
|
384
|
+
flushSync2 as flushSync,
|
|
385
|
+
getComponentCatalogue2 as getComponentCatalogue,
|
|
386
|
+
useAppContext2 as useAppContext,
|
|
387
|
+
useBlur,
|
|
388
|
+
useFocus,
|
|
389
|
+
useKeyboard,
|
|
390
|
+
useOnResize,
|
|
391
|
+
usePaste,
|
|
392
|
+
useRenderer,
|
|
393
|
+
useSelectionHandler,
|
|
394
|
+
useTerminalDimensions,
|
|
395
|
+
useTimeline
|
|
392
396
|
};
|
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.0.0-
|
|
7
|
+
"version": "0.0.0-20260929-c138f0f8",
|
|
8
8
|
"description": "React renderer for building terminal user interfaces using OpenTUI core",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
@@ -52,12 +52,12 @@
|
|
|
52
52
|
}
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@opentui/core": "0.0.0-
|
|
55
|
+
"@opentui/core": "0.0.0-20260929-c138f0f8",
|
|
56
56
|
"react-reconciler": "^0.33.0"
|
|
57
57
|
},
|
|
58
58
|
"devDependencies": {
|
|
59
59
|
"@opentui/keymap": "workspace:*",
|
|
60
|
-
"@types/bun": "1.
|
|
60
|
+
"@types/bun": "1.4.1",
|
|
61
61
|
"@types/node": "^24.0.0",
|
|
62
62
|
"@types/react": "^19.2.0",
|
|
63
63
|
"@types/react-reconciler": "^0.33.0",
|
package/test-utils.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import"./chunk-2mx7fq49.js";
|
|
3
|
+
createRoot2
|
|
4
|
+
} from "./chunk-tc772gtx.js";
|
|
6
5
|
|
|
7
6
|
// src/test-utils.ts
|
|
8
7
|
import { createTestRenderer } from "@opentui/core/testing";
|
|
@@ -26,7 +25,7 @@ async function testRender(node, testRendererOptions) {
|
|
|
26
25
|
setIsReactActEnvironment(false);
|
|
27
26
|
}
|
|
28
27
|
});
|
|
29
|
-
root =
|
|
28
|
+
root = createRoot2(testSetup.renderer);
|
|
30
29
|
act(() => {
|
|
31
30
|
if (root) {
|
|
32
31
|
root.render(node);
|
package/chunk-2mx7fq49.js
DELETED