@opentui/react 0.1.13 → 0.1.15

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
@@ -18,7 +18,7 @@ function App() {
18
18
  <box>
19
19
  <text fg="#00FF00">Hello, Terminal!</text>
20
20
  <box title="Welcome" padding={2}>
21
- <text>Welcome to OpenTUI with React!"</text>
21
+ <text>Welcome to OpenTUI with React!</text>
22
22
  </box>
23
23
  </box>
24
24
  )
@@ -484,7 +484,7 @@ class ButtonRenderable extends BoxRenderable {
484
484
 
485
485
  set label(value: string) {
486
486
  this._label = value
487
- this.needsUpdate()
487
+ this.requestRender()
488
488
  }
489
489
  }
490
490
 
package/index.js CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  ASCIIFontRenderable,
5
5
  BoxRenderable,
6
6
  InputRenderable,
7
+ ScrollBoxRenderable,
7
8
  SelectRenderable,
8
9
  TabSelectRenderable,
9
10
  TextRenderable
@@ -13,6 +14,7 @@ var baseComponents = {
13
14
  text: TextRenderable,
14
15
  input: InputRenderable,
15
16
  select: SelectRenderable,
17
+ scrollbox: ScrollBoxRenderable,
16
18
  "ascii-font": ASCIIFontRenderable,
17
19
  "tab-select": TabSelectRenderable
18
20
  };
@@ -34,14 +36,30 @@ var useAppContext = () => {
34
36
  };
35
37
  // src/hooks/use-keyboard.tsx
36
38
  import { useEffect } from "react";
39
+
40
+ // src/hooks/use-event.tsx
41
+ import { useCallback, useLayoutEffect, useRef } from "react";
42
+ function useEvent(handler) {
43
+ const handlerRef = useRef(handler);
44
+ useLayoutEffect(() => {
45
+ handlerRef.current = handler;
46
+ });
47
+ return useCallback((...args) => {
48
+ const fn = handlerRef.current;
49
+ return fn(...args);
50
+ }, []);
51
+ }
52
+
53
+ // src/hooks/use-keyboard.tsx
37
54
  var useKeyboard = (handler) => {
38
55
  const { keyHandler } = useAppContext();
56
+ const stableHandler = useEvent(handler);
39
57
  useEffect(() => {
40
- keyHandler?.on("keypress", handler);
58
+ keyHandler?.on("keypress", stableHandler);
41
59
  return () => {
42
- keyHandler?.off("keypress", handler);
60
+ keyHandler?.off("keypress", stableHandler);
43
61
  };
44
- }, [keyHandler, handler]);
62
+ }, [keyHandler, stableHandler]);
45
63
  };
46
64
  // src/hooks/use-renderer.tsx
