@opentui/react 0.1.12 → 0.1.14

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
@@ -15,12 +15,12 @@ import { render } from "@opentui/react"
15
15
 
16
16
  function App() {
17
17
  return (
18
- <group>
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
- </group>
23
+ </box>
24
24
  )
25
25
  }
26
26
 
@@ -35,7 +35,6 @@ OpenTUI React provides several built-in components that map to OpenTUI core rend
35
35
 
36
36
  - **`<text>`** - Display text with styling
37
37
  - **`<box>`** - Container with borders and layout
38
- - **`<group>`** - Layout container for organizing components
39
38
  - **`<input>`** - Text input field
40
39
  - **`<select>`** - Selection dropdown
41
40
  - **`<tab-select>`** - Tab-based selection
@@ -147,14 +146,14 @@ function MyComponent() {
147
146
  const { width, height } = useTerminalDimensions()
148
147
 
149
148
  return (
150
- <group>
149
+ <box>
151
150
  <text>
152
151
  Terminal dimensions: {width}x{height}
153
152
  </text>
154
153
  <box style={{ width: Math.floor(width / 2), height: Math.floor(height / 3) }}>
155
154
  <text>Half-width, third-height box</text>
156
155
  </box>
157
- </group>
156
+ </box>
158
157
  )
159
158
  }
160
159
  ```
@@ -172,7 +171,7 @@ import { bold, fg, t } from "@opentui/core"
172
171
 
173
172
  function TextExample() {
174
173
  return (
175
- <group>
174
+ <box>
176
175
  {/* Simple text */}
177
176
  <text>Hello World</text>
178
177
 
@@ -181,7 +180,7 @@ function TextExample() {
181
180
 
182
181
  {/* Template literals */}
183
182
  <text>{t`${bold("Bold")} and ${fg("blue")("Blue")}`}</text>
184
- </group>
183
+ </box>
185
184
  )
186
185
  }
187
186
  ```
@@ -193,7 +192,7 @@ Container with borders and layout capabilities.
193
192
  ```tsx
194
193
  function BoxExample() {
195
194
  return (
196
- <group flexDirection="column">
195
+ <box flexDirection="column">
197
196
  {/* Basic box */}
198
197
  <box>
199
198
  <text>Simple box</text>
@@ -216,26 +215,7 @@ function BoxExample() {
216
215
  >
217
216
  <text>Centered content</text>
218
217
  </box>
219
- </group>
220
- )
221
- }
222
- ```
223
-
224
- ### Group Component
225
-
226
- Layout container for organizing multiple components.
227
-
228
- ```tsx
229
- function GroupExample() {
230
- return (
231
- <group flexDirection="row">
232
- <box>
233
- <text>Left</text>
234
- </box>
235
- <box>
236
- <text>Right</text>
237
- </box>
238
- </group>
218
+ </box>
239
219
  )
240
220
  }
241
221
  ```
@@ -318,7 +298,7 @@ function ASCIIFontExample() {
318
298
  })
319
299
 
