@opentui/core
Version:
OpenTUI is a TypeScript library for building terminal user interfaces (TUIs)
371 lines (370 loc) • 13.3 kB
TypeScript
import { Renderable, RootRenderable } from "./Renderable";
import { type CursorStyle, DebugOverlayCorner, type RenderContext, type WidthMethod } from "./types";
import { RGBA, type ColorInput } from "./lib/RGBA";
import type { Pointer } from "bun:ffi";
import { OptimizedBuffer } from "./buffer";
import { type RenderLib } from "./zig";
import { TerminalConsole, type ConsoleOptions } from "./console";
import { type MouseEventType, type RawMouseEvent, type ScrollInfo } from "./lib/parse.mouse";
import { Selection } from "./lib/selection";
import { type ClipboardTarget } from "./lib/clipboard";
import { EventEmitter } from "events";
import { KeyHandler, InternalKeyHandler } from "./lib/KeyHandler";
import { type TerminalColors, type GetPaletteOptions } from "./lib/terminal-palette";
export interface CliRendererConfig {
stdin?: NodeJS.ReadStream;
stdout?: NodeJS.WriteStream;
remote?: boolean;
exitOnCtrlC?: boolean;
exitSignals?: NodeJS.Signals[];
debounceDelay?: number;
targetFps?: number;
maxFps?: number;
memorySnapshotInterval?: number;
useThread?: boolean;
gatherStats?: boolean;
maxStatSamples?: number;
consoleOptions?: ConsoleOptions;
postProcessFns?: ((buffer: OptimizedBuffer, deltaTime: number) => void)[];
enableMouseMovement?: boolean;
useMouse?: boolean;
autoFocus?: boolean;
useAlternateScreen?: boolean;
useConsole?: boolean;
experimental_splitHeight?: number;
useKittyKeyboard?: KittyKeyboardOptions | null;
backgroundColor?: ColorInput;
openConsoleOnError?: boolean;
prependInputHandlers?: ((sequence: string) => boolean)[];
onDestroy?: () => void;
}
export type PixelResolution = {
width: number;
height: number;
};
/**
* Kitty Keyboard Protocol configuration options
* See: https://sw.kovidgoyal.net/kitty/keyboard-protocol/#progressive-enhancement
*/
export interface KittyKeyboardOptions {
/** Disambiguate escape codes (fixes ESC timing, alt+key ambiguity, ctrl+c as event). Default: true */
disambiguate?: boolean;
/** Report alternate keys (numpad, shifted, base layout) for cross-keyboard shortcuts. Default: true */
alternateKeys?: boolean;
/** Report event types (press/repeat/release). Default: false */
events?: boolean;
/** Report all keys as escape codes. Default: false */
allKeysAsEscapes?: boolean;
/** Report text associated with key events. Default: false */
reportText?: boolean;
}
/**
* Build kitty keyboard protocol flags based on configuration
* @param config Kitty keyboard configuration object (null/undefined = disabled)
* @returns The combined flags value (0 = disabled, >0 = enabled)
* @internal Exported for testing
*/
export declare function buildKittyKeyboardFlags(config: KittyKeyboardOptions | null | undefined): number;
export declare class MouseEvent {
readonly type: MouseEventType;
readonly button: number;
readonly x: number;
readonly y: number;
readonly source?: Renderable;
readonly modifiers: {
shift: boolean;
alt: boolean;
ctrl: boolean;
};
readonly scroll?: ScrollInfo;
readonly target: Renderable | null;
readonly isDragging?: boolean;
private _propagationStopped;
private _defaultPrevented;
get propagationStopped(): boolean;
get defaultPrevented(): boolean;
constructor(target: Renderable | null, attributes: RawMouseEvent & {
source?: Renderable;
isDragging?: boolean;
});
stopPropagation(): void;
preventDefault(): void;
}
export declare enum MouseButton {
LEFT = 0,
MIDDLE = 1,
RIGHT = 2,
WHEEL_UP = 4,
WHEEL_DOWN = 5
}
export declare function createCliRenderer(config?: CliRendererConfig): Promise<CliRenderer>;
export declare enum CliRenderEvents {
DEBUG_OVERLAY_TOGGLE = "debugOverlay:toggle",
DESTROY = "destroy"
}
export declare enum RendererControlState {
IDLE = "idle",
AUTO_STARTED = "auto_started",
EXPLICIT_STARTED = "explicit_started",
EXPLICIT_PAUSED = "explicit_paused",
EXPLICIT_SUSPENDED = "explicit_suspended",
EXPLICIT_STOPPED = "explicit_stopped"
}
export declare class CliRenderer extends EventEmitter implements RenderContext {
private static animationFrameId;
private lib;
rendererPtr: Pointer;
stdin: NodeJS.ReadStream;
private stdout;
private exitOnCtrlC;
private exitSignals;
private _exitListenersAdded;
private _isDestroyed;
private _destroyPending;
private _destroyFinalized;
nextRenderBuffer: OptimizedBuffer;
currentRenderBuffer: OptimizedBuffer;
private _isRunning;
private targetFps;
private maxFps;
private automaticMemorySnapshot;
private memorySnapshotInterval;
private memorySnapshotTimer;
private lastMemorySnapshot;
readonly root: RootRenderable;
width: number;
height: number;
private _useThread;
private gatherStats;
private frameTimes;
private maxStatSamples;
private postProcessFns;
private backgroundColor;
private waitingForPixelResolution;
private rendering;
private renderingNative;
private renderTimeout;
private lastTime;
private frameCount;
private lastFpsTime;
private currentFps;
private targetFrameTime;
private minTargetFrameTime;
private immediateRerenderRequested;
private updateScheduled;
private liveRequestCounter;
private _controlState;
private frameCallbacks;
private renderStats;
debugOverlay: {
enabled: any;
corner: DebugOverlayCorner;
};
private _console;
private _resolution;
private _keyHandler;
private _stdinBuffer;
private animationRequest;
private resizeTimeoutId;
private capabilityTimeoutId;
private resizeDebounceDelay;
private enableMouseMovement;
private _useMouse;
private autoFocus;
private _useAlternateScreen;
private _suspendedMouseEnabled;
private _previousControlState;
private capturedRenderable?;
private lastOverRenderableNum;
private lastOverRenderable?;
private currentSelection;
private selectionContainers;
private clipboard;
private _splitHeight;
private renderOffset;
private _terminalWidth;
private _terminalHeight;
private _terminalIsSetup;
private realStdoutWrite;
private captureCallback;
private _useConsole;
private mouseParser;
private sigwinchHandler;
private _capabilities;
private _latestPointer;
private _hasPointer;
private _lastPointerModifiers;
private _currentFocusedRenderable;
private lifecyclePasses;
private _openConsoleOnError;
private _paletteDetector;
private _cachedPalette;
private _paletteDetectionPromise;
private _onDestroy?;
private inputHandlers;
private prependedInputHandlers;
private idleResolvers;
private _debugInputs;
private _debugModeEnabled;
private handleError;
private dumpOutputCache;
private exitHandler;
private warningHandler;
get controlState(): RendererControlState;
constructor(lib: RenderLib, rendererPtr: Pointer, stdin: NodeJS.ReadStream, stdout: NodeJS.WriteStream, width: number, height: number, config?: CliRendererConfig);
private addExitListeners;
private removeExitListeners;
get isDestroyed(): boolean;
registerLifecyclePass(renderable: Renderable): void;
unregisterLifecyclePass(renderable: Renderable): void;
getLifecyclePasses(): Set<Renderable>;
get currentFocusedRenderable(): Renderable | null;
focusRenderable(renderable: Renderable): void;
private setCapturedRenderable;
addToHitGrid(x: number, y: number, width: number, height: number, id: number): void;
pushHitGridScissorRect(x: number, y: number, width: number, height: number): void;
popHitGridScissorRect(): void;
clearHitGridScissorRects(): void;
get widthMethod(): WidthMethod;
private writeOut;
requestRender(): void;
private activateFrame;
get useConsole(): boolean;
set useConsole(value: boolean);
get isRunning(): boolean;
private isIdleNow;
private resolveIdleIfNeeded;
idle(): Promise<void>;
get resolution(): PixelResolution | null;
get console(): TerminalConsole;
get keyInput(): KeyHandler;
get _internalKeyInput(): InternalKeyHandler;
get terminalWidth(): number;
get terminalHeight(): number;
get useThread(): boolean;
get useMouse(): boolean;
set useMouse(useMouse: boolean);
get experimental_splitHeight(): number;
get liveRequestCount(): number;
get currentControlState(): string;
get capabilities(): any | null;
getDebugInputs(): Array<{
timestamp: string;
sequence: string;
}>;
get useKittyKeyboard(): boolean;
set useKittyKeyboard(use: boolean);
set experimental_splitHeight(splitHeight: number);
private interceptStdoutWrite;
disableStdoutInterception(): void;
private flushStdoutCache;
private enableMouse;
private disableMouse;
enableKittyKeyboard(flags?: number): void;
disableKittyKeyboard(): void;
set useThread(useThread: boolean);
setupTerminal(): Promise<void>;
private stdinListener;
addInputHandler(handler: (sequence: string) => boolean): void;
prependInputHandler(handler: (sequence: string) => boolean): void;
removeInputHandler(handler: (sequence: string) => boolean): void;
private capabilityHandler;
private focusHandler;
private setupInput;
private dispatchMouseEvent;
private handleMouseData;
/**
* Recheck hover state after hit grid changes.
* Called after render when native code detects the hit grid changed.
* Fires out/over events if the element under the cursor changed.
*/
private recheckHoverState;
hitTest(x: number, y: number): number;
private takeMemorySnapshot;
private startMemorySnapshotTimer;
private stopMemorySnapshotTimer;
setMemorySnapshotInterval(interval: number): void;
private handleResize;
private queryPixelResolution;
private processResize;
setBackgroundColor(color: ColorInput): void;
toggleDebugOverlay(): void;
configureDebugOverlay(options: {
enabled?: boolean;
corner?: DebugOverlayCorner;
}): void;
setTerminalTitle(title: string): void;
copyToClipboardOSC52(text: string, target?: ClipboardTarget): boolean;
clearClipboardOSC52(target?: ClipboardTarget): boolean;
isOsc52Supported(): boolean;
dumpHitGrid(): void;
dumpBuffers(timestamp?: number): void;
dumpStdoutBuffer(timestamp?: number): void;
static setCursorPosition(renderer: CliRenderer, x: number, y: number, visible?: boolean): void;
static setCursorStyle(renderer: CliRenderer, style: CursorStyle, blinking?: boolean, color?: RGBA): void;
static setCursorColor(renderer: CliRenderer, color: RGBA): void;
setCursorPosition(x: number, y: number, visible?: boolean): void;
setCursorStyle(style: CursorStyle, blinking?: boolean, color?: RGBA): void;
setCursorColor(color: RGBA): void;
getCursorState(): import("./zig").CursorState;
addPostProcessFn(processFn: (buffer: OptimizedBuffer, deltaTime: number) => void): void;
removePostProcessFn(processFn: (buffer: OptimizedBuffer, deltaTime: number) => void): void;
clearPostProcessFns(): void;
setFrameCallback(callback: (deltaTime: number) => Promise<void>): void;
removeFrameCallback(callback: (deltaTime: number) => Promise<void>): void;
clearFrameCallbacks(): void;
requestLive(): void;
dropLive(): void;
start(): void;
auto(): void;
private internalStart;
pause(): void;
suspend(): void;
resume(): void;
private internalPause;
stop(): void;
private internalStop;
destroy(): void;
private finalizeDestroy;
private startRenderLoop;
private loop;
intermediateRender(): void;
private renderNative;
private collectStatSample;
getStats(): {
fps: number;
frameCount: number;
frameTimes: number[];
averageFrameTime: number;
minFrameTime: number;
maxFrameTime: number;
};
resetStats(): void;
setGatherStats(enabled: boolean): void;
getSelection(): Selection | null;
get hasSelection(): boolean;
getSelectionContainer(): Renderable | null;
clearSelection(): void;
/**
* Start a new selection at the given coordinates.
* Used by both mouse and keyboard selection.
*/
startSelection(renderable: Renderable, x: number, y: number): void;
updateSelection(currentRenderable: Renderable | undefined, x: number, y: number, options?: {
finishDragging?: boolean;
}): void;
requestSelectionUpdate(): void;
private isWithinContainer;
private finishSelection;
private notifySelectablesOfSelectionChange;
private walkSelectableRenderables;
get paletteDetectionStatus(): "idle" | "detecting" | "cached";
clearPaletteCache(): void;
/**
* Detects the terminal's color palette
*
* @returns Promise resolving to TerminalColors object containing palette and special colors
* @throws Error if renderer is suspended
*/
getPalette(options?: GetPaletteOptions): Promise<TerminalColors>;
}