@opentuah/react 0.1.77

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.
@@ -0,0 +1,2 @@
1
+ export { Fragment, jsxDEV } from "react/jsx-dev-runtime"
2
+ export type * from "./jsx-namespace.d.ts"
@@ -0,0 +1 @@
1
+ export { Fragment, jsxDEV } from "react/jsx-dev-runtime"
@@ -0,0 +1,62 @@
1
+ import type * as React from "react"
2
+ import type {
3
+ AsciiFontProps,
4
+ BoxProps,
5
+ CodeProps,
6
+ DiffProps,
7
+ ExtendedIntrinsicElements,
8
+ InputProps,
9
+ LineBreakProps,
10
+ LineNumberProps,
11
+ LinkProps,
12
+ MarkdownProps,
13
+ OpenTUIComponents,
14
+ ScrollBoxProps,
15
+ SelectProps,
16
+ SpanProps,
17
+ TabSelectProps,
18
+ TextareaProps,
19
+ TextProps,
20
+ } from "./src/types/components"
21
+
22
+ export namespace JSX {
23
+ type Element = React.ReactNode
24
+
25
+ interface ElementClass extends React.ComponentClass<any> {
26
+ render(): React.ReactNode
27
+ }
28
+
29
+ interface ElementAttributesProperty {
30
+ props: {}
31
+ }
32
+
33
+ interface ElementChildrenAttribute {
34
+ children: {}
35
+ }
36
+
37
+ interface IntrinsicAttributes extends React.Attributes {}
38
+
39
+ interface IntrinsicElements extends React.JSX.IntrinsicElements, ExtendedIntrinsicElements<OpenTUIComponents> {
40
+ box: BoxProps
41
+ text: TextProps
42
+ span: SpanProps
43
+ code: CodeProps
44
+ diff: DiffProps
45
+ markdown: MarkdownProps
46
+ input: InputProps
47
+ textarea: TextareaProps
48
+ select: SelectProps
49
+ scrollbox: ScrollBoxProps
50
+ "ascii-font": AsciiFontProps
51
+ "tab-select": TabSelectProps
52
+ "line-number": LineNumberProps
53
+ // Text modifiers
54
+ b: SpanProps
55
+ i: SpanProps
56
+ u: SpanProps
57
+ strong: SpanProps
58
+ em: SpanProps
59
+ br: LineBreakProps
60
+ a: LinkProps
61
+ }
62
+ }
@@ -0,0 +1,2 @@
1
+ export { Fragment, jsx, jsxs } from "react/jsx-runtime"
2
+ export type * from "./jsx-namespace.d.ts"
package/jsx-runtime.js ADDED
@@ -0,0 +1 @@
1
+ export { Fragment, jsx, jsxs } from "react/jsx-runtime"
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@opentuah/react",
3
+ "module": "index.js",
4
+ "main": "index.js",
5
+ "types": "src/index.d.ts",
6
+ "type": "module",
7
+ "version": "0.1.77",
8
+ "description": "React renderer for building terminal user interfaces using OpenTUI core",
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/anomalyco/opentui",
13
+ "directory": "packages/react"
14
+ },
15
+ "exports": {
16
+ ".": {
17
+ "types": "./src/index.d.ts",
18
+ "import": "./index.js",
19
+ "require": "./index.js"
20
+ },
21
+ "./renderer": {
22
+ "types": "./src/reconciler/renderer.d.ts",
23
+ "import": "./index.js",
24
+ "require": "./index.js"
25
+ },
26
+ "./test-utils": {
27
+ "types": "./src/test-utils.d.ts",
28
+ "import": "./test-utils.js",
29
+ "require": "./test-utils.js"
30
+ },
31
+ "./jsx-runtime": {
32
+ "types": "./jsx-runtime.d.ts",
33
+ "import": "./jsx-runtime.js",
34
+ "require": "./jsx-runtime.js"
35
+ },
36
+ "./jsx-dev-runtime": {
37
+ "types": "./jsx-dev-runtime.d.ts",
38
+ "import": "./jsx-dev-runtime.js",
39
+ "require": "./jsx-dev-runtime.js"
40
+ }
41
+ },
42
+ "dependencies": {
43
+ "@opentuah/core": "0.1.77",
44
+ "react-reconciler": "^0.32.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/bun": "latest",
48
+ "@types/node": "^24.0.0",
49
+ "@types/react": "^19.0.0",
50
+ "@types/react-reconciler": "^0.32.0",
51
+ "@types/ws": "^8.18.1",
52
+ "react": ">=19.0.0",
53
+ "react-devtools-core": "^7.0.1",
54
+ "typescript": "^5",
55
+ "ws": "^8.18.0"
56
+ },
57
+ "peerDependencies": {
58
+ "react": ">=19.0.0",
59
+ "react-devtools-core": "^7.0.1",
60
+ "ws": "^8.18.0"
61
+ }
62
+ }
@@ -0,0 +1,8 @@
1
+ import type { CliRenderer, KeyHandler } from "@opentui/core";
2
+ interface AppContext {
3
+ keyHandler: KeyHandler | null;
4
+ renderer: CliRenderer | null;
5
+ }
6
+ export declare const AppContext: import("react").Context<AppContext>;
7
+ export declare const useAppContext: () => AppContext;
8
+ export {};
@@ -0,0 +1,16 @@
1
+ import React from "react";
2
+ export declare class ErrorBoundary extends React.Component<{
3
+ children: React.ReactNode;
4
+ }, {
5
+ hasError: boolean;
6
+ error: Error | null;
7
+ }> {
8
+ constructor(props: {
9
+ children: React.ReactNode;
10
+ });
11
+ static getDerivedStateFromError(error: Error): {
12
+ hasError: boolean;
13
+ error: Error;
14
+ };
15
+ render(): any;
16
+ }
@@ -0,0 +1,42 @@
1
+ import { ASCIIFontRenderable, BoxRenderable, CodeRenderable, DiffRenderable, InputRenderable, LineNumberRenderable, MarkdownRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextareaRenderable, TextRenderable } from "@opentui/core";
2
+ import type { RenderableConstructor } from "../types/components";
3
+ import { BoldSpanRenderable, ItalicSpanRenderable, LineBreakRenderable, LinkRenderable, SpanRenderable, UnderlineSpanRenderable } from "./text";
4
+ export declare const baseComponents: {
5
+ box: typeof BoxRenderable;
6
+ text: typeof TextRenderable;
7
+ code: typeof CodeRenderable;
8
+ diff: typeof DiffRenderable;
9
+ markdown: typeof MarkdownRenderable;
10
+ input: typeof InputRenderable;
11
+ select: typeof SelectRenderable;
12
+ textarea: typeof TextareaRenderable;
13
+ scrollbox: typeof ScrollBoxRenderable;
14
+ "ascii-font": typeof ASCIIFontRenderable;
15
+ "tab-select": typeof TabSelectRenderable;
16
+ "line-number": typeof LineNumberRenderable;
17
+ span: typeof SpanRenderable;
18
+ br: typeof LineBreakRenderable;
19
+ b: typeof BoldSpanRenderable;
20
+ strong: typeof BoldSpanRenderable;
21
+ i: typeof ItalicSpanRenderable;
22
+ em: typeof ItalicSpanRenderable;
23
+ u: typeof UnderlineSpanRenderable;
24
+ a: typeof LinkRenderable;
25
+ };
26
+ type ComponentCatalogue = Record<string, RenderableConstructor>;
27
+ export declare const componentCatalogue: ComponentCatalogue;
28
+ /**
29
+ * Extend the component catalogue with new renderable components
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * // Extend with an object of components
34
+ * extend({
35
+ * consoleButton: ConsoleButtonRenderable,
36
+ * customBox: CustomBoxRenderable
37
+ * })
38
+ * ```
39
+ */
40
+ export declare function extend<T extends ComponentCatalogue>(objects: T): void;
41
+ export declare function getComponentCatalogue(): ComponentCatalogue;
42
+ export type { ExtendedComponentProps, ExtendedIntrinsicElements, RenderableConstructor } from "../types/components";
@@ -0,0 +1,30 @@
1
+ import { TextNodeRenderable, type RenderContext, type TextNodeOptions } from "@opentui/core";
2
+ export declare const textNodeKeys: readonly ["span", "b", "strong", "i", "em", "u", "br", "a"];
3
+ export type TextNodeKey = (typeof textNodeKeys)[number];
4
+ export declare class SpanRenderable extends TextNodeRenderable {
5
+ private readonly ctx;
6
+ constructor(ctx: RenderContext | null, options: TextNodeOptions);
7
+ }
8
+ declare class TextModifierRenderable extends SpanRenderable {
9
+ constructor(options: TextNodeOptions, modifier?: TextNodeKey);
10
+ }
11
+ export declare class BoldSpanRenderable extends TextModifierRenderable {
12
+ constructor(_ctx: RenderContext | null, options: TextNodeOptions);
13
+ }
14
+ export declare class ItalicSpanRenderable extends TextModifierRenderable {
15
+ constructor(_ctx: RenderContext | null, options: TextNodeOptions);
16
+ }
17
+ export declare class UnderlineSpanRenderable extends TextModifierRenderable {
18
+ constructor(_ctx: RenderContext | null, options: TextNodeOptions);
19
+ }
20
+ export declare class LineBreakRenderable extends SpanRenderable {
21
+ constructor(_ctx: RenderContext | null, options: TextNodeOptions);
22
+ add(): number;
23
+ }
24
+ export interface LinkOptions extends TextNodeOptions {
25
+ href: string;
26
+ }
27
+ export declare class LinkRenderable extends SpanRenderable {
28
+ constructor(_ctx: RenderContext | null, options: LinkOptions);
29
+ }
30
+ export {};
@@ -0,0 +1,5 @@
1
+ export * from "./use-keyboard";
2
+ export * from "./use-renderer";
3
+ export * from "./use-resize";
4
+ export * from "./use-terminal-dimensions";
5
+ export * from "./use-timeline";
@@ -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 useEffectEvent<T extends (...args: any[]) => any>(handler: T): T;
@@ -0,0 +1,22 @@
1
+ import type { KeyEvent } from "@opentui/core";
2
+ export interface UseKeyboardOptions {
3
+ /** Include release events - callback receives events with eventType: "release" */
4
+ release?: boolean;
5
+ }
6
+ /**
7
+ * Subscribe to keyboard events.
8
+ *
9
+ * By default, only receives press events (including key repeats with `repeated: true`).
10
+ * Use `options.release` to also receive release events.
11
+ *
12
+ * @example
13
+ * // Basic press handling (includes repeats)
14
+ * useKeyboard((e) => console.log(e.name, e.repeated ? "(repeat)" : ""))
15
+ *
16
+ * // With release events
17
+ * useKeyboard((e) => {
18
+ * if (e.eventType === "release") keys.delete(e.name)
19
+ * else keys.add(e.name)
20
+ * }, { release: true })
21
+ */
22
+ export declare const useKeyboard: (handler: (key: KeyEvent) => void, options?: UseKeyboardOptions) => void;
@@ -0,0 +1 @@
1
+ export declare const useRenderer: () => import("node_modules/@opentuah/core/dist").CliRenderer;
@@ -0,0 +1 @@
1
+ export declare const useOnResize: (callback: (width: number, height: number) => void) => import("node_modules/@opentuah/core/dist").CliRenderer;
@@ -0,0 +1,4 @@
1
+ export declare const useTerminalDimensions: () => {
2
+ width: number;
3
+ height: number;
4
+ };
@@ -0,0 +1,2 @@
1
+ import { Timeline, type TimelineOptions } from "@opentui/core";
2
+ export declare const useTimeline: (options?: TimelineOptions) => Timeline;
package/src/index.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ export * from "./components";
2
+ export * from "./components/app";
3
+ export * from "./hooks";
4
+ export * from "./reconciler/renderer";
5
+ export * from "./types/components";
6
+ export { createElement } from "react";
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ import "./devtools-polyfill";
@@ -0,0 +1,9 @@
1
+ import type { HostConfig } from "react-reconciler";
2
+ import type { Container, HostContext, Instance, Props, PublicInstance, TextInstance, Type } from "../types/host";
3
+ export declare const hostConfig: HostConfig<Type, Props, Container, Instance, TextInstance, unknown, // SuspenseInstance
4
+ unknown, // HydratableInstance
5
+ unknown, // FormInstance
6
+ PublicInstance, HostContext, unknown, // ChildSet
7
+ unknown, // TimeoutHandle
8
+ unknown, // NoTimeout
9
+ unknown>;
@@ -0,0 +1,5 @@
1
+ import type { RootRenderable } from "@opentui/core";
2
+ import React from "react";
3
+ import ReactReconciler from "react-reconciler";
4
+ export declare const reconciler: ReactReconciler.Reconciler<RootRenderable, import("@opentui/core").BaseRenderable, import("@opentui/core").TextNodeRenderable, unknown, unknown, import("@opentui/core").BaseRenderable>;
5
+ export declare function _render(element: React.ReactNode, root: RootRenderable): any;
@@ -0,0 +1,23 @@
1
+ import { CliRenderer } from "@opentui/core";
2
+ import { type ReactNode } from "react";
3
+ declare const flushSync: {
4
+ (): void;
5
+ <R>(fn: () => R): R;
6
+ };
7
+ declare const createPortal: (children: ReactNode, containerInfo: any, implementation: any, key?: string | null) => import("react-reconciler").ReactPortal;
8
+ export type Root = {
9
+ render: (node: ReactNode) => void;
10
+ unmount: () => void;
11
+ };
12
+ /**
13
+ * Creates a root for rendering a React tree with the given CLI renderer.
14
+ * @param renderer The CLI renderer to use
15
+ * @returns A root object with a `render` method
16
+ * @example
17
+ * ```tsx
18
+ * const renderer = await createCliRenderer()
19
+ * createRoot(renderer).render(<App />)
20
+ * ```
21
+ */
22
+ export declare function createRoot(renderer: CliRenderer): Root;
23
+ export { createPortal, flushSync };
@@ -0,0 +1,11 @@
1
+ import { type TestRendererOptions } from "@opentui/core/testing";
2
+ import { type ReactNode } from "react";
3
+ export declare function testRender(node: ReactNode, testRendererOptions: TestRendererOptions): Promise<{
4
+ renderer: import("@opentui/core/testing").TestRenderer;
5
+ mockInput: import("@opentui/core/testing").MockInput;
6
+ mockMouse: import("@opentui/core/testing").MockMouse;
7
+ renderOnce: () => Promise<void>;
8
+ captureCharFrame: () => string;
9
+ captureSpans: () => import("node_modules/@opentuah/core/dist").CapturedFrame;
10
+ resize: (width: number, height: number) => void;
11
+ }>;
@@ -0,0 +1,86 @@
1
+ import type { ASCIIFontOptions, ASCIIFontRenderable, BaseRenderable, BoxOptions, BoxRenderable, CodeOptions, CodeRenderable, DiffRenderable, DiffRenderableOptions, InputRenderable, InputRenderableOptions, LineNumberOptions, LineNumberRenderable, MarkdownOptions, MarkdownRenderable, RenderableOptions, RenderContext, ScrollBoxOptions, ScrollBoxRenderable, SelectOption, SelectRenderable, SelectRenderableOptions, TabSelectOption, TabSelectRenderable, TabSelectRenderableOptions, TextareaOptions, TextareaRenderable, TextNodeOptions, TextNodeRenderable, TextOptions, TextRenderable } from "@opentui/core";
2
+ import type React from "react";
3
+ /** Properties that should not be included in the style prop */
4
+ export type NonStyledProps = "id" | "buffered" | "live" | "enableLayout" | "selectable" | "renderAfter" | "renderBefore" | `on${string}`;
5
+ /** React-specific props for all components */
6
+ export type ReactProps<TRenderable = unknown> = {
7
+ key?: React.Key;
8
+ ref?: React.Ref<TRenderable>;
9
+ };
10
+ /** Base type for any renderable constructor */
11
+ export type RenderableConstructor<TRenderable extends BaseRenderable = BaseRenderable> = new (ctx: RenderContext, options: any) => TRenderable;
12
+ /** Extract the options type from a renderable constructor */
13
+ type ExtractRenderableOptions<TConstructor> = TConstructor extends new (ctx: RenderContext, options: infer TOptions) => any ? TOptions : never;
14
+ /** Extract the renderable type from a constructor */
15
+ type ExtractRenderable<TConstructor> = TConstructor extends new (ctx: RenderContext, 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" : TConstructor extends RenderableConstructor<InputRenderable> ? NonStyledProps | "placeholder" | "value" : TConstructor extends RenderableConstructor<TextareaRenderable> ? NonStyledProps | "placeholder" | "initialValue" : TConstructor extends RenderableConstructor<CodeRenderable> ? NonStyledProps | "content" | "filetype" | "syntaxStyle" | "treeSitterClient" | "conceal" | "drawUnstyledText" : TConstructor extends RenderableConstructor<MarkdownRenderable> ? NonStyledProps | "content" | "syntaxStyle" | "treeSitterClient" | "conceal" | "renderNode" : NonStyledProps;
18
+ /** Base props for container components that accept children */
19
+ type ContainerProps<TOptions> = TOptions & {
20
+ children?: React.ReactNode;
21
+ };
22
+ /** Smart component props that automatically determine excluded properties */
23
+ type ComponentProps<TOptions extends RenderableOptions<TRenderable>, TRenderable extends BaseRenderable> = 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 | React.ReactNode;
28
+ export type TextProps = ComponentProps<TextOptions, TextRenderable> & {
29
+ children?: TextChildren;
30
+ };
31
+ export type SpanProps = ComponentProps<TextNodeOptions, TextNodeRenderable> & {
32
+ children?: TextChildren;
33
+ };
34
+ export type LinkProps = SpanProps & {
35
+ href: string;
36
+ };
37
+ export type LineBreakProps = Pick<SpanProps, "id">;
38
+ export type BoxProps = ComponentProps<ContainerProps<BoxOptions>, BoxRenderable> & {
39
+ focused?: boolean;
40
+ };
41
+ export type InputProps = ComponentProps<InputRenderableOptions, InputRenderable> & {
42
+ focused?: boolean;
43
+ onInput?: (value: string) => void;
44
+ onChange?: (value: string) => void;
45
+ onSubmit?: (value: string) => void;
46
+ };
47
+ export type TextareaProps = ComponentProps<TextareaOptions, TextareaRenderable> & {
48
+ focused?: boolean;
49
+ };
50
+ export type CodeProps = ComponentProps<CodeOptions, CodeRenderable>;
51
+ export type MarkdownProps = ComponentProps<MarkdownOptions, MarkdownRenderable>;
52
+ export type DiffProps = ComponentProps<DiffRenderableOptions, DiffRenderable>;
53
+ export type SelectProps = ComponentProps<SelectRenderableOptions, SelectRenderable> & {
54
+ focused?: boolean;
55
+ onChange?: (index: number, option: SelectOption | null) => void;
56
+ onSelect?: (index: number, option: SelectOption | null) => void;
57
+ };
58
+ export type ScrollBoxProps = ComponentProps<ContainerProps<ScrollBoxOptions>, ScrollBoxRenderable> & {
59
+ focused?: boolean;
60
+ };
61
+ export type AsciiFontProps = ComponentProps<ASCIIFontOptions, ASCIIFontRenderable>;
62
+ export type TabSelectProps = ComponentProps<TabSelectRenderableOptions, TabSelectRenderable> & {
63
+ focused?: boolean;
64
+ onChange?: (index: number, option: TabSelectOption | null) => void;
65
+ onSelect?: (index: number, option: TabSelectOption | null) => void;
66
+ };
67
+ export type LineNumberProps = ComponentProps<ContainerProps<LineNumberOptions>, LineNumberRenderable> & {
68
+ focused?: boolean;
69
+ };
70
+ /** Convert renderable constructor to component props with proper style exclusions */
71
+ export type ExtendedComponentProps<TConstructor extends RenderableConstructor, TOptions = ExtractRenderableOptions<TConstructor>> = TOptions & {
72
+ children?: React.ReactNode;
73
+ style?: Partial<Omit<TOptions, GetNonStyledProperties<TConstructor>>>;
74
+ } & ReactProps<ExtractRenderable<TConstructor>>;
75
+ /** Helper type to create JSX element properties from a component catalogue */
76
+ export type ExtendedIntrinsicElements<TComponentCatalogue extends Record<string, RenderableConstructor>> = {
77
+ [TComponentName in keyof TComponentCatalogue]: ExtendedComponentProps<TComponentCatalogue[TComponentName]>;
78
+ };
79
+ /**
80
+ * Global augmentation interface for extended components
81
+ * This will be augmented by user code using module augmentation
82
+ */
83
+ export interface OpenTUIComponents {
84
+ [componentName: string]: RenderableConstructor;
85
+ }
86
+ export {};
@@ -0,0 +1,11 @@
1
+ import type { BaseRenderable, RootRenderable, TextNodeRenderable } from "@opentui/core";
2
+ import { baseComponents } from "../components";
3
+ export type Type = keyof typeof baseComponents;
4
+ export type Props = Record<string, any>;
5
+ export type Container = RootRenderable;
6
+ export type Instance = BaseRenderable;
7
+ export type TextInstance = TextNodeRenderable;
8
+ export type PublicInstance = Instance;
9
+ export type HostContext = Record<string, any> & {
10
+ isInsideText?: boolean;
11
+ };
@@ -0,0 +1,2 @@
1
+ import type { Type } from "../types/host";
2
+ export declare function getNextId(type: Type): string;
@@ -0,0 +1,3 @@
1
+ import type { Instance, Props, Type } from "../types/host";
2
+ export declare function setInitialProperties(instance: Instance, type: Type, props: Props): void;
3
+ export declare function updateProperties(instance: Instance, type: Type, oldProps: Props, newProps: Props): void;
package/test-utils.js ADDED
@@ -0,0 +1,39 @@
1
+ // @bun
2
+ import {
3
+ createRoot
4
+ } from "./chunk-7627c4s7.js";
5
+ import"./chunk-eecw9x2f.js";
6
+
7
+ // src/test-utils.ts
8
+ import { createTestRenderer } from "@opentui/core/testing";
9
+ import { act } from "react";
10
+ function setIsReactActEnvironment(isReactActEnvironment) {
11
+ globalThis.IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
12
+ }
13
+ async function testRender(node, testRendererOptions) {
14
+ let root = null;
15
+ setIsReactActEnvironment(true);
16
+ const testSetup = await createTestRenderer({
17
+ ...testRendererOptions,
18
+ onDestroy() {
19
+ act(() => {
20
+ if (root) {
21
+ root.unmount();
22
+ root = null;
23
+ }
24
+ });
25
+ testRendererOptions.onDestroy?.();
26
+ setIsReactActEnvironment(false);
27
+ }
28
+ });
29
+ root = createRoot(testSetup.renderer);
30
+ act(() => {
31
+ if (root) {
32
+ root.render(node);
33
+ }
34
+ });
35
+ return testSetup;
36
+ }
37
+ export {
38
+ testRender
39
+ };