tinky 0.1.0 → 1.0.0

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.ja-JP.md CHANGED
@@ -282,6 +282,8 @@ Tinky は複数のカラー形式をサポートしています:
282
282
 
283
283
  ## 🔧 API リファレンス
284
284
 
285
+ 完全な API ドキュメントは [API ドキュメント](./docs/api/globals.md) を参照してください。
286
+
285
287
  ### render(element, options?)
286
288
 
287
289
  React 要素をターミナルにレンダリングします。
package/README.md CHANGED
@@ -282,6 +282,8 @@ Tinky supports multiple color formats:
282
282
 
283
283
  ## 🔧 API Reference
284
284
 
285
+ For complete API documentation, see the [API Docs](./docs/api/globals.md).
286
+
285
287
  ### render(element, options?)
286
288
 
287
289
  Render a React element to the terminal.
package/README.zh-CN.md CHANGED
@@ -282,6 +282,8 @@ Tinky 支持多种颜色格式:
282
282
 
283
283
  ## 🔧 API 参考
284
284
 
285
+ 完整的 API 文档请参阅 [API 文档](./docs/api/globals.md)。
286
+
285
287
  ### render(element, options?)
286
288
 
287
289
  将 React 元素渲染到终端。
@@ -1,9 +1,30 @@
1
1
  /**
2
- * Context to manage accessibility settings, specifically screen reader support.
2
+ * Value type for the AccessibilityContext.
3
3
  */
4
- export declare const AccessibilityContext: import("react").Context<{
4
+ export interface AccessibilityContextValue {
5
5
  /**
6
6
  * Whether the screen reader is enabled.
7
+ *
8
+ * @defaultValue false
7
9
  */
8
- isScreenReaderEnabled: boolean;
9
- }>;
10
+ readonly isScreenReaderEnabled: boolean;
11
+ }
12
+ /**
13
+ * Context to manage accessibility settings, specifically screen reader.
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * import { useIsScreenReaderEnabled } from 'tinky';
18
+ *
19
+ * const Component = () => {
20
+ * const isScreenReaderEnabled = useIsScreenReaderEnabled();
21
+ *
22
+ * if (isScreenReaderEnabled) {
23
+ * return <Text>Screen reader friendly output</Text>;
24
+ * }
25
+ *
26
+ * return <Text>Regular output</Text>;
27
+ * };
28
+ * ```
29
+ */
30
+ export declare const AccessibilityContext: import("react").Context<AccessibilityContextValue>;
@@ -1,6 +1,21 @@
1
1
  import { createContext } from "react";
2
2
  /**
3
- * Context to manage accessibility settings, specifically screen reader support.
3
+ * Context to manage accessibility settings, specifically screen reader.
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * import { useIsScreenReaderEnabled } from 'tinky';
8
+ *
9
+ * const Component = () => {
10
+ * const isScreenReaderEnabled = useIsScreenReaderEnabled();
11
+ *
12
+ * if (isScreenReaderEnabled) {
13
+ * return <Text>Screen reader friendly output</Text>;
14
+ * }
15
+ *
16
+ * return <Text>Regular output</Text>;
17
+ * };
18
+ * ```
4
19
  */
