UNPKG

@unblessed/core

Version:

Platform-agnostic terminal UI core library with runtime dependency injection

1,720 lines (1,705 loc) 183 kB
import { Buffer } from 'buffer'; import * as child_process from 'child_process'; import EventEmitter$1 from 'events'; import * as fs from 'fs'; import * as net from 'net'; import * as path from 'path'; import { Readable, Writable } from 'stream'; import { StringDecoder } from 'string_decoder'; import * as tty from 'tty'; import * as url from 'url'; import * as util from 'util'; /** * Runtime abstraction layer for @unblessed/core * * @remarks * The Runtime interface provides platform-specific APIs through dependency injection. * This allows unblessed to work across different platforms (Node.js, browsers, Deno, etc.) * by abstracting platform-specific operations behind a common interface. * * **Key Concepts**: * - **Runtime Interface**: Defines what platforms must provide * - **Runtime Context**: Global singleton holding the current runtime * - **Platform Implementations**: Node.js runtime, browser runtime, etc. * - **Dependency Injection**: Core code uses runtime instead of direct imports * * **Platform adapters**: * - `@unblessed/node`: Wraps real Node.js APIs (fs, path, process, child_process, tty, etc.) * - `@unblessed/browser`: Provides browser polyfills and virtual filesystem * - Tests: Uses real Node.js APIs for testing * * @example Using runtime in code * ```typescript * import { getRuntime } from '@unblessed/core/runtime-context'; * * function loadTerminfo(term: string): Buffer { * const runtime = getRuntime(); * * // Use runtime.fs instead of import fs * const path = runtime.path.join('/usr/share/terminfo', term[0], term); * * if (!runtime.fs.existsSync(path)) { * throw new Error(`Terminfo not found: ${term}`); * } * * return runtime.fs.readFileSync(path); * } * ``` * * @example Feature detection for optional APIs * ```typescript * import { getRuntime, hasImageSupport } from '@unblessed/core'; * * function canRenderImages(): boolean { * const runtime = getRuntime(); * return runtime.images !== undefined; * } * * function parseImage(buffer: Buffer) { * const runtime = getRuntime(); * * if (!runtime.images) { * throw new Error('Image support not available'); * } * * const png = runtime.images.png.PNG.sync.read(buffer); * return png; * } * ``` * * @example Type-safe feature detection * ```typescript * const runtime = getRuntime(); * * if (hasImageSupport(runtime)) { * // TypeScript knows runtime.images exists here! * const png = runtime.images.png.PNG.sync.read(buffer); * } * ``` * * @example Platform detection * ```typescript * import { getRuntime } from '@unblessed/core/runtime-context'; * * const runtime = getRuntime(); * * if (runtime.process.platform === 'browser') { * console.log('Running in browser'); * } else { * console.log('Running in Node.js'); * } * ``` * * @see {@link getRuntime} for accessing the current runtime * @see {@link setRuntime} for platform initialization */ /** * Complete runtime abstraction interface * All @unblessed/core modules accept this interface for platform operations * * Core APIs (always required): * - fs, path, process, buffer, url, utils * * Optional APIs (use feature detection): * - images: PNG/GIF rendering (only needed by Image widgets) * - processes: Child process spawning (Terminal widget, image tools) * - networking: Network and TTY operations (GPM mouse - very rare) */ interface Runtime { /** File system operations */ fs: FileSystemAPI; /** Path manipulation operations */ path: PathAPI; /** Process operations (stdin/stdout/env/etc) */ process: ProcessAPI; /** Buffer operations */ buffer: BufferAPI; /** URL operations (fileURLToPath for module resolution) */ url: UrlAPI; /** Utility functions (inspect, format) */ util: UtilAPI; /** Stream operations (Readable, Writable) */ stream: StreamAPI; /** String decoder for buffer/string conversion */ stringDecoder: StringDecoderAPI; /** Event emitter for event-driven programming */ events: EventsAPI; /** Image processing (PNG/GIF rendering) - Optional */ images?: ImageAPI; /** Process spawning - Optional */ processes?: ProcessesAPI; /** Networking and TTY operations - Optional */ networking?: NetworkingAPI; } /** * File system operations interface * * @remarks * Subset of Node.js fs module needed by unblessed for: * - Reading terminfo/termcap files * - Loading font definitions * - Logging and debugging * - Temporary file operations * * @example Reading terminfo files * ```typescript * const runtime = getRuntime(); * const data = runtime.fs.readFileSync('/usr/share/terminfo/x/xterm'); * ``` * * @example Checking file existence * ```typescript * const runtime = getRuntime(); * if (runtime.fs.existsSync('/path/to/file')) { * const content = runtime.fs.readFileSync('/path/to/file', 'utf8'); * } * ``` */ interface FileSystemAPI { readFileSync: typeof fs.readFileSync; readdirSync: typeof fs.readdirSync; existsSync: typeof fs.existsSync; statSync: typeof fs.statSync; mkdirSync: typeof fs.mkdirSync; createWriteStream: typeof fs.createWriteStream; readFile: typeof fs.readFile; unlink: typeof fs.unlink; writeFile: typeof fs.writeFile; stat: typeof fs.stat; readdir: typeof fs.readdir; lstatSync: typeof fs.lstatSync; readlinkSync: typeof fs.readlinkSync; } /** * Path manipulation interface * Subset of Node.js path module */ interface PathAPI { join: typeof path.join; resolve: typeof path.resolve; dirname: typeof path.dirname; basename: typeof path.basename; normalize: typeof path.normalize; extname: typeof path.extname; sep: typeof path.sep; delimiter: typeof path.delimiter; } /** * Process operations interface * * @remarks * Subset of Node.js process global used for: * - I/O streams (stdin/stdout/stderr) * - Environment variables (TERM, EDITOR, HOME, etc.) * - Process events (exit, SIGTSTP, etc.) * - Platform detection (platform, arch) * * @example Accessing I/O streams * ```typescript * const runtime = getRuntime(); * const { Readable, Writable } = runtime.utils.stream; * * const readable = new Readable(); * readable.push('Hello\n'); * readable.push(null); * readable.pipe(runtime.process.stdout); * ``` * * @example Environment variables * ```typescript * const runtime = getRuntime(); * const term = runtime.process.env.TERM || 'xterm-256color'; * const editor = runtime.process.env.EDITOR || 'vi'; * const home = runtime.process.env.HOME || '/'; * ``` */ interface ProcessAPI { stdin: NodeJS.ReadStream & { fd: 0; }; stdout: NodeJS.WriteStream & { fd: 1; }; stderr: NodeJS.WriteStream & { fd: 2; }; platform: NodeJS.Platform; arch: NodeJS.Architecture; env: NodeJS.ProcessEnv; cwd: () => string; exit: (code?: number) => never; pid: number; title: string; version: string; on: (event: string, listener: (...args: any[]) => void) => any; once: (event: string, listener: (...args: any[]) => void) => any; removeListener: (event: string, listener: (...args: any[]) => void) => any; listeners: (event: any) => Function[]; nextTick: (callback: Function, ...args: any[]) => void; kill: (pid: number, signal?: string | number) => boolean; } /** * Child process operations interface * Subset of Node.js child_process module */ interface ChildProcessAPI { spawn: typeof child_process.spawn; execSync: typeof child_process.execSync; execFileSync: typeof child_process.execFileSync; } /** * TTY operations interface * Subset of Node.js tty module */ interface TtyAPI { isatty: typeof tty.isatty; } /** * URL operations interface * Subset of Node.js url module */ interface UrlAPI { parse: typeof url.parse; format: typeof url.format; fileURLToPath: typeof url.fileURLToPath; } /** * Utility functions interface * Subset of Node.js util module */ interface UtilAPI { inspect: typeof util.inspect; format: typeof util.format; } interface NetAPI { createConnection: typeof net.createConnection; } /** * String decoder interface * Subset of Node.js string_decoder module */ interface StringDecoderAPI { StringDecoder: typeof StringDecoder; } /** * Stream operations interface * Subset of Node.js stream module */ interface StreamAPI { Readable: typeof Readable; Writable: typeof Writable; } /** * Readable type alias for use throughout the codebase */ type ReadableType = InstanceType<StreamAPI["Readable"]>; /** * Writable type alias for use throughout the codebase */ type WritableType = InstanceType<StreamAPI["Writable"]>; interface EventsAPI { EventEmitter: typeof EventEmitter$1; } /** * EventEmitter type alias for use throughout the codebase */ interface EventEmitterType extends InstanceType<EventsAPI["EventEmitter"]> { } /** * Buffer operations interface * Subset of Node.js buffer module */ interface BufferAPI { Buffer: typeof Buffer; } /** * Buffer type alias for use throughout the codebase */ type BufferType = InstanceType<BufferAPI["Buffer"]>; /** * PNG image library interface (pngjs) */ interface PngAPI { PNG: { new (options?: any): { width: number; height: number; data: BufferType; gamma: number; parse(data: BufferType, callback?: (error: Error | null, data: any) => void): any; pack(): any; on(event: string, callback: (...args: any[]) => void): any; }; sync: { read(data: BufferType): { width: number; height: number; data: BufferType; gamma: number; }; }; }; } /** * GIF image library interface (omggif) */ interface GifAPI { GifReader: new (buffer: BufferType) => { width: number; height: number; numFrames(): number; loopCount(): number; frameInfo(frameNum: number): { x: number; y: number; width: number; height: number; has_local_palette: boolean; palette_offset: number | null; palette_size: number | null; data_offset: number; data_length: number; transparent_index: number | null; interlaced: boolean; delay: number; disposal: number; }; decodeAndBlitFrameRGBA(frameNum: number, pixels: Uint8Array): void; }; } /** * Image processing API group (optional) * Combines PNG and GIF libraries for image rendering */ interface ImageAPI { /** PNG image library (pngjs) */ png: PngAPI; /** GIF image library (omggif) */ gif: GifAPI; } /** * Process spawning API group (optional) * Child process operations for specialized widgets */ interface ProcessesAPI { /** Child process spawning */ childProcess: ChildProcessAPI; } /** * Networking API group (optional) * Network connections and TTY operations */ interface NetworkingAPI { /** Network socket operations */ net: NetAPI; /** TTY operations */ tty: TtyAPI; } /** * Check if runtime has image processing support * * @remarks * Type guard to check if the current runtime supports PNG/GIF rendering. * Use this before accessing `runtime.images` to avoid runtime errors. * * @param runtime - Runtime instance to check * @returns True if image support is available * * @example Type-safe feature detection * ```typescript * const runtime = getRuntime(); * * if (hasImageSupport(runtime)) { * // TypeScript knows runtime.images exists here! * const png = runtime.images.png.PNG.sync.read(buffer); * } * ``` */ declare function hasImageSupport(runtime: Runtime): runtime is Runtime & { images: ImageAPI; }; /** * Check if runtime has process spawning support * * @remarks * Type guard to check if the current runtime can spawn child processes. * Needed for Terminal widget, text editors (vi, nano), and image tools. * * @param runtime - Runtime instance to check * @returns True if process support is available * * @example Conditional feature usage * ```typescript * const runtime = getRuntime(); * * if (hasProcessSupport(runtime)) { * // TypeScript knows runtime.processes exists * const proc = runtime.processes.childProcess.spawn('vi', ['file.txt']); * } else { * console.warn('Cannot spawn processes in this environment'); * } * ``` */ declare function hasProcessSupport(runtime: Runtime): runtime is Runtime & { processes: ProcessesAPI; }; /** * Check if runtime has networking support * * @remarks * Type guard for network and TTY operations. Currently only used for * GPM mouse protocol on Linux console (very rare). * * @param runtime - Runtime instance to check * @returns True if networking support is available */ declare function hasNetworkSupport(runtime: Runtime): runtime is Runtime & { networking: NetworkingAPI; }; /** * runtime-context.ts - Global runtime context for @unblessed/core * * Provides global access to the platform Runtime. * Runtime is stateless (just platform APIs), so safe to share globally. * * One Runtime per process: * - Node.js process → NodeRuntime (all screens share) * - Browser tab → BrowserRuntime (all screens share) */ /** * Set the global runtime instance * * Called by platform packages during initialization to register their Runtime implementation. * Should be called once before creating any widgets. * * @internal - Platform packages handle initialization automatically. * Users typically don't need to call this directly. * * Example (platform package): * import { setRuntime } from '@unblessed/core'; * const runtime = new NodeRuntime(); * setRuntime(runtime); * * @param rt - The Runtime implementation to use globally */ declare function setRuntime(rt: Runtime): void; /** * Border style definitions. * Character sets for different border styles (single, double, round, etc.). */ interface BorderCharacters { /** Top-left corner character */ topLeft: string; /** Top border character */ top: string; /** Top-right corner character */ topRight: string; /** Right border character */ right: string; /** Bottom-right corner character */ bottomRight: string; /** Bottom border character */ bottom: string; /** Bottom-left corner character */ bottomLeft: string; /** Left border character */ left: string; } /** * Available border style names. */ type BorderStyleName = "single" | "double" | "round" | "bold" | "singleDouble" | "doubleSingle" | "classic" | "arrow"; /** * Predefined border styles. * * Includes common Unicode box-drawing characters and ASCII fallback. */ declare const borderStyles: Record<BorderStyleName, BorderCharacters>; /** * Get border characters for a given style name. * * @param style - The border style name or custom character set * @returns Border characters for the specified style * @throws {Error} If style name is invalid * * @example * ```typescript * const chars = getBorderChars('double'); * // => { topLeft: '╔', top: '═', ... } * * // Custom characters * const custom = getBorderChars({ * topLeft: '*', top: '-', topRight: '*', * right: '|', bottomRight: '*', bottom: '-', * bottomLeft: '*', left: '|' * }); * ``` */ declare function getBorderChars(style: BorderStyleName | BorderCharacters): BorderCharacters; /** * Check if a border style name is valid. * * @param style - The style name to check * @returns True if the style exists */ declare function isValidBorderStyle(style: string): style is BorderStyleName; /** * Common type definitions for blessed */ /** * Position value for element layout. * * Supports flexible positioning with multiple formats: * - **Absolute cells**: `10`, `20` - Fixed position in terminal cells * - **Percentage**: `"50%"`, `"100%"` - Relative to parent size * - **Offset percentage**: `"50%+2"`, `"50%-5"` - Percentage with cell offset * - **Center**: `"center"` - Center alignment (for top/left) * - **Size keywords**: `"half"`, `"shrink"` - Special sizing (for width/height) * * @example * ```typescript * const box = new Box({ * top: 10, // 10 cells from top * left: "50%", // Middle of parent * width: "half", // Half of parent width * height: "50%-5" // Middle minus 5 cells * }); * ``` */ type PositionValue = number | string; /** * Text alignment options. */ type Alignment = "left" | "center" | "right"; /** * Mouse action types supported by blessed. */ type MouseAction = "mousedown" | "mouseup" | "mousemove" | "mousewheel" | "wheeldown" | "wheelup"; /** * Padding specification for elements. * Amount of padding on the inside of the element. */ interface Padding { /** Left padding in cells (defaults to 0) */ left: number; /** Right padding in cells (defaults to 0) */ right: number; /** Top padding in cells (defaults to 0) */ top: number; /** Bottom padding in cells (defaults to 0) */ bottom: number; } /** * Position specification for elements. * * All position and size properties accept flexible values: * - **Numbers**: Absolute cell positions/sizes * - **Percentages**: Relative to parent (`"50%"`) * - **Offset percentages**: Relative with adjustment (`"50%+2"`) * - **Keywords**: Special values like `"center"`, `"half"`, `"shrink"` * * @example * ```typescript * const box = new Box({ * top: "center", // Vertically centered * left: "50%-10", // Horizontally centered, shifted left 10 cells * width: "half", // 50% of parent width * height: 20 // Fixed 20 cells high * }); * ``` */ interface Position { /** Left offset. Can be number, percentage, or "center" */ left?: PositionValue; /** Right offset. Can be number or percentage */ right?: PositionValue; /** Top offset. Can be number, percentage, or "center" */ top?: PositionValue; /** Bottom offset. Can be number or percentage */ bottom?: PositionValue; /** Width of element. Can be number, percentage, or keywords like "half" or "shrink" */ width?: PositionValue; /** Height of element. Can be number, percentage, or keywords like "half" or "shrink" */ height?: PositionValue; } /** * Internal position coordinates in cells. * These are the calculated absolute positions after rendering. */ interface PositionCoords { /** Internal x coordinate (left edge) */ xi: number; /** x limit (right edge) */ xl: number; /** Internal y coordinate (top edge) */ yi: number; /** y limit (bottom edge) */ yl: number; } /** * Extended coordinate information including content positioning. */ interface Coords { /** x limit (right edge) */ xl: number; /** Internal x coordinate (left edge) */ xi: number; /** y limit (bottom edge) */ yl: number; /** Internal y coordinate (top edge) */ yi: number; /** Base line for scrolling */ base: number; /** Content end position */ _contentEnd: { x: number; y: number; }; /** Top offset without border/padding */ notop: PositionValue; /** Left offset without border/padding */ noleft: PositionValue; /** Right offset without border/padding */ noright: PositionValue; /** Bottom offset without border/padding */ nobot: PositionValue; } /** * Render coordinates returned by _getCoords. * Contains position information and clipping flags. */ interface RenderCoords { /** Internal x coordinate (left edge) */ xi: number; /** x limit (right edge) */ xl: number; /** Internal y coordinate (top edge) */ yi: number; /** y limit (bottom edge) */ yl: number; /** Base line for scrolling */ base: number; /** Whether left side is clipped */ noleft: boolean; /** Whether right side is clipped */ noright: boolean; /** Whether top side is clipped */ notop: boolean; /** Whether bottom side is clipped */ nobot: boolean; /** Render counter */ renders: number; /** Absolute left position */ aleft?: number; /** Absolute top position */ atop?: number; /** Absolute right position */ aright?: number; /** Absolute bottom position */ abottom?: number; /** Width of the element */ width?: number; /** Height of the element */ height?: number; } /** * Border specification for elements. * Can be "line" (uses line characters) or "bg" (uses background character). */ interface Border { /** Type of border: "line" or "bg". bg by default. */ type?: "line" | "bg"; /** * Border style when type is "line". * Supports predefined styles ('single', 'double', 'round', 'bold', etc.) * or a custom BorderCharacters object. * @default 'single' */ style?: BorderStyleName | BorderCharacters; /** Character to use if bg type. Default is space. */ ch?: string; /** Border background color. Must be a number (-1 for default). */ bg?: number; /** Border foreground color. Must be a number (-1 for default). */ fg?: number; /** Bold attribute for border */ bold?: boolean; /** Underline attribute for border */ underline?: boolean; /** Whether to draw top border */ top?: boolean; /** Whether to draw bottom border */ bottom?: boolean; /** Whether to draw left border */ left?: boolean; /** Whether to draw right border */ right?: boolean; /** Top border color (overrides fg). Can be color name or number. */ topColor?: number | string; /** Bottom border color (overrides fg). Can be color name or number. */ bottomColor?: number | string; /** Left border color (overrides fg). Can be color name or number. */ leftColor?: number | string; /** Right border color (overrides fg). Can be color name or number. */ rightColor?: number | string; /** Dim all borders (50% opacity effect) */ dim?: boolean; /** Dim top border */ topDim?: boolean; /** Dim bottom border */ bottomDim?: boolean; /** Dim left border */ leftDim?: boolean; /** Dim right border */ rightDim?: boolean; /** * Controls which border colors corners inherit. * - 'horizontal': corners use top/bottom border colors (Ink's approach) * - 'vertical': corners use left/right border colors * @default 'vertical' */ cornerColorMode?: "horizontal" | "vertical"; /** * Array of colors for each border cell (addressable, like LED strips). * Enables per-cell color control for animations (rainbow waves, chase effects, etc.). * Index 0 starts at top-left corner, proceeds clockwise around perimeter. * Can contain color names ("cyan"), hex codes ("#00ff00"), or numeric codes (6). */ colors?: (string | number)[]; /** * Whether to repeat the colors array if shorter than border perimeter. * When false, uses fallback colors (topColor/leftColor/fg) after array ends. * @default true */ repeatColors?: boolean; } /** * Label options for element labels. */ interface LabelOptions { /** Label text content */ text: string; /** Side to place label on */ side: Alignment; } /** * Cursor configuration for elements with cursor support. * Have blessed draw a custom cursor and hide the terminal cursor (experimental). */ interface Cursor { /** Have blessed draw a custom cursor and hide the terminal cursor (experimental) */ artificial: boolean; /** Shape of the cursor: block, underline, or line */ shape: "block" | "underline" | "line"; /** Whether the cursor blinks */ blink: boolean; /** Color of the cursor. Accepts any valid color value (null is default) */ color: string; } /** * Image data structure for ANSIImage and OverlayImage widgets. */ interface ImageData { /** Pixel width of the image */ width: number; /** Pixel height of the image */ height: number; /** Image bitmap data */ bmp: any; /** Image cellmap (bitmap scaled down to cell size) */ cellmap: any; } /** * Event type definitions for blessed. * Based on patterns from @types/blessed community types. */ /** * Mouse event argument passed to mouse event handlers. * Contains information about the mouse action and position. */ interface MouseEvent { /** X coordinate of the mouse event (column) */ x: number; /** Y coordinate of the mouse event (row) */ y: number; /** Type of mouse action that occurred */ action: MouseAction; /** Whether the Shift modifier key was held (optional) */ shift?: boolean; /** Whether the Control modifier key was held (optional) */ ctrl?: boolean; /** Whether the Meta/Alt modifier key was held (optional) */ meta?: boolean; /** The mouse button that was pressed (optional) */ button?: "left" | "middle" | "right"; } /** * Key event argument passed to keypress event handlers. * Contains information about the key that was pressed including modifiers. */ interface KeyEvent { /** Full key sequence (e.g., "C-c", "escape", "a") */ full: string; /** Name of the key (e.g., "c", "escape", "return") */ name: string; /** Whether the Shift modifier key was held */ shift: boolean; /** Whether the Control modifier key was held */ ctrl: boolean; /** Whether the Meta/Alt modifier key was held */ meta: boolean; /** Raw escape sequence received from the terminal */ sequence: string; /** The character pressed (if applicable) */ ch?: string; } /** * Node event types that can be emitted by Node elements. */ type NodeEventType = /** Received when node is added to a parent */ "adopt" /** Received when node is removed from its current parent */ | "remove" /** Received when node gains a new parent */ | "reparent" /** Received when node is attached to the screen directly or somewhere in its ancestry */ | "attach" /** Received when node is detached from the screen directly or somewhere in its ancestry */ | "detach"; /** * Screen-specific event types. */ type NodeScreenEventType = /** * Received when the terminal window focuses. * Requires a terminal supporting the focus protocol and * focus needs to be passed to program.enableMouse(). */ "focus" /** * Received when the terminal window blurs. * Requires a terminal supporting the focus protocol and * focus needs to be passed to program.enableMouse(). */ | "blur" /** Element was clicked (slightly smarter than mouseup) */ | "click" | "element click" | "element mouseover" | "element mouseout" | "element mouseup"; /** * Mouse-related event types. */ type NodeMouseEventType = "mouse" | "mouseout" | "mouseover" | "mousedown" | "mouseup" | "mousewheel" | "wheeldown" | "wheelup" | "mousemove"; /** * Generic element event types. */ type NodeGenericEventType = /** Received on screen resize */ "resize" /** Received before render */ | "prerender" /** Received on render */ | "render" /** Received when the screen is destroyed (only useful when using multiple screens) */ | "destroy" /** Received when the element is moved */ | "move" /** Received when element is shown */ | "show" /** Received when element becomes hidden */ | "hide" /** Received when content is set */ | "set content" /** Received when content is parsed */ | "parsed content"; /** * List element specific event types. */ type ListElementEventType = /** List was canceled (when esc is pressed with the keys option) */ "cancel" /** Either a select or a cancel event was received */ | "action" | "create item" | "add item" | "remove item" | "insert item" | "set items"; /** * Textarea element specific event types. */ type TextareaElementEventType = /** Value is an error */ "error" /** Value is submitted (enter) */ | "submit" /** Value is discarded (escape) */ | "cancel" /** Either submit or cancel */ | "action"; /** * events.ts - event emitter for blessed */ /** * EventEmitter */ declare class EventEmitter { _events: any; _maxListeners?: number; type?: string; parent?: any; constructor(); setMaxListeners(n: number): void; addListener(type: string, listener: Function): void; on(type: string, listener: Function): any; removeListener(type: string, listener: Function): void; off(type: string, listener: Function): any; removeAllListeners(type?: string): void; once(type: string, listener: Function): any; listeners(type: string): Function[]; _emit(type: string, args: any[]): any; emit(type: string, ...rest: any[]): boolean; } /** * node.ts - base abstract node for blessed */ /** * Modules */ /** * Node */ declare class Node extends EventEmitter { static uid: number; static ScreenRegistry: any; /** * Type of the node (e.g. box, list, form, etc.). * Used to identify the widget type at runtime. */ type: string; options: NodeOptions; /** * Reference to the parent Screen instance. * Type: Screen (subclass of Node) * * Kept as any due to circular dependency between Node and Screen, * and to preserve access to Screen-specific methods like clearRegion(), * render(), and the program property without complex generic typing. */ screen: any; /** * Reference to the parent element in the widget tree. * Type: Node (can be any Element/Box/List/etc subclass) * * Kept as any to avoid complex generic typing and preserve access * to subclass-specific methods. Attempting to type as Node loses * methods from subclasses like Box, List, Form, etc. */ parent: any; /** * Array of child elements. * Type: Node[] (can contain any Node subclasses) * * Kept as any[] to preserve flexibility with mixed widget types. */ children: any[]; /** * An object for any miscellaneous user data. */ $: Record<string, unknown>; /** * An object for any miscellaneous user data. */ _: Record<string, unknown>; /** * An object for any miscellaneous user data. */ data: Record<string, unknown>; uid: number; /** * Render index (document order index) of the last render call. * Indicates the order in which this element was rendered relative to others. * Set to -1 initially, updated during rendering. */ index: number; detached?: boolean; destroyed?: boolean; runtime: Runtime; constructor(options?: NodeOptions); /** * Insert a node to this node's children at index i. */ insert(element: any, i: number): void; /** * Prepend a node to this node's children. */ prepend(element: any): void; /** * Append a node to this node's children. */ append(element: any): void; /** * Insert a node to this node's children before the reference node. */ insertBefore(element: any, other: any): void; /** * Insert a node from node after the reference node. */ insertAfter(element: any, other: any): void; /** * Remove child node from node. */ remove(element: any): void; /** * Remove node from its parent. */ detach(): void; /** * Free up the element. Automatically unbind all events that may have been bound * to the screen object. This prevents memory leaks. */ free(): void; /** * Same as the detach() method, except this will automatically call free() and unbind any screen * events to prevent memory leaks. For use with onScreenEvent(), removeScreenEvent(), and free(). */ destroy(): void; /** * Iterate over all descendants, calling iter(el) for each. */ forDescendants(iter: (el: any) => void, s?: any): void; /** * Iterate over all ancestors, calling iter(el) for each. */ forAncestors(iter: (el: any) => void, s?: any): void; /** * Collect all descendants into an array. */ collectDescendants(s?: any): any[]; /** * Collect all ancestors into an array. */ collectAncestors(s?: any): any[]; /** * Emit event for element, and recursively emit same event for all descendants. */ emitDescendants(...args: any[]): void; /** * Emit event for element, and recursively emit same event for all ancestors. */ emitAncestors(...args: any[]): void; /** * Check if target is a descendant of this node. */ hasDescendant(target: any): boolean; /** * Check if target is an ancestor of this node. */ hasAncestor(target: any): boolean; /** * Get user property with a potential default value. */ get(name: string, value?: any): any; /** * Set user property to value. */ set(name: string, value: any): any; } /** * Style type definitions for blessed */ /** * Type for style color properties that can be static values or dynamic functions. * Dynamic functions are evaluated at render time with the element as context. * Colors can be strings (color names) or numbers (color codes). */ type StyleColor = string | number | ((element: any) => string | number | undefined); /** * Type for style boolean properties that can be static values or dynamic functions. * Dynamic functions are evaluated at render time with the element as context. */ type StyleBoolean = boolean | ((element: any) => boolean | undefined); interface StyleBorder { bg?: StyleColor; fg?: StyleColor; } interface Effects { bg?: StyleColor; fg?: StyleColor; border?: StyleBorder; } interface Style { /** Background color - can be static or a function evaluated at render time */ bg?: StyleColor; /** Foreground color - can be static or a function evaluated at render time */ fg?: StyleColor; ch?: string; /** Bold attribute - can be static or a function evaluated at render time */ bold?: StyleBoolean; /** Dim attribute - can be static or a function evaluated at render time */ dim?: StyleBoolean; /** Underline attribute - can be static or a function evaluated at render time */ underline?: StyleBoolean; /** Blink attribute - can be static or a function evaluated at render time */ blink?: StyleBoolean; /** Inverse attribute - can be static or a function evaluated at render time */ inverse?: StyleBoolean; /** Invisible attribute - can be static or a function evaluated at render time */ invisible?: StyleBoolean; transparent?: boolean; border?: StyleBorder; hover?: Effects; focus?: Effects; label?: Partial<Style>; track?: { bg?: StyleColor; fg?: StyleColor; }; scrollbar?: { bg?: StyleColor; fg?: StyleColor; }; /** Progress bar fill style (for ProgressBar widget). Style object with fg/bg properties. */ bar?: Partial<Style>; /** Prefix style (for Listbar widget). Style object with fg/bg properties. */ prefix?: Partial<Style>; } interface ListElementStyle extends Style { /** * Style for selected list item. * Kept as any to support truly dynamic styling properties. * Community @types/blessed also uses any for this property. */ selected?: any; /** * Style for regular (unselected) list items. * Kept as any to support truly dynamic styling properties. * Community @types/blessed also uses any for this property. */ item?: any; } interface StyleListTable extends ListElementStyle { /** * Style for table header row. * Kept as any to support dynamic styling configurations. */ header?: any; /** * Style for table cells. * Kept as any to support dynamic styling configurations. */ cell?: any; } /** * Style for ProgressBar widget */ interface ProgressBarStyle extends Style { } /** * Options type definitions for blessed widgets */ type Screen$1 = any; type BlessedElement = any; interface NodeOptions { name?: string; screen?: Screen$1; parent?: Node; children?: Node[]; focusable?: boolean; _isScreen?: boolean; } interface ScrollbarConfig { ch?: string; style?: any; track?: TrackConfig; fg?: string; bg?: string; bold?: boolean; underline?: boolean; inverse?: boolean; invisible?: boolean; ignoreBorder?: boolean; } interface TrackConfig { ch?: string; style?: any; fg?: string; bg?: string; bold?: boolean; underline?: boolean; inverse?: boolean; invisible?: boolean; } interface ScrollableOptions { /** * Whether the element is scrollable or not. */ scrollable?: boolean; /** * A limit to the childBase. Default is Infinity. */ baseLimit?: number; /** * A option which causes the ignoring of childOffset. This in turn causes the * childBase to change every time the element is scrolled. */ alwaysScroll?: boolean; /** * Object enabling a scrollbar. * Style of the scrollbar track if present (takes regular style options). */ scrollbar?: ScrollbarConfig; track?: TrackConfig; /** * Whether to enable automatic mouse support for this element. */ mouse?: boolean; /** * Use pre-defined keys (i or enter for insert, e for editor, C-e for editor while inserting). */ keys?: boolean | string | string[]; /** * Use vi keys with the keys option. */ vi?: boolean; ignoreKeys?: boolean | string[]; } interface ElementOptions extends NodeOptions, ScrollableOptions { /** * Parse tags in content (e.g. {bold}text{/bold}). */ tags?: boolean; /** * Parse tags in content (alias for tags). */ parseTags?: boolean; /** * Foreground color. */ fg?: string; /** * Background color. */ bg?: string; /** * Bold text attribute. */ bold?: boolean; /** * Underline text attribute. */ underline?: boolean; /** * Blinking text attribute. */ blink?: boolean; /** * Inverse/reverse video attribute (swap foreground and background). */ inverse?: boolean; /** * Invisible text attribute. */ invisible?: boolean; /** * Lower element opacity to 50% using naive color blending. * Displays dimmed content with parent's background visible behind it. * Works best with 256-color terminals. * * @example * const box = blessed.box({ transparent: true }); */ transparent?: boolean; /** * Style object containing colors and attributes. */ style?: any; /** * Border object, see below. */ border?: Border | "line" | "bg"; /** * Element's text content. */ content?: string; /** * Element is clickable. */ clickable?: boolean; /** * Element is focusable and can receive key input. */ input?: boolean; /** * Element is focusable and can receive key input (alias for input). */ keyable?: boolean; /** * Controls keyboard focus navigation (similar to HTML tabindex attribute). * * - `undefined`: Not focusable (default for Box, Text, etc.) * - `-1`: Programmatically focusable only, excluded from Tab order * - `0`: Focusable in natural document order (default for Input and List widgets) * - `1+`: Explicit tab order (focused before natural order elements) * * @example * // Make a box focusable in natural order * const box = new Box({ parent: screen, tabIndex: 0 }); * * // Programmatic focus only (excluded from Tab navigation) * const modal = new Box({ parent: screen, tabIndex: -1 }); * * // Explicit order (focused before natural order elements) * const importantBox = new Box({ parent: screen, tabIndex: 1 }); */ tabIndex?: number; /** * Element is focused. */ focused?: BlessedElement; /** * Whether the element is hidden. */ hidden?: boolean; /** * A simple text label for the element. */ label?: string; /** * A floating text label for the element which appears on mouseover. */ hoverText?: string; /** * Background color when element is hovered. */ hoverBg?: string; /** * Effects to apply when element is hovered. */ hoverEffects?: any; /** * Effects to apply when element is focused. */ focusEffects?: any; /** * General effects to apply to the element. */ effects?: any; /** * Text alignment: left, center, or right. */ align?: "left" | "center" | "right"; /** * Vertical text alignment: top, middle, or bottom. */ valign?: "top" | "middle" | "bottom"; /** * Shrink/flex/grow to content and child elements. Width/height during render. */ shrink?: boolean; /** * Wrap text if it exceeds width. */ wrap?: boolean; /** * Amount of padding on the inside of the element. Can be a number or an object containing * the properties: left, right, top, and bottom. */ padding?: number | Partial<Padding>; /** * Offsets of the element relative to its parent. Can be a number, percentage (0-100%), or * keyword (center). right and bottom do not accept keywords. Percentages can also have * offsets (50%+1, 50%-1). */ top?: PositionValue; /** * Offsets of the element relative to its parent. Can be a number, percentage (0-100%), or * keyword (center). right and bottom do not accept keywords. Percentages can also have * offsets (50%+1, 50%-1). */ left?: PositionValue; /** * Right offset of the element relative to its parent. Can be a number or percentage (0-100%). * Percentages can also have offsets (50%+1, 50%-1). */ right?: PositionValue; /** * Bottom offset of the element relative to its parent. Can be a number or percentage (0-100%). * Percentages can also have offsets (50%+1, 50%-1). */ bottom?: PositionValue; /** * Width/height of the element, can be a number, percentage (0-100%), or keyword (half or shrink). * Percentages can also have offsets (50%+1, 50%-1). */ width?: number | string; /** * Width/height of the element, can be a number, percentage (0-100%), or keyword (half or shrink). * Percentages can also have offsets (50%+1, 50%-1). */ height?: number | string; /** * Can contain the above options. */ position?: Position; /** * Background character (default is whitespace ). */ ch?: string; /** * Allow the element to be dragged with the mouse. */ draggable?: boolean; /** * Draw a translucent offset shadow behind the element. * Automatically darkens the background behind the shadow. * * @example * const box = blessed.box({ shadow: true }); */ shadow?: boolean; /** * Prevent content from overflowing the element boundaries. */ noOverflow?: boolean; /** * Automatically "dock" borders with adjacent elements. * Instead of overlapping, borders connect seamlessly to neighboring elements. * * @example * const box1 = blessed.box({ dockBorders: true, top: 0, left: 0, width: 10, height: 5 }); * const box2 = blessed.box({ dockBorders: true, top: 0, left: 10, width: 10, height: 5 }); */ dockBorders?: boolean; /** * Fixed position (does not scroll with parent). */ fixed?: boolean; /** * Type identifier for the element. */ type?: string; } interface ScrollableBoxOptions extends ElementOptions { } interface ScrollableTextOptions extends ScrollableBoxOptions { } interface BoxOptions extends ScrollableTextOptions { bindings?: any; } interface TextOptions extends ElementOptions { /** * Fill the entire line with chosen bg until parent bg ends, even if there * is not enough text to fill the entire width. */ fill?: boolean; /** * Text alignment: left, center, or right. */ align?: Alignment; } interface LineOptions extends BoxOptions { /** * Line orientation. Can be vertical or horizontal. */ orientation?: "vertical" | "horizontal"; /** * Type of line. Treated the same as a border object (attributes can be contained in style). */ type?: string; /** * Background color for the line. */ bg?: string; /** * Foreground color for the line. */ fg?: string; /** * Character to use for drawing the line. */ ch?: string; } interface BigTextOptions extends BoxOptions { /** * Path to bdf->json font file to use (see ttystudio for instructions on compiling BDFs to JSON). */ font?: string; /** * Path to bdf->json bold font file to use (see ttystudio for instructions on compiling BDFs to JSON). */ fontBold?: string; /** * Foreground character to use for rendering (default is space ' '). */ fch?: string; } interface ListOptions<TStyle extends ListElementStyle = ListElementStyle> extends BoxOptions { /** * Style for a selected item and an unselected item. */ style?: TStyle; /** * An array of strings which become the list's items. */ items?: string[]; /** * A function that is called when vi mode is enabled and the key / is pressed. This function accepts a * callback function which should be called with the search string. The search string is then used to * jump to an item that is found in items. */ search?(err: any, value?: string): void; /** * Whether the list is interactive and can have items selected (Default: true). */ interactive?: boolean; /** * Whether to automatically override tags and invert fg of item when selected (Default: true). */ invertSelected?: boolean; /** * Normal shrink behavior for list items. */ normalShrink?: boolean; /** * Background color for selected items. */ selectedBg?: string; /** * Foreground color for selected items. */ selectedFg?: string; /** * Bold attribute for selected items. */ selectedBold?: boolean; /** * Underline attribute for selected items. */ selectedUnderline?: boolean; /** * Blink attribute for selected items. */ selectedBlink?: boolean; /** * Inverse attribute for selected items. */ selectedInverse?: boolean; /** * Invisible attribute for selected items. */ selectedInvisible?: boolean; /** * Background color for unselected items. */ itemBg?: string; /** * Foreground color for unselected items. */ itemFg?: string; /** * Bold attribute for unselected items. */ itemBold?: boolean; /** * Underline attribute for unselected items. */ itemUnderline?: boolean; /** * Blink attribute for unselected items. */ itemBlink?: boolean; /** * Inverse attribute for unselected items. */ itemInverse?: boolean; /** * Invisible attribute for unselected items. */ itemInvisible?: boolean; /** * Background color when hovering over items. */ itemHoverBg?: string; /** * Effects to apply when hovering over items. */ itemHoverEffects?: any; /** * Effects to apply when items are focused. */ itemFocusEffects?: any; } interface FileManagerOptions extends ListOptions<ListElementStyle> { /** * Current working directory. */ cwd?: string; } interface ListTableOptions extends ListOptions<StyleListTable> { /** * Array of array of strings representing rows. */ rows?: string[]; /** * Array of array of strings representing rows (same as rows). */ data?: string[][]; /** * Spaces to attempt to pad on the sides of each cell. 2 by default: one space on each side * (only useful if the width is shrunken). */ pad?: number; /** * Do not draw inner cells. */ noCellBorders?: boolean; /** * Fill cell borders with the adjacent background color. */ fillCellBorders?: boolean; /** * Style configuration for the list table including header and cell styles. */ style?: StyleListTable; } interface ListbarOptions extends BoxOptions { /** * Style configuration for the listbar. */ style?: ListElementStyle; /** * Set buttons using an array of command objects containing keys and callbacks. */ commands?: any[]; /** * Array of items for the listbar (same as commands). */ items?: any[]; /** * Automatically bind list buttons to keys 0-9. */ autoCommandKeys?: boolean; } interface FormOptions extends BoxOptions { /** * Allow default keys (tab, vi keys, enter). */ keys?: any; /** * Allow vi keys for navigation. */ vi?: boolean; /** * Automatically focus next element after submission. */ autoNext?: boolean; } interface InputOptions extends BoxOptions { } interface TextareaOptions extends InputOptions { /** * Call readInput() when the element is focused. Automatically unfocus. */ inputOnFocus?: boolean; /** * Initial value of the textarea. */ value?: string; /** * Enable key support (can be boolean, string, or array of strings). *