@opentui/react 0.1.7 → 0.1.9

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
@@ -39,6 +39,7 @@ OpenTUI React provides several built-in components that map to OpenTUI core rend
39
39
  - **`<input>`** - Text input field
40
40
  - **`<select>`** - Selection dropdown
41
41
  - **`<tab-select>`** - Tab-based selection
42
+ - **`<ascii-font>`** - Display ASCII art text with different font styles
42
43
 
43
44
  ### Styling
44
45
 
@@ -135,6 +136,29 @@ function MyComponent() {
135
136
  }
136
137
  ```
137
138
 
139
+ #### `useTerminalDimensions()`
140
+
141
+ Get current terminal dimensions and automatically update when the terminal is resized.
142
+
143
+ ```tsx
144
+ import { useTerminalDimensions } from "@opentui/react"
145
+
146
+ function MyComponent() {
147
+ const { width, height } = useTerminalDimensions()
148
+
149
+ return (
150
+ <group>
151
+ <text>Terminal dimensions: {width}x{height}</text>
152
+ <box style={{ width: Math.floor(width / 2), height: Math.floor(height / 3) }}>
153
+ <text>Half-width, third-height box</text>
154
+ </box>
155
+ </group>
156
+ )
157
+ }
158
+ ```
159
+
160
+ **Returns:** An object with `width` and `height` properties representing the current terminal dimensions.
161
+
138
162
  ## Components
139
163
 
140
164
  ### Text Component
@@ -274,6 +298,67 @@ function SelectExample() {
274
298
  }
275
299
  ```
276
300
 
301
+ ### ASCII Font Component
302
+
303
+ Display ASCII art text with different font styles.
304
+
305
+ ```tsx
306
+ import { measureText } from "@opentui/core"
307
+ import { useState } from "react"
308
+
309
+ function ASCIIFontExample() {
310
+ const text = "ASCII"
311
+ const [font, setFont] = useState<"block" | "shade" | "slick" | "tiny">("tiny")
312
+
313
+ const { width, height } = measureText({
314
+ text,
315
+ font,
316
+ })
317
+
318
+ return (
319
+ <group style={{ paddingLeft: 1, paddingRight: 1 }}>
320
+ <box
321
+ style={{
322
+ height: 8,
323
+ marginBottom: 1,
324
+ }}
325
+ >
326
+ <select
327
+ focused
328
+ onChange={(_, option) => setFont(option?.value)}
329
+ showScrollIndicator
330
+ options={[
331
+ {
332
+ name: "Tiny",
333
+ description: "Tiny font",
334
+ value: "tiny",
335
+ },
336
+ {
337
+ name: "Block",
338
+ description: "Block font",
339
+ value: "block",
340
+ },
341
+ {
342
+ name: "Slick",
343
+ description: "Slick font",
344
+ value: "slick",
345
+ },
346
+ {
347
+ name: "Shade",
348
+ description: "Shade font",
349
+ value: "shade",
350
+ },
351
+ ]}
352
+ style={{ flexGrow: 1 }}
353
+ />
354
+ </box>
355
+
356
+ <ascii-font style={{ width, height }} text={text} font={font} />
357
+ </group>
358
+ )
359
+ }
360
+ ```
361
+
277
362
  ## Examples
278
363
 
279
364
  ### Login Form
package/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  // @bun
2
2
  // src/components/index.ts
3
3
  import {
4
+ ASCIIFontRenderable,
4
5
  BoxRenderable,
5
6
  GroupRenderable,
6
7
  InputRenderable,
@@ -14,6 +15,7 @@ var baseComponents = {
14
15
  group: GroupRenderable,
15
16
  input: InputRenderable,
16
17
  select: SelectRenderable,
18
+ "ascii-font": ASCIIFontRenderable,
17
19
  "tab-select": TabSelectRenderable
18
20
  };
19
21
  var componentCatalogue = { ...baseComponents };
@@ -63,6 +65,20 @@ var useOnResize = (callback) => {
63
65
  }, [renderer, callback]);
64
66
  return renderer;
65
67
  };
