import { Renderable, RootRenderable } from "./Renderable.js"; import { DebugOverlayCorner, type CursorStyleOptions, type MousePointerStyle, type RenderContext, type TerminalCapabilities, type ThemeMode, type WidthMethod } from "./types.js"; import { RGBA, type ColorInput } from "./lib/RGBA.js"; import { OptimizedBuffer } from "./buffer.js"; import { type NativeBufferedOutput, type NativeRenderStats, type RendererHandle } from "./zig.js"; import { TerminalConsole, type ConsoleOptions } from "./console.js"; import { type MouseEventType, type RawMouseEvent, type ScrollInfo } from "./lib/parse.mouse.js"; import { Selection } from "./lib/selection.js"; import { type ClipboardTarget } from "./lib/clipboard.js"; import { EventEmitter } from "events"; import { KeyHandler, InternalKeyHandler } from "./lib/KeyHandler.js"; import { type EditBufferRenderable } from "./renderables/EditBufferRenderable.js"; import { type TerminalColors, type GetPaletteOptions } from "./lib/terminal-palette.js"; import { type Clock } from "./lib/clock.js"; export interface CliRendererConfig { stdin?: NodeJS.ReadStream; stdout?: NodeJS.WriteStream; width?: number; height?: number; remote?: boolean; bufferedOutput?: NativeBufferedOutput; exitOnCtrlC?: boolean; exitSignals?: NodeJS.Signals[]; clearOnShutdown?: boolean; forwardEnvKeys?: string[]; debounceDelay?: number; targetFps?: number; maxFps?: number; memorySnapshotInterval?: number; useThread?: boolean; gatherStats?: boolean; maxStatSamples?: number; consoleOptions?: Omit; postProcessFns?: ((buffer: OptimizedBuffer, deltaTime: number) => void)[]; enableMouseMovement?: boolean; useMouse?: boolean; autoFocus?: boolean; screenMode?: ScreenMode; footerHeight?: number; externalOutputMode?: ExternalOutputMode; consoleMode?: ConsoleMode; useKittyKeyboard?: KittyKeyboardOptions | null; backgroundColor?: ColorInput; openConsoleOnError?: boolean; prependInputHandlers?: ((sequence: string) => boolean)[]; stdinParserMaxBufferBytes?: number; clock?: Clock; onDestroy?: () => void; } export type ScreenMode = "alternate-screen" | "main-screen" | "split-footer"; export type ExternalOutputMode = "capture-stdout" | "passthrough"; export interface CliRendererExternalOutputEvent { snapshot: OptimizedBuffer; rowColumns: number; startOnNewLine: boolean; trailingNewline: boolean; } export type ConsoleMode = "console-overlay" | "disabled"; export type PixelResolution = { width: number; height: number; }; export interface CliRendererStats extends NativeRenderStats { fps: number; frameCount: number; frameTimes: number[]; averageFrameTime: number; minFrameTime: number; maxFrameTime: number; frameCallbackTime: number; } export interface CliRendererFrameEvent { frameId: number; } export interface RendererSchedulerState { isRunning: boolean; isRendering: boolean; hasScheduledRender: boolean; } export interface ScrollbackRenderContext { width: number; widthMethod: WidthMethod; tailColumn: number; renderContext: RenderContext; } export interface ScrollbackSnapshot { root: Renderable; width?: number; height?: number; rowColumns?: number; startOnNewLine?: boolean; trailingNewline?: boolean; teardown?: () => void; } export type ScrollbackWriter = (ctx: ScrollbackRenderContext) => ScrollbackSnapshot; export interface ScrollbackSurfaceOptions { startOnNewLine?: boolean; } export interface ScrollbackSurfaceCommitOptions { rowColumns?: number; trailingNewline?: boolean; } export interface ScrollbackSurface { readonly renderContext: RenderContext; readonly root: Renderable; readonly width: number; readonly height: number; readonly isDestroyed: boolean; render(): void; settle(timeoutMs?: number): Promise; commitRows(startRow: number, endRowExclusive: number, options?: ScrollbackSurfaceCommitOptions): void; destroy(): void; } export interface SplitFooterReplayResetOptions { clearSavedLines?: boolean; } /** * 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 } /** * Create a CLI renderer and run its async terminal setup. The constructor * owns all stream and backend decisions; this factory only layers on the * `--delay-start` flag and the `await setupTerminal()` convenience. */ export declare function createCliRenderer(config?: CliRendererConfig): Promise; export declare enum CliRenderEvents { RESIZE = "resize", FRAME = "frame", EXTERNAL_OUTPUT = "external_output", FOCUS = "focus", BLUR = "blur", FOCUSED_RENDERABLE = "focused_renderable", FOCUSED_EDITOR = "focused_editor", THEME_MODE = "theme_mode", PALETTE = "palette", CAPABILITIES = "capabilities", SELECTION = "selection", DEBUG_OVERLAY_TOGGLE = "debugOverlay:toggle", DESTROY = "destroy", MEMORY_SNAPSHOT = "memory:snapshot" } 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: RendererHandle; stdin: NodeJS.ReadStream; private stdout; private exitOnCtrlC; private exitSignals; private _exitListenersAdded; private _isDestroyed; private _destroyPending; private _destroyFinalized; private _destroyCleanupPrepared; private _streamLeaseAcquired; 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 readonly clock; private rendering; private renderingNative; private renderTimeout; private lastTime; private frameCount; private _frameId; 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 stdinParser; private readonly oscSubscribers; private hasLoggedStdinParserError; private animationRequest; private resizeTimeoutId; private capabilityTimeoutId; private xtVersionWaiters; private splitStartupSeedTimeoutId; private pendingSplitStartupCursorSeed; private resizeDebounceDelay; private enableMouseMovement; private _useMouse; private autoFocus; private _screenMode; private _footerHeight; private _externalOutputMode; private clearOnShutdown; private _suspendedMouseEnabled; private _previousControlState; private pendingSuspendedTerminalSetup; private suspendedNonAltSurfacePreserved; private capturedRenderable?; private lastOverRenderableNum; private lastOverRenderable?; private currentSelection; private selectionContainers; private clipboard; private _splitHeight; private renderOffset; private splitTailColumn; private pendingSplitFooterTransition; private forceFullRepaintRequested; private readonly maxSplitCommitsPerFrame; private _terminalWidth; private _terminalHeight; private _terminalIsSetup; private externalOutputQueue; private pendingExternalOutputMode; private realStdoutWrite; private _useConsole; private sigwinchHandler; private _capabilities; private _latestPointer; private _hasPointer; private _lastPointerModifiers; private _currentMousePointerStyle; private _currentFocusedRenderable; private lifecyclePasses; private _openConsoleOnError; private _paletteDetector; private _paletteCache; private _paletteDetectionPromise; private _paletteDetectionSize; private _paletteEpoch; private _nativePaletteSignature; private _emittedPaletteSignature; private _palettePublishGeneration; private _onDestroy?; private themeModeState; private _terminalFocusState; private sequenceHandlers; private prependedInputHandlers; private shouldRestoreModesOnNextFocus; private themeModeHandler; private idleResolvers; private _debugInputs; private _debugModeEnabled; private handleError; private dumpOutputCache; private exitHandler; private warningHandler; private readonly _usesProcessStdout; private _feed; private _detachFeed; private _detachFeedError; private feedIdleRenderScheduled; private ordinaryFrameWaitingForFeed; private ordinaryFrameWaitControlState; get controlState(): RendererControlState; /** * Construct a renderer over the given streams. * * If `stdout` is not `process.stdout`, a `NativeSpanFeed` is allocated * internally and rendered bytes are piped through it to `stdout` unless * `bufferedOutput: "memory"` is set. Prefer `createCliRenderer` for the async * `setupTerminal` convenience. * * Construction side effects (observable before the constructor returns): * - Acquires exclusive ownership of the given stdin/stdout streams * - Allocates a `NativeSpanFeed` (for non-process stdout unless bufferedOutput is "memory") * - Calls `lib.createRenderer` → native Zig allocation * - Registers in the process-wide `rendererTracker` * - Adds `process.on(...)` listeners for SIGWINCH (process.stdout only), * "warning", "uncaughtException", "unhandledRejection", "beforeExit", * plus the configured `exitSignals` * - Replaces `global.requestAnimationFrame` with the renderer's impl * - When `setupTerminal()` is called, it will put `stdin` in raw mode and * call `stdin.resume()` * * Some late constructor side effects are not rolled back if construction * throws partway; production callers should use `createCliRenderer`, which * wraps `setupTerminal()` in a try/catch that calls `destroy()` on failure. */ constructor(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; get currentFocusedRenderable(): Renderable | null; get currentFocusedEditor(): EditBufferRenderable | null; private normalizeClockTime; private getElapsedMs; focusRenderable(renderable: Renderable): void; blurRenderable(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; get frameId(): number; private writeOut; private scheduleRenderAfterFeedIdle; private handleNativeRenderRejection; private reportNativeRenderFailure; private scheduleRenderTimer; private scheduleRenderAfterBackpressure; requestRender(): void; private activateFrame; get consoleMode(): ConsoleMode; set consoleMode(mode: ConsoleMode); get isRunning(): boolean; private isIdleNow; private resolveIdleIfNeeded; idle(): Promise; getSchedulerState(): RendererSchedulerState; get resolution(): PixelResolution | null; get console(): TerminalConsole; get keyInput(): KeyHandler; get _internalKeyInput(): InternalKeyHandler; get terminalWidth(): number; get terminalHeight(): number; get useThread(): boolean; get targetFps(): number; set targetFps(targetFps: number); get maxFps(): number; set maxFps(maxFps: number); get useMouse(): boolean; set useMouse(useMouse: boolean); get screenMode(): ScreenMode; set screenMode(mode: ScreenMode); get footerHeight(): number; set footerHeight(footerHeight: number); get externalOutputMode(): ExternalOutputMode; set externalOutputMode(mode: ExternalOutputMode); private applyExternalOutputMode; private afterExternalOutputModeChanged; private applyPendingExternalOutputModeIfReady; private flushPendingSplitOutputBeforeLeavingSplitFooter; get liveRequestCount(): number; get currentControlState(): string; get capabilities(): TerminalCapabilities | null; triggerNotification(message: string, title?: string): boolean; get themeMode(): ThemeMode | null; waitForThemeMode(timeoutMs?: number): Promise; getDebugInputs(): Array<{ timestamp: string; sequence: string; }>; get useKittyKeyboard(): boolean; set useKittyKeyboard(use: boolean); createScrollbackSurface(options?: ScrollbackSurfaceOptions): ScrollbackSurface; writeToScrollback(write: ScrollbackWriter): void; resetSplitFooterForReplay(options?: SplitFooterReplayResetOptions): void; private getSnapshotWidth; private getSnapshotHeight; private getSnapshotRowWidths; private advanceSplitTailColumn; private getSplitTailColumnAfterCommit; private recordSplitCommit; private getPendingSplitTailColumn; private enqueueRenderedScrollbackCommit; private enqueueSplitCommit; private createStdoutSnapshotCommit; private splitStdoutRows; private createStdoutSnapshotCommits; private flushPendingSplitCommits; private interceptStdoutWrite; private getSplitPinnedRenderOffset; private getSplitCursorSeedRows; private isSplitCursorSeedFrameBlocked; private canFlushSplitOutputBeforeTransition; private clearSplitStartupCursorSeed; private abortSplitStartupCursorSeed; private flushPendingSplitOutputBeforeTransition; private resetSplitScrollback; private syncSplitScrollback; private getSplitOutputOffset; private clearPendingSplitFooterTransition; private setPendingSplitFooterTransition; private syncSplitFooterState; private clearStaleSplitSurfaceRows; private applyScreenMode; private flushStdoutCache; private enableMouse; private disableMouse; enableKittyKeyboard(flags?: number): void; disableKittyKeyboard(): void; set useThread(useThread: boolean); setupTerminal(): Promise; private stdinListener; addInputHandler(handler: (sequence: string) => boolean): void; prependInputHandler(handler: (sequence: string) => boolean): void; removeInputHandler(handler: (sequence: string) => boolean): void; private updateStdinParserProtocolContext; subscribeOsc(handler: (sequence: string) => void): () => void; private processCapabilitySequence; private capabilityHandler; private focusHandler; private dispatchSequenceHandlers; private drainStdinParser; private handleStdinEvent; private handleStdinParserFailure; private setupInput; private dispatchMouseEvent; private processSingleMouseEvent; /** * 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; setMousePointer(style: MousePointerStyle): void; hitTest(x: number, y: number): number; private takeMemorySnapshot; private startMemorySnapshotTimer; private stopMemorySnapshotTimer; setMemorySnapshotInterval(interval: number): void; private handleResize; private queryPixelResolution; private processResize; /** * Programmatically resize the renderer to new dimensions. * * Use this for externally-driven resize events — for example, an SSH * `window-change` signal or a test harness simulating a terminal resize. * When the renderer is attached to `process.stdout`, `SIGWINCH` is handled * automatically and callers do not need this method. */ resize(width: number, height: number): void; setBackgroundColor(color: ColorInput): void; toggleDebugOverlay(): void; configureDebugOverlay(options: { enabled?: boolean; corner?: DebugOverlayCorner; }): void; setTerminalTitle(title: string): void; /** * Reset the terminal background color to its default via OSC 111. * Called automatically by destroy() and suspend(), but exposed for * consumers that need explicit control (e.g. before SIGTSTP). */ resetTerminalBgColor(): void; copyToClipboardOSC52(text: string, target?: ClipboardTarget): boolean; clearClipboardOSC52(target?: ClipboardTarget): boolean; isOsc52Supported(): boolean; dumpHitGrid(): void; dumpBuffers(timestamp?: number): void; dumpOutputBuffer(timestamp?: number): void; static setCursorPosition(renderer: CliRenderer, x: number, y: number, visible?: boolean): void; static setCursorStyle(renderer: CliRenderer, options: CursorStyleOptions): void; static setCursorColor(renderer: CliRenderer, color: RGBA): void; setCursorPosition(x: number, y: number, visible?: boolean): void; setCursorStyle(options: CursorStyleOptions): void; setCursorColor(color: RGBA): void; getCursorState(): import("./zig.js").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; removeFrameCallback(callback: (deltaTime: number) => Promise): 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 cleanupBeforeDestroy; private prepareDestroyDuringRender; private finalizeDestroy; private startRenderLoop; private loop; intermediateRender(): void; private renderNative; private collectStatSample; getNativeStats(): NativeRenderStats; getStats(): CliRendererStats; 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"; private getCachedPaletteBySize; private ensurePaletteDetector; private syncNativePaletteState; private emitPaletteChange; private resolveXtVersionWaiters; private waitForXtVersion; private shouldSyncNativePaletteState; private refreshPalette; 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; }