320
300
  return (
321
- <group style={{ paddingLeft: 1, paddingRight: 1 }}>
301
+ <box style={{ paddingLeft: 1, paddingRight: 1 }}>
322
302
  <box
323
303
  style={{
324
304
  height: 8,
@@ -356,7 +336,7 @@ function ASCIIFontExample() {
356
336
  </box>
357
337
 
358
338
  <ascii-font style={{ width, height }} text={text} font={font} />
359
- </group>
339
+ </box>
360
340
  )
361
341
  }
362
342
  ```
@@ -390,7 +370,7 @@ function LoginForm() {
390
370
  }, [username, password])
391
371
 
392
372
  return (
393
- <group style={{ padding: 2, flexDirection: "column" }}>
373
+ <box style={{ padding: 2, flexDirection: "column" }}>
394
374
  <text fg="#FFFF00">Login Form</text>
395
375
 
396
376
  <box title="Username" style={{ width: 40, height: 3, marginTop: 1 }}>
@@ -418,7 +398,7 @@ function LoginForm() {
418
398
  >
419
399
  {status.toUpperCase()}
420
400
  </text>
421
- </group>
401
+ </box>
422
402
  )
423
403
  }
424
404
 
@@ -460,7 +440,7 @@ import { render } from "@opentui/react"
460
440
 
461
441
  function StyledTextShowcase() {
462
442
  return (
463
- <group style={{ flexDirection: "column" }}>
443
+ <box style={{ flexDirection: "column" }}>
464
444
  <text>Simple text</text>
465
445
  <text>{bold("Bold text")}</text>
466
446
  <text>{underline("Underlined text")}</text>
@@ -468,7 +448,7 @@ function StyledTextShowcase() {
468
448
  <text>{blue("Blue text")}</text>
469
449
  <text>{bold(red("Bold red text"))}</text>
470
450
  <text>{t`${bold("Bold")} and ${blue("blue")} combined`}</text>
471
- </group>
451
+ </box>
472
452
  )
473
453
  }
474
454
 
@@ -504,7 +484,7 @@ class ButtonRenderable extends BoxRenderable {
504
484
 
505
485
  set label(value: string) {
506
486
  this._label = value
507
- this.needsUpdate()
487
+ this.requestRender()
508
488
  }
509
489
  }
510
490
 
@@ -521,10 +501,10 @@ extend({ button: ButtonRenderable })
521
501
  // Use in JSX
522
502
  function App() {
523
503
  return (
524
- <group>
504
+ <box>
525
505
  <button label="Click me!" style={{ backgroundColor: "blue" }} />
526
506
  <button label="Another button" style={{ backgroundColor: "green" }} />
527
- </group>
507
+ </box>
528
508
  )
529
509
  }
530
510
 
package/index.js CHANGED
@@ -3,7 +3,6 @@
3
3
  import {
4
4
  ASCIIFontRenderable,
5
5
  BoxRenderable,
6
- GroupRenderable,
7
6
  InputRenderable,
8
7
  SelectRenderable,
9
8
  TabSelectRenderable,
@@ -12,7 +11,6 @@ import {
12
11
  var baseComponents = {
13
12
  box: BoxRenderable,
14
13
  text: TextRenderable,
15
- group: GroupRenderable,
16
14
  input: InputRenderable,
17
15
  select: SelectRenderable,
18
16
  "ascii-font": ASCIIFontRenderable,
@@ -36,14 +34,30 @@ var useAppContext = () => {
36
34
  };
37
35
  // src/hooks/use-keyboard.tsx
38
36
  import { useEffect } from "react";
37
+
38
+ // src/hooks/use-event.tsx
39
+ import { useCallback, useLayoutEffect, useRef } from "react";
40
+ function useEvent(handler) {
41
+ const handlerRef = useRef(handler);
42
+ useLayoutEffect(() => {
43
+ handlerRef.current = handler;
44
+ });
45
+ return useCallback((...args) => {
46
+ const fn = handlerRef.current;
47
+ return fn(...args);
48
+ }, []);
49
+ }
50
+
51
+ // src/hooks/use-keyboard.tsx
39
52
  var useKeyboard = (handler) => {
40
53
  const { keyHandler } = useAppContext();
54
+ const stableHandler = useEvent(handler);
41
55
  useEffect(() => {
42
- keyHandler?.on("keypress", handler);
56
+ keyHandler?.on("keypress", stableHandler);
43
57
  return () => {
44
- keyHandler?.off("keypress", handler);
58
+ keyHandler?.off("keypress", stableHandler);
45
59
  };
46
- }, [keyHandler, handler]);
60
+ }, [keyHandler, stableHandler]);
47
61
  };
48
62
  // src/hooks/use-renderer.tsx
49
63
  var useRenderer = () => {
@@ -70,8 +84,8 @@ import { useState } from "react";
70
84
  var useTerminalDimensions = () => {
71
85
  const renderer = useRenderer();
72
86
  const [dimensions, setDimensions] = useState({
73
- width: renderer.terminalWidth,
74
- height: renderer.terminalHeight
87
+ width: renderer.width,
88
+ height: renderer.height
75
89
  });
76
90
  const cb = (width, height) => {
77
91
  setDimensions({ width, height });
@@ -289,7 +303,7 @@ var hostConfig = {
289
303
  return null;
290
304
  },
291
305
  resetAfterCommit(containerInfo) {
292
- containerInfo.needsUpdate();
306
+ containerInfo.requestRender();
293
307
  },
294
308
  getRootHostContext(rootContainerInstance) {
295
309
  return {};
@@ -323,11 +337,11 @@ var hostConfig = {
323
337
  commitMount(instance, type, props, internalInstanceHandle) {},
324
338
  commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
325
339
  updateProperties(instance, type, oldProps, newProps);
326
- instance.needsUpdate();
340
+ instance.requestRender();
327
341
  },
328
342
  commitTextUpdate(textInstance, oldText, newText) {
329
343
  textInstance.content = newText;
330
- textInstance.needsUpdate();
344
+ textInstance.requestRender();
331
345
  },
332
346
  appendChildToContainer(container, child) {
333
347
  container.add(child);
@@ -337,19 +351,19 @@ var hostConfig = {
337
351
  },
338
352
  hideInstance(instance) {
339
353
  instance.visible = false;
340
- instance.needsUpdate();
354
+ instance.requestRender();
341
355
  },
342
356
  unhideInstance(instance, props) {
343
357
  instance.visible = true;
344
- instance.needsUpdate();
358
+ instance.requestRender();
345
359
  },
346
360
  hideTextInstance(textInstance) {
347
361
  textInstance.visible = false;
348
- textInstance.needsUpdate();
362
+ textInstance.requestRender();
349
363
  },
350
364
  unhideTextInstance(textInstance, text) {
351
365
  textInstance.visible = true;
352
- textInstance.needsUpdate();
366
+ textInstance.requestRender();
353
367
  },
354
368
  clearContainer(container) {
355
369
  const children = container.getChildren();
@@ -2,7 +2,6 @@ import type * as React from "react"
2
2
  import type {
3
3
  AsciiFontProps,
4
4
  BoxProps,
5
- GroupProps,
6
5
  InputProps,
7
6
  SelectProps,
8
7
  TabSelectProps,
@@ -28,7 +27,6 @@ export namespace JSX {
28
27
  interface IntrinsicElements extends React.JSX.IntrinsicElements, ExtendedIntrinsicElements<OpenTUIComponents> {
29
28
  box: BoxProps
30
29
  text: TextProps
31
- group: GroupProps
32
30
  input: InputProps
33
31
  select: SelectProps
34
32
  "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.12",
7
+ "version": "0.1.14",
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.12",
38
+ "@opentui/core": "0.1.14",
39
39
  "react-reconciler": "^0.32.0"
40
40
  },
41
41
  "devDependencies": {
@@ -1,9 +1,8 @@
1
- import { ASCIIFontRenderable, BoxRenderable, GroupRenderable, InputRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
1
+ import { ASCIIFontRenderable, BoxRenderable, InputRenderable, 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
- group: typeof GroupRenderable;
7
6
  input: typeof InputRenderable;
8
7
  select: typeof SelectRenderable;
9
8
  "ascii-font": typeof ASCIIFontRenderable;
@@ -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;
@@ -22,7 +22,6 @@ import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constant
22
22
  import {
23
23
  ASCIIFontRenderable,
24
24
  BoxRenderable,
25
- GroupRenderable,
26
25
  InputRenderable,
27
26
  SelectRenderable,
28
27
  TabSelectRenderable,
@@ -31,7 +30,6 @@ import {
31
30
  var baseComponents = {
32
31
  box: BoxRenderable,
33
32
  text: TextRenderable,
34
- group: GroupRenderable,
35
33
  input: InputRenderable,
36
34
  select: SelectRenderable,
37
35
  "ascii-font": ASCIIFontRenderable,
@@ -240,7 +238,7 @@ var hostConfig = {
240
238
  return null;
241
239
  },
242
240
  resetAfterCommit(containerInfo) {
243
- containerInfo.needsUpdate();
241
+ containerInfo.requestRender();
244
242
  },
245
243
  getRootHostContext(rootContainerInstance) {
246
244
  return {};
@@ -274,11 +272,11 @@ var hostConfig = {
274
272
  commitMount(instance, type, props, internalInstanceHandle) {},
275
273
  commitUpdate(instance, type, oldProps, newProps, internalInstanceHandle) {
276
274
  updateProperties(instance, type, oldProps, newProps);
277
- instance.needsUpdate();
275
+ instance.requestRender();
278
276
  },
279
277
  commitTextUpdate(textInstance, oldText, newText) {
280
278
  textInstance.content = newText;
281
- textInstance.needsUpdate();
279
+ textInstance.requestRender();
282
280
  },
283
281
  appendChildToContainer(container, child) {
284
282
  container.add(child);
@@ -288,19 +286,19 @@ var hostConfig = {
288
286
  },
289
287
  hideInstance(instance) {
290
288
  instance.visible = false;
291
- instance.needsUpdate();
289
+ instance.requestRender();
292
290
  },
293
291
  unhideInstance(instance, props) {
294
292
  instance.visible = true;
295
- instance.needsUpdate();
293
+ instance.requestRender();
296
294
  },
297
295
  hideTextInstance(textInstance) {
298
296
  textInstance.visible = false;
299
- textInstance.needsUpdate();
297
+ textInstance.requestRender();
300
298
  },
301
299
  unhideTextInstance(textInstance, text) {
302
300
  textInstance.visible = true;
303
- textInstance.needsUpdate();
301
+ textInstance.requestRender();
304
302
  },
305
303
  clearContainer(container) {
306
304
  const children = container.getChildren();
@@ -1,4 +1,4 @@
1
- import type { ASCIIFontOptions, ASCIIFontRenderable, BoxOptions, BoxRenderable, GroupRenderable, 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, 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}`;
@@ -29,7 +29,6 @@ export type TextProps = ComponentProps<TextOptions, TextRenderable> & {
29
29
  children?: TextChildren | StyledText | TextChunk | Array<TextChildren | StyledText | TextChunk>;
30
30
  };
31
31
  export type BoxProps = ComponentProps<ContainerProps<BoxOptions>, BoxRenderable>;
32
- export type GroupProps = ComponentProps<ContainerProps<RenderableOptions>, GroupRenderable>;
33
32
  export type InputProps = ComponentProps<InputRenderableOptions, InputRenderable> & {
34
33
  focused?: boolean;
35
34
  onInput?: (value: string) => void;