@unblessed/core
Version:
Platform-agnostic terminal UI core library with runtime dependency injection
1,720 lines (1,705 loc) • 183 kB
TypeScript
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).
*