@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 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
- const syntaxStyle = SyntaxStyle.fromStyles({
691
- keyword: { fg: RGBA.fromHex("#ff6b6b"), bold: true }, // red, bold
692
- string: { fg: RGBA.fromHex("#51cf66") }, // green
693
- comment: { fg: RGBA.fromHex("#868e96"), italic: true }, // gray, italic
694
- number: { fg: RGBA.fromHex("#ffd43b") }, // yellow
695
- default: { fg: RGBA.fromHex("#ffffff") }, // white
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
- function App() {
726
- const lineNumberRef = useRef<LineNumberRenderable>(null)
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 baseComponents = {
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 componentCatalogue = { ...baseComponents };
112
- function extend(objects) {
113
- Object.assign(componentCatalogue, objects);
107
+ var componentCatalogue2 = { ...baseComponents2 };
108
+ function extend2(objects) {
109
+ Object.assign(componentCatalogue2, objects);
114
110
  }
115
- function getComponentCatalogue() {
116
- return componentCatalogue;
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 AppContext = createContext({
117
+ var AppContext2 = createContext({
122
118
  keyHandler: null,
123
119
  renderer: null
124
120
  });
125
- var useAppContext = () => {
126
- return useContext(AppContext);
121
+ var useAppContext2 = () => {
122
+ return useContext(AppContext2);
127
123
  };
128
124
 
129
125
  // src/reconciler/renderer.ts
130
- import { CliRenderEvents, engine } from "@opentui/core";
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-20260829-c2e7cb1b",
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.3.14",
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 = getComponentCatalogue();
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-bdqvmfwv.js");
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 flushSync = _r.flushSyncFromReconciler ?? _r.flushSync;
581
- var { createPortal } = reconciler;
582
- function createRoot(renderer) {
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
- engine.attach(renderer);
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 { baseComponents, componentCatalogue, extend, getComponentCatalogue, AppContext, useAppContext, Fragment, jsxDEV, flushSync, createPortal, createRoot };
596
+ export { baseComponents2, componentCatalogue2, extend2, getComponentCatalogue2, AppContext2, useAppContext2, Fragment, jsxDEV, flushSync2, createPortal2, createRoot2 };
@@ -1,8 +1,4 @@
1
1
  // @bun
2
- import {
3
- __require
4
- } from "./chunk-2mx7fq49.js";
5
-
6
2
  // src/reconciler/devtools-polyfill.ts
7
3
  var g = globalThis;
8
4
  if (typeof g.WebSocket === "undefined") {
package/index.js CHANGED
@@ -1,18 +1,17 @@
1
1
  // @bun
2
2
  import {
3
- AppContext,
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
- useAppContext
14
- } from "./chunk-c3qd75wx.js";
15
- import"./chunk-2mx7fq49.js";
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 } = useAppContext();
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 } = useAppContext();
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 } = useAppContext();
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 { engine, Timeline } from "@opentui/core";
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
- timeline.pause();
146
- engine.unregister(timeline);
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
- extend({ "time-to-first-draw": TimeToFirstDrawRenderable });
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
- useTimeline,
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
- AppContext
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-20260829-c2e7cb1b",
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-20260829-c2e7cb1b",
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.3.14",
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
- createRoot
4
- } from "./chunk-c3qd75wx.js";
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 = createRoot(testSetup.renderer);
28
+ root = createRoot2(testSetup.renderer);
30
29
  act(() => {
31
30
  if (root) {
32
31
  root.render(node);
package/chunk-2mx7fq49.js DELETED
@@ -1,4 +0,0 @@
1
- // @bun
2
- var __require = import.meta.require;
3
-
4
- export { __require };