5
20
  export const AccessibilityContext = createContext({
6
21
  /**
@@ -2,9 +2,24 @@ import { type LiteralUnion } from "type-fest";
2
2
  import { type ForegroundColorName } from "ansi-styles";
3
3
  /**
4
4
  * Union type for background colors.
5
+ *
6
+ * Accepts any named color from `ansi-styles`, hex colors, RGB colors,
7
+ * or ANSI 256 color codes.
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * backgroundColor="red" // Named color
12
+ * backgroundColor="#ff6600" // Hex color
13
+ * backgroundColor="rgb(255, 102, 0)" // RGB color
14
+ * backgroundColor="ansi256:208" // ANSI 256 color
15
+ * ```
5
16
  */
6
17
  export type BackgroundColor = LiteralUnion<ForegroundColorName, string>;
7
18
  /**
8
19
  * Context to pass the background color down the component tree.
20
+ *
21
+ * Allows nested components to inherit the background color from parents.
22
+ *
23
+ * @defaultValue undefined
9
24
  */
10
25
  export declare const backgroundContext: import("react").Context<BackgroundColor | undefined>;
@@ -1,5 +1,9 @@
1
1
  import { createContext } from "react";
2
2
  /**
3
3
  * Context to pass the background color down the component tree.
4
+ *
5
+ * Allows nested components to inherit the background color from parents.
6
+ *
7
+ * @defaultValue undefined
4
8
  */
5
9
  export const backgroundContext = createContext(undefined);
@@ -8,20 +8,29 @@ export interface FocusProps {
8
8
  readonly activeId?: string;
9
9
  /**
10
10
  * Register a new focusable component.
11
+ *
12
+ * @param id - Unique identifier for the focusable component.
13
+ * @param options.autoFocus - Auto-focus the component when registered.
11
14
  */
12
15
  readonly add: (id: string, options: {
13
16
  autoFocus: boolean;
14
17
  }) => void;
15
18
  /**
16
19
  * Unregister a focusable component.
20
+ *
21
+ * @param id - Unique identifier of the component to unregister.
17
22
  */
18
23
  readonly remove: (id: string) => void;
19
24
  /**
20
- * Mark a component as active.
25
+ * Mark a component as active (focused).
26
+ *
27
+ * @param id - Unique identifier of the component to activate.
21
28
  */
22
29
  readonly activate: (id: string) => void;
23
30
  /**
24
- * Mark a component as inactive.
31
+ * Mark a component as inactive (unfocused).
32
+ *
33
+ * @param id - Unique identifier of the component to deactivate.
25
34
  */
26
35
  readonly deactivate: (id: string) => void;
27
36
  /**
@@ -42,6 +51,8 @@ export interface FocusProps {
42
51
  readonly focusPrevious: () => void;
43
52
  /**
44
53
  * Focus a specific component by ID.
54
+ *
55
+ * @param id - Unique identifier of the component to focus.
45
56
  */
46
57
  readonly focus: (id: string) => void;
47
58
  }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Dimensions with width and height.
3
+ */
4
+ export interface Dimension {
5
+ /** Width in columns. */
6
+ width: number;
7
+ /** Height in rows. */
8
+ height: number;
9
+ }
@@ -0,0 +1 @@
1
+ export {};
package/lib/dom.d.ts CHANGED
@@ -4,10 +4,14 @@ import { TaffyNode } from "./taffy-node.js";
4
4
  /**
5
5
  * Interface representing a node in the Tinky tree.
6
6
  */
7
- interface TinkyNode {
7
+ export interface TinkyNode {
8
+ /** Parent DOM element in the tree. */
8
9
  parentNode: DOMElement | undefined;
10
+ /** Taffy layout node for computing dimensions. */
9
11
  taffyNode?: TaffyNode;
12
+ /** Whether this node is inside a Static component. */
10
13
  internal_static?: boolean;
14
+ /** Styles applied to this node. */
11
15
  style: Styles;
12
16
  }
13
17
  /**
@@ -26,35 +30,58 @@ export type NodeNames = ElementNames | TextName;
26
30
  * Interface representing a DOM element in Tinky.
27
31
  */
28
32
  export type DOMElement = {
33
+ /** Name of the element type. */
29
34
  nodeName: ElementNames;
35
+ /** Key-value pairs of element attributes. */
30
36
  attributes: Record<string, DOMNodeAttribute>;
37
+ /** Array of child nodes. */
31
38
  childNodes: DOMNode[];
39
+ /** Function to transform the output of this element. */
32
40
  internal_transform?: OutputTransformer;
41
+ /** Accessibility attributes for screen readers. */
33
42
  internal_accessibility?: {
43
+ /** ARIA role for the element. */
34
44
  role?: "button" | "checkbox" | "combobox" | "list" | "listbox" | "listitem" | "menu" | "menuitem" | "option" | "progressbar" | "radio" | "radiogroup" | "tab" | "tablist" | "table" | "textbox" | "timer" | "toolbar";
45
+ /** ARIA state values for the element. */
35
46
  state?: {
47
+ /** Whether the element is busy. */
36
48
  busy?: boolean;
49
+ /** Whether the element is checked. */
37
50
  checked?: boolean;
51
+ /** Whether the element is disabled. */
38
52
  disabled?: boolean;
53
+ /** Whether the element is expanded. */
39
54
  expanded?: boolean;
55
+ /** Whether the element accepts multiline input. */
40
56
  multiline?: boolean;
57
+ /** Whether multiple items can be selected. */
41
58
  multiselectable?: boolean;
59
+ /** Whether the element is read-only. */
42
60
  readonly?: boolean;
61
+ /** Whether the element is required. */
43
62
  required?: boolean;
63
+ /** Whether the element is selected. */
44
64
  selected?: boolean;
45
65
  };
46
66
  };
67
+ /** Whether static nodes need to be re-rendered. */
47
68
  isStaticDirty?: boolean;
69
+ /** Reference to the Static component node. */
48
70
  staticNode?: DOMElement;
71
+ /** Callback to compute layout before rendering. */
49
72
  onComputeLayout?: () => void;
73
+ /** Callback to trigger a render. */
50
74
  onRender?: () => void;
75
+ /** Callback to trigger an immediate render. */
51
76
  onImmediateRender?: () => void;
52
77
  } & TinkyNode;
53
78
  /**
54
79
  * Interface representing a text node in Tinky.
55
80
  */
56
81
  export type TextNode = {
82
+ /** Text node identifier. */
57
83
  nodeName: TextName;
84
+ /** Text content of the node. */
58
85
  nodeValue: string;
59
86
  } & TinkyNode;
60
87
  /**
@@ -127,4 +154,3 @@ export declare const createTextNode: (text: string) => TextNode;
127
154
  * @param text - The new text value.
128
155
  */
129
156
  export declare const setTextNodeValue: (node: TextNode, text: string) => void;
130
- export {};
@@ -21,6 +21,6 @@
21
21
  * render(<Component />);
22
22
  * ```
23
23
  *
24
- * @returns The app context containing the exit function.
24
+ * @returns The app context containing the `exit` function.
25
25
  */
26
26
  export declare const useApp: () => import("../index.js").AppProps;
@@ -23,6 +23,6 @@ import { AppContext } from "../components/AppContext.js";
23
23
  * render(<Component />);
24
24
  * ```
25
25
  *
26
- * @returns The app context containing the exit function.
26
+ * @returns The app context containing the `exit` function.
27
27
  */
28
28
  export const useApp = () => useContext(AppContext);
@@ -1,8 +1,8 @@
1
1
  import { type FocusProps } from "../components/FocusContext.js";
2
2
  /**
3
- * Output of the useFocusManager hook.
3
+ * Focus management control methods.
4
4
  */
5
- interface Output {
5
+ export interface FocusManager {
6
6
  /**
7
7
  * Enable focus management for all components.
8
8
  */
@@ -36,5 +36,4 @@ interface Output {
36
36
  *
37
37
  * @returns Object containing focus management functions.
38
38
  */
39
- export declare const useFocusManager: () => Output;
40
- export {};
39
+ export declare const useFocusManager: () => FocusManager;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Options for the useFocus hook.
3
3
  */
4
- interface Input {
4
+ export interface FocusOptions {
5
5
  /**
6
6
  * Enable or disable this component's focus, while still maintaining its
7
7
  * position in the list of focusable components.
@@ -23,9 +23,9 @@ interface Input {
23
23
  id?: string;
24
24
  }
25
25
  /**
26
- * Output of the useFocus hook.
26
+ * Focus state and control methods.
27
27
  */
28
- interface Output {
28
+ export interface FocusState {
29
29
  /**
30
30
  * Determines whether this component is focused.
31
31
  */
@@ -63,5 +63,4 @@ interface Output {
63
63
  * @param options - Configuration options.
64
64
  * @returns Focus state and control methods.
65
65
  */
66
- export declare const useFocus: ({ isActive, autoFocus, id: customId, }?: Input) => Output;
67
- export {};
66
+ export declare const useFocus: ({ isActive, autoFocus, id: customId, }?: FocusOptions) => FocusState;
@@ -70,11 +70,11 @@ export interface Key {
70
70
  /**
71
71
  * Handler function for input events.
72
72
  */
73
- type Handler = (input: string, key: Key) => void;
73
+ export type InputHandler = (input: string, key: Key) => void;
74
74
  /**
75
75
  * Options for useInput hook.
76
76
  */
77
- interface Options {
77
+ export interface InputOptions {
78
78
  /**
79
79
  * Enable or disable capturing of user input. Useful when there are
80
80
  * multiple `useInput` hooks used at once to avoid handling same input.
@@ -114,5 +114,4 @@ interface Options {
114
114
  * @param inputHandler - The function to call when input is received.
115
115
  * @param options - Configuration options.
116
116
  */
117
- export declare const useInput: (inputHandler: Handler, options?: Options) => void;
118
- export {};
117
+ export declare const useInput: (inputHandler: InputHandler, options?: InputOptions) => void;
@@ -3,5 +3,21 @@
3
3
  * renders your app.
4
4
  *
5
5
  * @returns The stdout context.
6
+ * @returns {NodeJS.WriteStream} stdout.stdout - Stdout stream passed to
7
+ * `render()` in `options.stdout` or `process.stdout` by default.
8
+ * @returns {function(data: string): void} stdout.write - Write any string to
9
+ * stdout while preserving Tinky's output. Similar to `<Static>`, but only
10
+ * works with strings.
6
11
  */
7
- export declare const useStdout: () => import("../index.js").StdoutProps;
12
+ export declare const useStdout: () => {
13
+ /**
14
+ * Stdout stream passed to `render()` in `options.stdout` or `process.stdout`
15
+ * by default.
16
+ */
17
+ readonly stdout: NodeJS.WriteStream;
18
+ /**
19
+ * Write any string to stdout while preserving Tinky's output. Similar to
20
+ * `<Static>`, but only works with strings.
21
+ */
22
+ readonly write: (data: string) => void;
23
+ };
@@ -5,5 +5,10 @@ import { StdoutContext } from "../components/StdoutContext.js";
5
5
  * renders your app.
6
6
  *
7
7
  * @returns The stdout context.
8
+ * @returns {NodeJS.WriteStream} stdout.stdout - Stdout stream passed to
9
+ * `render()` in `options.stdout` or `process.stdout` by default.
10
+ * @returns {function(data: string): void} stdout.write - Write any string to
11
+ * stdout while preserving Tinky's output. Similar to `<Static>`, but only
12
+ * works with strings.
8
13
  */
9
14
  export const useStdout = () => useContext(StdoutContext);
package/lib/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- export { type RenderOptions, type Instance } from "./render.js";
2
- export { render } from "./render.js";
1
+ export { render, type RenderOptions, type Instance } from "./render.js";
3
2
  export { Box, type BoxProps } from "./components/Box.js";
4
3
  export { Text, type TextProps } from "./components/Text.js";
5
4
  export { AppContext, type AppProps } from "./components/AppContext.js";
@@ -10,13 +9,18 @@ export { Static, type StaticProps } from "./components/Static.js";
10
9
  export { Transform, type TransformProps } from "./components/Transform.js";
11
10
  export { Newline, type NewlineProps } from "./components/Newline.js";
12
11
  export { Spacer } from "./components/Spacer.js";
13
- export { useInput, type Key } from "./hooks/use-input.js";
12
+ export { useInput, type InputHandler, type InputOptions, type Key, } from "./hooks/use-input.js";
14
13
  export { useApp } from "./hooks/use-app.js";
15
14
  export { useStdin } from "./hooks/use-stdin.js";
16
15
  export { useStdout } from "./hooks/use-stdout.js";
17
16
  export { useStderr } from "./hooks/use-stderr.js";
18
- export { useFocus } from "./hooks/use-focus.js";
19
- export { useFocusManager } from "./hooks/use-focus-manager.js";
17
+ export { useFocus, type FocusOptions, type FocusState, } from "./hooks/use-focus.js";
18
+ export { useFocusManager, type FocusManager, } from "./hooks/use-focus-manager.js";
20
19
  export { useIsScreenReaderEnabled } from "./hooks/use-is-screen-reader-enabled.js";
21
20
  export { measureElement } from "./measure-element.js";
22
- export { type DOMElement } from "./dom.js";
21
+ export { type Dimension } from "./dimension.js";
22
+ export { type DOMElement, type DOMNode, type DOMNodeAttribute, type ElementNames, type NodeNames, type TextName, type TextNode, type TinkyNode, } from "./dom.js";
23
+ export { type Styles } from "./styles.js";
24
+ export { type OutputTransformer } from "./render-node-to-output.js";
25
+ export { type RenderMetrics } from "./tinky.js";
26
+ export { type TaffyNode } from "./taffy-node.js";
package/lib/index.js CHANGED
@@ -9,12 +9,12 @@ export { Static } from "./components/Static.js";
9
9
  export { Transform } from "./components/Transform.js";
10
10
  export { Newline } from "./components/Newline.js";
11
11
  export { Spacer } from "./components/Spacer.js";
12
- export { useInput } from "./hooks/use-input.js";
12
+ export { useInput, } from "./hooks/use-input.js";
13
13
  export { useApp } from "./hooks/use-app.js";
14
14
  export { useStdin } from "./hooks/use-stdin.js";
15
15
  export { useStdout } from "./hooks/use-stdout.js";
16
16
  export { useStderr } from "./hooks/use-stderr.js";
17
- export { useFocus } from "./hooks/use-focus.js";
18
- export { useFocusManager } from "./hooks/use-focus-manager.js";
17
+ export { useFocus, } from "./hooks/use-focus.js";
18
+ export { useFocusManager, } from "./hooks/use-focus-manager.js";
19
19
  export { useIsScreenReaderEnabled } from "./hooks/use-is-screen-reader-enabled.js";
20
20
  export { measureElement } from "./measure-element.js";
@@ -1,19 +1,9 @@
1
1
  import { type DOMElement } from "./dom.js";
2
- interface Output {
3
- /**
4
- * Element width.
5
- */
6
- width: number;
7
- /**
8
- * Element height.
9
- */
10
- height: number;
11
- }
2
+ import { type Dimension } from "./dimension.js";
12
3
  /**
13
4
  * Measure the dimensions of a particular `<Box>` element.
14
5
  *
15
6
  * @param node - The DOM element to measure.
16
7
  * @returns The measured dimensions.
17
8
  */
18
- export declare const measureElement: (node: DOMElement) => Output;
19
- export {};
9
+ export declare const measureElement: (node: DOMElement) => Dimension;
@@ -1,7 +1,4 @@
1
- interface Output {
2
- width: number;
3
- height: number;
4
- }
1
+ import { type Dimension } from "./dimension.js";
5
2
  /**
6
3
  * Measures the width and height of a text string.
7
4
  * Results are cached to improve performance.
@@ -9,5 +6,4 @@ interface Output {
9
6
  * @param text - The text string to measure.
10
7
  * @returns The width and height of the text.
11
8
  */
12
- export declare const measureText: (text: string) => Output;
13
- export {};
9
+ export declare const measureText: (text: string) => Dimension;
package/lib/output.d.ts CHANGED
@@ -1,24 +1,16 @@
1
1
  import { type OutputTransformer } from "./render-node-to-output.js";
2
- /**
3
- * Options for creating an Output instance.
4
- */
5
- interface Options {
6
- /**
7
- * Width of the output area.
8
- */
9
- width: number;
10
- /**
11
- * Height of the output area.
12
- */
13
- height: number;
14
- }
2
+ import { type Dimension } from "./dimension.js";
15
3
  /**
16
4
  * Represents a clipping rectangle.
17
5
  */
18
6
  interface Clip {
7
+ /** Left boundary (undefined for no limit). */
19
8
  x1: number | undefined;
9
+ /** Right boundary (undefined for no limit). */
20
10
  x2: number | undefined;
11
+ /** Top boundary (undefined for no limit). */
21
12
  y1: number | undefined;
13
+ /** Bottom boundary (undefined for no limit). */
22
14
  y2: number | undefined;
23
15
  }
24
16
  /**
@@ -36,9 +28,9 @@ export declare class Output {
36
28
  /**
37
29
  * Creates a new Output instance.
38
30
  *
39
- * @param options - Configuration options containing width and height.
31
+ * @param dimension - Dimensions containing width and height.
40
32
  */
41
- constructor(options: Options);
33
+ constructor(dimension: Dimension);
42
34
  /**
43
35
  * Writes text to the output at a specified position.
44
36
  *
package/lib/output.js CHANGED
@@ -17,10 +17,10 @@ export class Output {
17
17
  /**
18
18
  * Creates a new Output instance.
19
19
  *
20
- * @param options - Configuration options containing width and height.
20
+ * @param dimension - Dimensions containing width and height.
21
21
  */
22
- constructor(options) {
23
- const { width, height } = options;
22
+ constructor(dimension) {
23
+ const { width, height } = dimension;
24
24
  this.width = width;
25
25
  this.height = height;
26
26
  }
@@ -1,13 +1,24 @@
1
1
  import { Buffer } from "node:buffer";
2
2
  export declare const nonAlphanumericKeys: string[];
3
+ /**
4
+ * Represents a parsed key press with modifier states.
5
+ */
3
6
  interface ParsedKey {
7
+ /** Name of the key (e.g., "enter", "escape", "a"). */
4
8
  name: string;
9
+ /** Whether the Ctrl modifier key was pressed. */
5
10
  ctrl: boolean;
11
+ /** Whether the Meta (Option/Command) modifier key was pressed. */
6
12
  meta: boolean;
13
+ /** Whether the Shift modifier key was pressed. */
7
14
  shift: boolean;
15
+ /** Whether the Option modifier key was pressed. */
8
16
  option: boolean;
17
+ /** Full key sequence including escape codes. */
9
18
  sequence: string;
19
+ /** Raw input string before parsing. */
10
20
  raw: string | undefined;
21
+ /** Internal key code for special keys. */
11
22
  code?: string;
12
23
  }
13
24
  /**
package/lib/reconciler.js CHANGED
@@ -4,7 +4,7 @@ import { DefaultEventPriority, NoEventPriority, } from "react-reconciler/constan
4
4
  import { Display } from "taffy-layout";
5
5
  import { createContext } from "react";
6
6
  import { createTextNode, appendChildNode, insertBeforeNode, removeChildNode, setStyle, setTextNodeValue, createNode, setAttribute, } from "./dom.js";
7
- import { styles } from "./styles.js";
7
+ import { applyStyles } from "./styles.js";
8
8
  // We need to conditionally perform devtools connection to avoid
9
9
  // accidentally breaking other third-party code.
10
10
  if (process.env["DEV"] === "true") {
@@ -54,10 +54,19 @@ const diff = (before, after) => {
54
54
  }
55
55
  return isChanged ? changed : undefined;
56
56
  };
57
+ /**
58
+ * Cleans up a Taffy node by freeing its resources.
59
+ *
60
+ * @param node - The Taffy node to cleanup.
61
+ */
57
62
  const cleanupTaffyNode = (node) => {
58
63
  node?.free();
59
64
  };
60
65
  let currentUpdatePriority = NoEventPriority;
66
+ /**
67
+ * Reference to the current root DOM element during rendering.
68
+ * Used to track static node state across renders.
69
+ */
61
70
  let currentRootNode;
62
71
  /**
63
72
  * React reconciler implementation for Tinky.
@@ -113,7 +122,7 @@ export const reconciler = createReconciler({
113
122
  if (key === "style") {
114
123
  setStyle(node, value);
115
124
  if (node.taffyNode) {
116
- styles(node.taffyNode, value);
125
+ applyStyles(node.taffyNode, value);
117
126
  }
118
127
  continue;
119
128
  }
@@ -224,7 +233,7 @@ export const reconciler = createReconciler({
224
233
  }
225
234
  }
226
235
  if (style && node.taffyNode) {
227
- styles(node.taffyNode, style);
236
+ applyStyles(node.taffyNode, style);
228
237
  }
229
238
  },
230
239
  commitTextUpdate(node, _oldText, newText) {
package/lib/render.d.ts CHANGED
@@ -23,7 +23,8 @@ export interface RenderOptions {
23
23
  */
24
24
  stderr?: NodeJS.WriteStream;
25
25
  /**
26
- * If true, each update will be rendered as separate output, without replacing.
26
+ * If true, each update will be rendered as separate output, without
27
+ * replacing.
27
28
  *
28
29
  * @defaultValue false
29
30
  */
@@ -63,7 +64,8 @@ export interface RenderOptions {
63
64
  maxFps?: number;
64
65
  /**
65
66
  * Enable incremental rendering mode which only updates changed lines instead
66
- * of redrawing the entire output. Reduces flickering and improves performance.
67
+ * of redrawing the entire output. Reduces flickering and improves
68
+ * performance.
67
69
  *
68
70
  * @defaultValue false
69
71
  */
package/lib/styles.d.ts CHANGED
@@ -399,4 +399,4 @@ export interface Styles {
399
399
  */
400
400
  readonly gridTemplateAreas?: GridTemplateArea[];
401
401
  }
402
- export declare const styles: (node: TaffyNode, style?: Styles) => void;
402
+ export declare const applyStyles: (node: TaffyNode, style?: Styles) => void;
package/lib/styles.js CHANGED
@@ -617,7 +617,7 @@ const applyGridStyles = (taffyStyle, style) => {
617
617
  }
618
618
  }
619
619
  };
620
- export const styles = (node, style = {}) => {
620
+ export const applyStyles = (node, style = {}) => {
621
621
  const taffyStyle = node.tree.getStyle(node.id);
622
622
  applyPositionStyles(taffyStyle, style);
623
623
  applyMarginStyles(taffyStyle, style);
@@ -35,5 +35,10 @@ export declare class TaffyNode {
35
35
  * Necessary to prevent memory leaks as Taffy nodes aren't GC'd automatically.
36
36
  */
37
37
  free(): void;
38
+ /**
39
+ * Recursively removes a node and all its descendants from the Taffy tree.
40
+ *
41
+ * @param id - The ID of the node to remove.
42
+ */
38
43
  private freeRecursive;
39
44
  }
package/lib/taffy-node.js CHANGED
@@ -51,6 +51,11 @@ export class TaffyNode {
51
51
  }
52
52
  this.tree.remove(this.id);
53
53
  }
54
+ /**
55
+ * Recursively removes a node and all its descendants from the Taffy tree.
56
+ *
57
+ * @param id - The ID of the node to remove.
58
+ */
54
59
  freeRecursive(id) {
55
60
  for (const childId of this.tree.children(id)) {
56
61
  this.freeRecursive(childId);
package/lib/tinky.d.ts CHANGED
@@ -68,19 +68,33 @@ export interface Options {
68
68
  * lifecycle, and terminal output.
69
69
  */
70
70
  export declare class Tinky {
71
+ /** Configuration options for this instance. */
71
72
  private readonly options;
73
+ /** Log update instance for output. */
72
74
  private readonly log;
75
+ /** Throttled log update instance for output. */
73
76
  private readonly throttledLog;
77
+ /** Whether screen reader support is enabled. */
74
78
  private readonly isScreenReaderEnabled;
79
+ /** Whether the app has been unmounted. */
75
80
  private isUnmounted;
81
+ /** Last output string that was rendered. */
76
82
  private lastOutput;
83
+ /** Height of the last output in lines. */
77
84
  private lastOutputHeight;
85
+ /** Width of the terminal at last render. */
78
86
  private lastTerminalWidth;
87
+ /** React reconciler container. */
79
88
  private readonly container;
89
+ /** Root DOM element for the React tree. */
80
90
  private readonly rootNode;
91
+ /** Full static output for debug mode. */
81
92
  private fullStaticOutput;
93
+ /** Promise that resolves when the app exits. */
82
94
  private exitPromise?;
95
+ /** Function to restore console after patching. */
83
96
  private restoreConsole?;
97
+ /** Function to unsubscribe from resize events. */
84
98
  private readonly unsubscribeResize?;
85
99
  /**
86
100
  * Creates an instance of Tinky.
@@ -99,9 +113,13 @@ export declare class Tinky {
99
113
  * Clears the screen when width decreases to prevent overlapping re-renders.
100
114
  */
101
115
  resized: () => void;
116
+ /** Resolves the exit promise when the app unmounts. */
102
117
  resolveExitPromise: () => void;
118
+ /** Rejects the exit promise with an error. */
103
119
  rejectExitPromise: (reason?: Error) => void;
120
+ /** Unsubscribes from the exit event. */
104
121
  unsubscribeExit: () => void;
122
+ /** Error that occurred during rendering, if any. */
105
123
  renderError: Error | null;
106
124
  /**
107
125
  * Calculates the layout of the UI (TaffyLayout).
package/lib/tinky.js CHANGED
@@ -23,24 +23,33 @@ const noop = () => {
23
23
  * lifecycle, and terminal output.
24
24
  */
25
25
  export class Tinky {
26
+ /** Configuration options for this instance. */
26
27
  options;
28
+ /** Log update instance for output. */
27
29
  log;
30
+ /** Throttled log update instance for output. */
28
31
  throttledLog;
32
+ /** Whether screen reader support is enabled. */
29
33
  isScreenReaderEnabled;
30
- // Ignore last render after unmounting a tree to prevent empty output before
31
- // exit
34
+ /** Whether the app has been unmounted. */
32
35
  isUnmounted;
36
+ /** Last output string that was rendered. */
33
37
  lastOutput;
38
+ /** Height of the last output in lines. */
34
39
  lastOutputHeight;
40
+ /** Width of the terminal at last render. */
35
41
  lastTerminalWidth;
42
+ /** React reconciler container. */
36
43
  container;
44
+ /** Root DOM element for the React tree. */
37
45
  rootNode;
38
- // This variable is used only in debug mode to store full static output so
39
- // that it's rerendered every time, not just new static parts, like in
40
- // non-debug mode
46
+ /** Full static output for debug mode. */
41
47
  fullStaticOutput;
48
+ /** Promise that resolves when the app exits. */
42
49
  exitPromise;
50
+ /** Function to restore console after patching. */
43
51
  restoreConsole;
52
+ /** Function to unsubscribe from resize events. */
44
53
  unsubscribeResize;
45
54
  /**
46
55
  * Creates an instance of Tinky.
@@ -134,10 +143,13 @@ export class Tinky {
134
143
  this.onRender();
135
144
  this.lastTerminalWidth = currentWidth;
136
145
  };
146
+ /** Resolves the exit promise when the app unmounts. */
137
147
  resolveExitPromise = noop;
148
+ /** Rejects the exit promise with an error. */
138
149
  rejectExitPromise = noop;
150
+ /** Unsubscribes from the exit event. */
139
151
  unsubscribeExit = noop;
140
- // Track whether an error occurred during rendering
152
+ /** Error that occurred during rendering, if any. */
141
153
  renderError = null;
142
154
  /**
143
155
  * Calculates the layout of the UI (TaffyLayout).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinky",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "React for CLIs, re-imagined with the Taffy engine",
5
5
  "keywords": [
6
6
  "react",
@@ -26,39 +26,53 @@
26
26
  },
27
27
  "type": "module",
28
28
  "main": "./lib/index.js",
29
+ "files": [
30
+ "./bin/*",
31
+ "./lib/*"
32
+ ],
29
33
  "scripts": {
30
34
  "test": "bun test",
31
- "build": "tsc",
35
+ "build": "tsc && npm run docs",
32
36
  "lint": "tslint -c tslint.json src/**/*.ts",
33
37
  "prepublish": "npm run build",
34
- "prepare": "husky"
38
+ "prepare": "husky",
39
+ "docs": "typedoc --plugin typedoc-plugin-markdown --disableSources --out docs/api && prettier --write docs/api"
35
40
  },
36
41
  "dependencies": {
37
42
  "@alcalzone/ansi-tokenize": "^0.2.3",
43
+ "ansi-escapes": "^7.2.0",
38
44
  "ansi-styles": "^6.2.3",
39
45
  "auto-bind": "^5.0.1",
40
46
  "chalk": "^5.6.2",
41
47
  "cli-boxes": "^4.0.1",
48
+ "cli-cursor": "^5.0.0",
49
+ "cli-truncate": "^5.1.1",
42
50
  "code-excerpt": "^4.0.0",
43
51
  "es-toolkit": "^1.43.0",
44
52
  "indent-string": "^5.0.0",
45
53
  "is-in-ci": "^2.0.0",
46
54
  "patch-console": "^2.0.0",
47
55
  "react-reconciler": "^0.33.0",
56
+ "signal-exit": "^4.1.0",
48
57
  "stack-utils": "^2.0.6",
49
58
  "taffy-layout": "^0.1.0",
50
59
  "tslint": "^5.20.1",
51
60
  "type-fest": "^5.4.1",
52
- "widest-line": "^5.0.0"
61
+ "widest-line": "^5.0.0",
62
+ "wrap-ansi": "^9.0.2"
63
+ },
64
+ "peerDependencies": {
65
+ "@types/react": ">=19.0.0",
66
+ "react": ">=19.0.0",
67
+ "react-devtools-core": ">=7.0.0"
53
68
  },
54
- "files": [
55
- "./bin/*",
56
- "./lib/*"
57
- ],
58
69
  "typings": "./lib/index.d.ts",
59
70
  "devDependencies": {
60
71
  "@commitlint/cli": "^20.3.0",
61
72
  "@commitlint/config-conventional": "^20.3.0",
73
+ "@semantic-release/changelog": "^6.0.3",
74
+ "@semantic-release/git": "^10.0.1",
75
+ "@semantic-release/npm": "^13.1.3",
62
76
  "@sinonjs/fake-timers": "^15.1.0",
63
77
  "@types/bun": "^1.3.6",
64
78
  "@types/react-reconciler": "^0.32.3",
@@ -73,7 +87,10 @@
73
87
  "jiti": "^2.6.1",
74
88
  "lint-staged": "^16.2.7",
75
89
  "prettier": "^3.7.4",
90
+ "semantic-release": "^25.0.2",
76
91
  "sinon": "^21.0.1",
92
+ "typedoc": "^0.28.16",
93
+ "typedoc-plugin-markdown": "^4.9.0",
77
94
  "typescript-eslint": "^8.53.0"
78
95
  },
79
96
  "commitlint": {