UNPKG

ink

Version:
136 lines (120 loc) 5.54 kB
import { type ReactNode } from 'react'; import { type CursorPosition } from './log-update.js'; import { type KittyKeyboardOptions } from './kitty-keyboard.js'; /** Performance metrics for a render operation. */ export type RenderMetrics = { /** Time spent rendering in milliseconds. */ renderTime: number; }; export type Options = { stdout: NodeJS.WriteStream; stdin: NodeJS.ReadStream; stderr: NodeJS.WriteStream; debug: boolean; exitOnCtrlC: boolean; patchConsole: boolean; onRender?: (metrics: RenderMetrics) => void; isScreenReaderEnabled?: boolean; waitUntilExit?: () => Promise<unknown>; maxFps?: number; incrementalRendering?: boolean; /** Enable React Concurrent Rendering mode. When enabled: - Suspense boundaries work correctly with async data - `useTransition` and `useDeferredValue` are fully functional - Updates can be interrupted for higher priority work Note: Concurrent mode changes the timing of renders. Some tests may need to use `act()` to properly await updates. Reusing the same stdout across multiple `render()` calls without unmounting is unsupported. Call `unmount()` first if you need to change the rendering mode or create a fresh instance. @default false @experimental */ concurrent?: boolean; kittyKeyboard?: KittyKeyboardOptions; /** Override automatic interactive mode detection. By default, Ink detects whether the environment is interactive based on CI detection (via [`is-in-ci`](https://github.com/sindresorhus/is-in-ci)) and `stdout.isTTY`. Most users should not need to set this. When non-interactive, Ink disables ANSI erase sequences, cursor manipulation, synchronized output, resize handling, and kitty keyboard auto-detection, writing only the final frame at unmount. Set to `false` to force non-interactive mode or `true` to force interactive mode when the automatic detection doesn't suit your use case. Note: Reusing the same stdout across multiple `render()` calls without unmounting is unsupported. Call `unmount()` first if you need to change this option or create a fresh instance. @default true (false if in CI or `stdout.isTTY` is falsy) @see {@link RenderOptions.interactive} */ interactive?: boolean; /** Render the app in the terminal's alternate screen buffer. When enabled, the app renders on a separate screen, and the original terminal content is restored when the app exits. This is the same mechanism used by programs like vim, htop, and less. Note: The terminal's scrollback buffer is not available while in the alternate screen. This is standard terminal behavior; programs like vim use the alternate screen specifically to avoid polluting the user's scrollback history. Note: Ink intentionally treats alternate-screen teardown output as disposable. It does not preserve or replay teardown-time frames, hook writes, or `console.*` output after restoring the primary screen. Only works in interactive mode. Ignored when `interactive` is `false` or in a non-interactive environment (CI, piped stdout). Note: Reusing the same stdout across multiple `render()` calls without unmounting is unsupported. Call `unmount()` first if you need to change this option or create a fresh instance. @default false @see {@link RenderOptions.alternateScreen} */ alternateScreen?: boolean; }; export default class Ink { /** Whether this instance is using concurrent rendering mode. */ readonly isConcurrent: boolean; private readonly options; private readonly log; private cursorPosition; private readonly throttledLog; private readonly isScreenReaderEnabled; private readonly interactive; private readonly renderThrottleMs; private alternateScreen; private isUnmounted; private isUnmounting; private lastOutput; private lastOutputToRender; private lastOutputHeight; private lastTerminalWidth; private readonly container; private readonly rootNode; private fullStaticOutput; private readonly exitPromise; private exitResult; private beforeExitHandler?; private restoreConsole?; private readonly unsubscribeResize?; private readonly throttledOnRender?; private hasPendingThrottledRender; private kittyProtocolEnabled; private cancelKittyDetection?; private nextRenderCommit?; constructor(options: Options); resized: () => void; resolveExitPromise: (result?: unknown) => void; rejectExitPromise: (reason?: Error) => void; unsubscribeExit: () => void; handleAppExit: (errorOrResult?: unknown) => void; setCursorPosition: (position: CursorPosition | undefined) => void; restoreLastOutput: () => void; calculateLayout: () => void; onRender: () => void; render(node: ReactNode): void; writeToStdout(data: string): void; writeToStderr(data: string): void; unmount(error?: Error | number | null): void; waitUntilExit(): Promise<unknown>; waitUntilRenderFlush(): Promise<void>; clear(): void; patchConsole(): void; private setAlternateScreen; private resolveInteractiveOption; private resolveAlternateScreenOption; private shouldSync; private writeBestEffort; private awaitExit; private hasPendingConcurrentWork; private awaitNextRender; private renderInteractiveFrame; private initKittyKeyboard; private confirmKittySupport; private enableKittyProtocol; }