47
65
  var useRenderer = () => {
@@ -68,8 +86,8 @@ import { useState } from "react";
68
86
  var useTerminalDimensions = () => {
69
87
  const renderer = useRenderer();
70
88
  const [dimensions, setDimensions] = useState({
71
- width: renderer.terminalWidth,
72
- height: renderer.terminalHeight
89
+ width: renderer.width,
90
+ height: renderer.height
73
91
  });
74
92
  const cb = (width, height) => {
75
93
  setDimensions({ width, height });
@@ -266,7 +284,10 @@ var hostConfig = {
266
284
  if (!components[type]) {
267
285
  throw new Error(`[Reconciler] Unknown component type: ${type}`);
268
286
  }
269
- return new components[type](rootContainerInstance.ctx, {});
287
+ return new components[type](rootContainerInstance.ctx, {
288
+ id,
289
+ ...props
290
+ });
270
291
  },
271
292
  appendChild(parent, child) {
272
293
  parent.add(child);
@@ -287,7 +308,7 @@ var hostConfig = {
287
308
  return null;
288
309
  },
289
310
  resetAfterCommit(containerInfo) {
290
- containerInfo.needsUpdate();
311
+ containerInfo.requestRender();
291
312
  },
292
313
  getRootHostContext(rootContainerInstance) {
293
314
  return {};
@@ -321,11 +342,11 @@ var hostConfig = {
321
342
  commitMount(instance, type, props, internalInstanceHandle) {},
322
343
  commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
323
344
  updateProperties(instance, type, oldProps, newProps);
324
- instance.needsUpdate();
345
+ instance.requestRender();
325
346
  },
326
347
  commitTextUpdate(textInstance, oldText, newText) {
327
348
  textInstance.content = newText;
328
- textInstance.needsUpdate();
349
+ textInstance.requestRender();
329
350
  },
330
351
  appendChildToContainer(container, child) {
331
352
  container.add(child);
@@ -335,19 +356,19 @@ var hostConfig = {
335
356
  },
336
357
  hideInstance(instance) {
337
358
  instance.visible = false;
338
- instance.needsUpdate();
359
+ instance.requestRender();
339
360
  },
340
361
  unhideInstance(instance, props) {
341
362
  instance.visible = true;
342
- instance.needsUpdate();
363
+ instance.requestRender();
343
364
  },
344
365
  hideTextInstance(textInstance) {
345
366
  textInstance.visible = false;
346
- textInstance.needsUpdate();
367
+ textInstance.requestRender();
347
368
  },
348
369
  unhideTextInstance(textInstance, text) {
349
370
  textInstance.visible = true;
350
- textInstance.needsUpdate();
371
+ textInstance.requestRender();
351
372
  },
352
373
  clearContainer(container) {
353
374
  const children = container.getChildren();
@@ -2,12 +2,14 @@ import type * as React from "react"
2
2
  import type {
3
3
  AsciiFontProps,
4
4
  BoxProps,
5
+ ExtendedIntrinsicElements,
5
6
  InputProps,
7
+ OpenTUIComponents,
8
+ ScrollBoxProps,
6
9
  SelectProps,
7
10
  TabSelectProps,
8
11
  TextProps,
9
12
  } from "./src/types/components"
10
- import type { ExtendedIntrinsicElements, OpenTUIComponents } from "./src/types/components"
11
13
 
12
14
  export namespace JSX {
13
15
  type Element = React.ReactNode
@@ -29,6 +31,7 @@ export namespace JSX {
29
31
  text: TextProps
30
32
  input: InputProps
31
33
  select: SelectProps
34
+ scrollbox: ScrollBoxProps
32
35
  "ascii-font": AsciiFontProps
33
36
  "tab-select": TabSelectProps
34
37
  }
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.13",
7
+ "version": "0.1.15",
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.13",
38
+ "@opentui/core": "0.1.15",
39
39
  "react-reconciler": "^0.32.0"
40
40
  },
41
41
  "devDependencies": {
@@ -1,10 +1,11 @@
1
- import { ASCIIFontRenderable, BoxRenderable, InputRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
1
+ import { ASCIIFontRenderable, BoxRenderable, InputRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
2
2
  import type { RenderableConstructor } from "../types/components";
3
3
  export declare const baseComponents: {
4
4
  box: typeof BoxRenderable;
5
5
  text: typeof TextRenderable;
6
6
  input: typeof InputRenderable;
7
7
  select: typeof SelectRenderable;
8
+ scrollbox: typeof ScrollBoxRenderable;
8
9
  "ascii-font": typeof ASCIIFontRenderable;
9
10
  "tab-select": typeof TabSelectRenderable;
10
11
  };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Returns a stable callback that always calls the latest version of the provided handler.
3
+ * This prevents unnecessary re-renders and effect re-runs while ensuring the callback
4
+ * always has access to the latest props and state.
5
+ *
6
+ * Useful for event handlers that need to be passed to effects with empty dependency arrays
7
+ * or memoized child components.
8
+ */
9
+ export declare function useEvent<T extends (...args: any[]) => any>(handler: T): T;
@@ -23,6 +23,7 @@ import {
23
23
  ASCIIFontRenderable,
24
24
  BoxRenderable,
25
25
  InputRenderable,
26
+ ScrollBoxRenderable,
26
27
  SelectRenderable,
27
28
  TabSelectRenderable,
28
29
  TextRenderable
@@ -32,6 +33,7 @@ var baseComponents = {
32
33
  text: TextRenderable,
33
34
  input: InputRenderable,
34
35
  select: SelectRenderable,
36
+ scrollbox: ScrollBoxRenderable,
35
37
  "ascii-font": ASCIIFontRenderable,
36
38
  "tab-select": TabSelectRenderable
37
39
  };
@@ -217,7 +219,10 @@ var hostConfig = {
217
219
  if (!components[type]) {
218
220
  throw new Error(`[Reconciler] Unknown component type: ${type}`);
219
221
  }
220
- return new components[type](rootContainerInstance.ctx, {});
222
+ return new components[type](rootContainerInstance.ctx, {
223
+ id,
224
+ ...props
225
+ });
221
226
  },
222
227
  appendChild(parent, child) {
223
228
  parent.add(child);
@@ -238,7 +243,7 @@ var hostConfig = {
238
243
  return null;
239
244
  },
240
245
  resetAfterCommit(containerInfo) {
241
- containerInfo.needsUpdate();
246
+ containerInfo.requestRender();
242
247
  },
243
248
  getRootHostContext(rootContainerInstance) {
244
249
  return {};
@@ -272,11 +277,11 @@ var hostConfig = {
272
277
  commitMount(instance, type, props, internalInstanceHandle) {},
273
278
  commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
274
279
  updateProperties(instance, type, oldProps, newProps);
275
- instance.needsUpdate();
280
+ instance.requestRender();
276
281
  },
277
282
  commitTextUpdate(textInstance, oldText, newText) {
278
283
  textInstance.content = newText;
279
- textInstance.needsUpdate();
284
+ textInstance.requestRender();
280
285
  },
281
286
  appendChildToContainer(container, child) {
282
287
  container.add(child);
@@ -286,19 +291,19 @@ var hostConfig = {
286
291
  },
287
292
  hideInstance(instance) {
288
293
  instance.visible = false;
289
- instance.needsUpdate();
294
+ instance.requestRender();
290
295
  },
291
296
  unhideInstance(instance, props) {
292
297
  instance.visible = true;
293
- instance.needsUpdate();
298
+ instance.requestRender();
294
299
  },
295
300
  hideTextInstance(textInstance) {
296
301
  textInstance.visible = false;
297
- textInstance.needsUpdate();
302
+ textInstance.requestRender();
298
303
  },
299
304
  unhideTextInstance(textInstance, text) {
300
305
  textInstance.visible = true;
301
- textInstance.needsUpdate();
306
+ textInstance.requestRender();
302
307
  },
303
308
  clearContainer(container) {
304
309
  const children = container.getChildren();
@@ -1,4 +1,4 @@
1
- import type { ASCIIFontOptions, ASCIIFontRenderable, BoxOptions, BoxRenderable, InputRenderable, InputRenderableOptions, Renderable, RenderableOptions, RenderContext, SelectOption, SelectRenderable, SelectRenderableOptions, StyledText, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextChunk, TextOptions, TextRenderable } from "@opentui/core";
1
+ import type { ASCIIFontOptions, ASCIIFontRenderable, BoxOptions, BoxRenderable, InputRenderable, InputRenderableOptions, Renderable, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, StyledText, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextChunk, 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}`;
@@ -40,6 +40,9 @@ export type SelectProps = ComponentProps<SelectRenderableOptions, SelectRenderab
40
40
  onChange?: (index: number, option: SelectOption | null) => void;
41
41
  onSelect?: (index: number, option: SelectOption | null) => void;
42
42
  };
43
+ export type ScrollBoxProps = ComponentProps<ContainerProps<ScrollBoxOptions>, ScrollBoxRenderable> & {
44
+ focused?: boolean;
45
+ };
43
46
  export type AsciiFontProps = ComponentProps<ASCIIFontOptions, ASCIIFontRenderable>;
44
47
  export type TabSelectProps = ComponentProps<TabSelectRenderableOptions, TabSelectRenderable> & {
45
48
  focused?: boolean;