68
+ // src/hooks/use-terminal-dimensions.tsx
69
+ import { useState } from "react";
70
+ var useTerminalDimensions = () => {
71
+ const renderer = useRenderer();
72
+ const [dimensions, setDimensions] = useState({
73
+ width: renderer.terminalWidth,
74
+ height: renderer.terminalHeight
75
+ });
76
+ const cb = (width, height) => {
77
+ setDimensions({ width, height });
78
+ };
79
+ useOnResize(cb);
80
+ return dimensions;
81
+ };
66
82
  // src/reconciler/renderer.ts
67
83
  import { createCliRenderer, getKeyHandler } from "@opentui/core";
68
84
  import React from "react";
@@ -405,6 +421,7 @@ async function render(node, rendererConfig = {}) {
405
421
  _render(React.createElement(AppContext.Provider, { value: { keyHandler, renderer } }, node), renderer.root);
406
422
  }
407
423
  export {
424
+ useTerminalDimensions,
408
425
  useRenderer,
409
426
  useOnResize,
410
427
  useKeyboard,
@@ -1,25 +1,37 @@
1
- import type { BoxProps, GroupProps, InputProps, SelectProps, TabSelectProps, TextProps } from "./src/types/components"
2
- import type { ExtendedIntrinsicElements, OpenTUIComponents } from "./src/types/extend"
1
+ import type * as React from "react"
2
+ import type {
3
+ AsciiFontProps,
4
+ BoxProps,
5
+ GroupProps,
6
+ InputProps,
7
+ SelectProps,
8
+ TabSelectProps,
9
+ TextProps,
10
+ } from "./src/types/components"
11
+ import type { ExtendedIntrinsicElements, OpenTUIComponents } from "./src/types/components"
3
12
 
4
13
  export namespace JSX {
5
- interface Element extends React.ReactElement<any, any> {}
14
+ type Element = React.ReactNode
6
15
 
7
- interface ElementClass {
8
- render: any
16
+ interface ElementClass extends React.ComponentClass<any> {
17
+ render(): React.ReactNode
9
18
  }
19
+
10
20
  interface ElementAttributesProperty {
11
21
  props: {}
12
22
  }
23
+
13
24
  interface ElementChildrenAttribute {
14
25
  children: {}
15
26
  }
16
27
 
17
- interface IntrinsicElements extends ExtendedIntrinsicElements<OpenTUIComponents> {
28
+ interface IntrinsicElements extends React.JSX.IntrinsicElements, ExtendedIntrinsicElements<OpenTUIComponents> {
18
29
  box: BoxProps
30
+ text: TextProps
19
31
  group: GroupProps
20
32
  input: InputProps
21
33
  select: SelectProps
34
+ "ascii-font": AsciiFontProps
22
35
  "tab-select": TabSelectProps
23
- text: TextProps
24
36
  }
25
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.7",
7
+ "version": "0.1.9",
8
8
  "description": "React renderer for building terminal user interfaces using OpenTUI core",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -35,13 +35,12 @@
35
35
  }
36
36
  },
37
37
  "dependencies": {
38
- "@opentui/core": "0.1.7",
38
+ "@opentui/core": "0.1.9",
39
39
  "react-reconciler": "^0.32.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/bun": "latest",
43
- "@types/react-reconciler": "^0.32.0",
44
- "prettier": "3.6.2"
43
+ "@types/react-reconciler": "^0.32.0"
45
44
  },
46
45
  "peerDependencies": {
47
46
  "react": ">=19.0.0",
@@ -1,11 +1,12 @@
1
- import { BoxRenderable, GroupRenderable, InputRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
2
- import type { RenderableConstructor } from "../types/extend";
1
+ import { ASCIIFontRenderable, BoxRenderable, GroupRenderable, InputRenderable, SelectRenderable, TabSelectRenderable, TextRenderable } from "@opentui/core";
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
  group: typeof GroupRenderable;
7
7
  input: typeof InputRenderable;
8
8
  select: typeof SelectRenderable;
9
+ "ascii-font": typeof ASCIIFontRenderable;
9
10
  "tab-select": typeof TabSelectRenderable;
10
11
  };
11
12
  type ComponentCatalogue = Record<string, RenderableConstructor>;
@@ -24,4 +25,4 @@ export declare const componentCatalogue: ComponentCatalogue;
24
25
  */
25
26
  export declare function extend<T extends ComponentCatalogue>(objects: T): void;
26
27
  export declare function getComponentCatalogue(): ComponentCatalogue;
27
- export type { ExtendedComponentProps, ExtendedIntrinsicElements, RenderableConstructor } from "../types/extend";
28
+ export type { ExtendedComponentProps, ExtendedIntrinsicElements, RenderableConstructor } from "../types/components";
@@ -0,0 +1,4 @@
1
+ export declare const useTerminalDimensions: () => {
2
+ width: number;
3
+ height: number;
4
+ };
package/src/index.d.ts CHANGED
@@ -3,6 +3,6 @@ export * from "./components/app";
3
3
  export * from "./hooks/use-keyboard";
4
4
  export * from "./hooks/use-renderer";
5
5
  export * from "./hooks/use-resize";
6
+ export * from "./hooks/use-terminal-dimensions";
6
7
  export * from "./reconciler/renderer";
7
- export * from "./types/extend";
8
8
  export * from "./types/components";
@@ -20,6 +20,7 @@ import { DefaultEventPriority, NoEventPriority } from "react-reconciler/constant
20
20
 
21
21
  // src/components/index.ts
22
22
  import {
23
+ ASCIIFontRenderable,
23
24
  BoxRenderable,
24
25
  GroupRenderable,
25
26
  InputRenderable,
@@ -33,6 +34,7 @@ var baseComponents = {
33
34
  group: GroupRenderable,
34
35
  input: InputRenderable,
35
36
  select: SelectRenderable,
37
+ "ascii-font": ASCIIFontRenderable,
36
38
  "tab-select": TabSelectRenderable
37
39
  };
38
40
  var componentCatalogue = { ...baseComponents };
@@ -1,32 +1,66 @@
1
- import type { BoxOptions, InputRenderableOptions, RenderableOptions, SelectOption, SelectRenderableOptions, StyledText, TabSelectOption, TabSelectRenderableOptions, TextChunk, TextOptions } from "@opentui/core";
1
+ import type { ASCIIFontOptions, ASCIIFontRenderable, BoxOptions, BoxRenderable, GroupRenderable, InputRenderable, InputRenderableOptions, Renderable, RenderableOptions, SelectOption, SelectRenderable, SelectRenderableOptions, StyledText, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextChunk, TextOptions, TextRenderable } from "@opentui/core";
2
2
  import type React from "react";
3
- type NonStyledProps = "buffered";
4
- type ContainerProps<T> = T & {
5
- children?: React.ReactNode;
3
+ /** Properties that should not be included in the style prop */
4
+ export type NonStyledProps = "buffered" | "live" | "enableLayout" | "selectable";
5
+ /** React-specific props for all components */
6
+ export type ReactProps<TRenderable = unknown> = {
7
+ key?: React.Key;
8
+ ref?: React.Ref<TRenderable>;
6
9
  };
7
- type ComponentProps<T extends RenderableOptions, K extends keyof T = NonStyledProps> = T & {
8
- style?: Partial<Omit<T, K | NonStyledProps>>;
10
+ /** Base type for any renderable constructor */
11
+ export type RenderableConstructor<TRenderable extends Renderable = Renderable> = new (id: string, options: any) => TRenderable;
12
+ /** Extract the options type from a renderable constructor */
13
+ type ExtractRenderableOptions<TConstructor> = TConstructor extends new (id: string, options: infer TOptions) => any ? TOptions : never;
14
+ /** Extract the renderable type from a constructor */
15
+ type ExtractRenderable<TConstructor> = TConstructor extends new (id: string, options: any) => infer TRenderable ? TRenderable : never;
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" : NonStyledProps;
18
+ /** Base props for container components that accept children */
19
+ type ContainerProps<TOptions> = TOptions & {
20
+ children?: React.ReactNode;
9
21
  };
10
- type TextChildren = (string & {}) | number | boolean | null | undefined;
11
- export type TextProps = ComponentProps<TextOptions, "content"> & {
22
+ /** Smart component props that automatically determine excluded properties */
23
+ type ComponentProps<TOptions extends RenderableOptions, TRenderable extends Renderable> = TOptions & {
24
+ style?: Partial<Omit<TOptions, GetNonStyledProperties<RenderableConstructor<TRenderable>>>>;
25
+ } & ReactProps<TRenderable>;
26
+ /** Valid text content types for Text component children */
27
+ type TextChildren = string | number | boolean | null | undefined;
28
+ export type TextProps = ComponentProps<TextOptions, TextRenderable> & {
12
29
  children?: TextChildren | StyledText | TextChunk | Array<TextChildren | StyledText | TextChunk>;
13
30
  };
14
- export type BoxProps = ComponentProps<ContainerProps<BoxOptions>, "title">;
15
- export type GroupProps = ComponentProps<ContainerProps<RenderableOptions>>;
16
- export type InputProps = ComponentProps<InputRenderableOptions> & {
31
+ export type BoxProps = ComponentProps<ContainerProps<BoxOptions>, BoxRenderable>;
32
+ export type GroupProps = ComponentProps<ContainerProps<RenderableOptions>, GroupRenderable>;
33
+ export type InputProps = ComponentProps<InputRenderableOptions, InputRenderable> & {
17
34
  focused?: boolean;
18
35
  onInput?: (value: string) => void;
19
36
  onChange?: (value: string) => void;
20
37
  onSubmit?: (value: string) => void;
21
38
  };
22
- export type SelectProps = ComponentProps<SelectRenderableOptions> & {
39
+ export type SelectProps = ComponentProps<SelectRenderableOptions, SelectRenderable> & {
23
40
  focused?: boolean;
24
41
  onChange?: (index: number, option: SelectOption | null) => void;
25
42
  onSelect?: (index: number, option: SelectOption | null) => void;
26
43
  };
27
- export type TabSelectProps = ComponentProps<TabSelectRenderableOptions> & {
44
+ export type AsciiFontProps = ComponentProps<ASCIIFontOptions, ASCIIFontRenderable>;
45
+ export type TabSelectProps = ComponentProps<TabSelectRenderableOptions, TabSelectRenderable> & {
28
46
  focused?: boolean;
29
47
  onChange?: (index: number, option: TabSelectOption | null) => void;
30
48
  onSelect?: (index: number, option: TabSelectOption | null) => void;
31
49
  };
50
+ /** Convert renderable constructor to component props with proper style exclusions */
51
+ export type ExtendedComponentProps<TConstructor extends RenderableConstructor, TOptions = ExtractRenderableOptions<TConstructor>> = TOptions & {
52
+ children?: React.ReactNode;
53
+ style?: Partial<Omit<TOptions, GetNonStyledProperties<TConstructor>>>;
54
+ } & ReactProps<ExtractRenderable<TConstructor>>;
55
+ /** Helper type to create JSX element properties from a component catalogue */
56
+ export type ExtendedIntrinsicElements<TComponentCatalogue extends Record<string, RenderableConstructor>> = {
57
+ [TComponentName in keyof TComponentCatalogue]: ExtendedComponentProps<TComponentCatalogue[TComponentName]>;
58
+ };
59
+ /**
60
+ * Global augmentation interface for extended components
61
+ * This will be augmented by user code using module augmentation
62
+ */
63
+ export interface OpenTUIComponents {
64
+ [componentName: string]: RenderableConstructor;
65
+ }
32
66
  export {};
@@ -1,15 +0,0 @@
1
- import type { Renderable } from "@opentui/core";
2
- import type React from "react";
3
- export type RenderableConstructor<T extends Renderable = Renderable> = new (id: string, options: any) => T;
4
- type ExtractRenderableOptions<T> = T extends new (id: string, options: infer O) => any ? O : never;
5
- export type ExtendedComponentProps<T extends RenderableConstructor> = ExtractRenderableOptions<T> & {
6
- children?: React.ReactNode;
7
- style?: Partial<ExtractRenderableOptions<T>>;
8
- };
9
- export type ExtendedIntrinsicElements<T extends Record<string, RenderableConstructor>> = {
10
- [K in keyof T]: ExtendedComponentProps<T[K]>;
11
- };
12
- export interface OpenTUIComponents {
13
- [key: string]: RenderableConstructor;
14
- }
15
- export {};