xAnsi

A lightweight ANSI utility library for styling terminal output.
xAnsi provides easy-to-use components for working with ANSI escape codes to create colorful and formatted terminal interfaces.
Features
- ANSI Component: Utility constants and functions for terminal control, including cursor movement, screen clearing operations, and raw terminal output through the
writeRaw
function.
- xTerm Component: Advanced terminal styling with support for xTerm color codes
- Shadow Renderer: A virtual terminal renderer that efficiently manages screen updates by maintaining separate buffers for desired content and current display state, supporting features like partial screen updates, content scrolling, variable viewport dimensions, and optimized rendering with minimal draw operations
Installation
npm install @remotex-labs/xansi
Optimizing Bundle Size
xAnsi supports subpath imports, allowing you to import only the specific components you need to minimize your application's bundle size:
import { xterm } from '@remotex-labs/xansi/xterm.component';
import { writeRaw, ANSI } from '@remotex-labs/xansi/ansi.component';
import { ShadowRenderer } from '@remotex-labs/xansi/shadow.service';
xTerm Component
The xterm
component provides advanced terminal styling capabilities with a chainable API for applying colors and text styles.
Basic Usage
import { xterm } from '@remotex-labs/xansi';
console.log(xterm.yellow('Hello %s'), name);
RGB and Hex Colors
console.log(xterm.rgb(255, 100, 50)('Custom RGB colored text'));
console.log(xterm.bgRgb(30, 60, 90)('Text with RGB background'));
console.log(xterm.hex('#ff5733')('Hex colored text'));
console.log(xterm.bgHex('#3498db')('Text with hex background'));
console.log(xterm.hex('#ff5733').bold.bgHex('#3498db')('Custom styled text'));
Style Combinations
The component uses TypeScript to enforce proper style combinations: xterm
- You can only apply one foreground color
- You can only apply one background color
- Text modifiers (bold, dim, inverse, etc.) can be combined
xterm.red.bold.inverse('Valid styling');
xterm.green.bgBlue.dim('Valid styling');
xterm.rgb(100, 150, 200).bgHex('#333').bold('Valid styling');
Available Styles
The component includes common ANSI text styles:
- Text modifiers:
bold
, dim
, inverse
, hidden
, reset
- Foreground colors:
black
, red
, green
, yellow
, blue
, magenta
, cyan
, white
, gray
- Bright foreground colors:
blackBright
, redBright
, greenBright
, yellowBright
, blueBright
, magentaBright
, cyanBright
, whiteBright
- Background colors:
bgBlack
, bgRed
, bgGreen
, bgYellow
, bgBlue
, bgMagenta
, bgCyan
, bgWhite
, bgGray
- Bright background colors:
bgBlackBright
, bgRedBright
, bgGreenBright
, bgYellowBright
, bgBlueBright
, bgMagentaBright
, bgCyanBright
, bgWhiteBright
ANSI Component
The ANSI component provides essential utilities for terminal control operations through raw ANSI escape sequences, allowing for low-level manipulation of terminal output.
Output Functions
import { writeRaw } from '@remotex-labs/xansi';
writeRaw('Hello, world!');
import { xterm } from '@remotex-labs/xansi';
writeRaw(xterm.bold.green('Success!'));
writeRaw(`First line
Second line`);
The writeRaw
function provides a consistent output method that automatically adapts to your JavaScript environment:
- In Node.js, it uses for more efficient output
process.stdout.write
- In browsers or other environments, it falls back to
console.log
Cursor Movement
import { moveCursor, writeRaw } from '@remotex-labs/xansi';
const cursorPosition = moveCursor(5, 10);
writeRaw(cursorPosition);
writeRaw('Text at specific position');
writeRaw(moveCursor(3, 1));
writeRaw('Line 3 content');
The moveCursor
function generates ANSI sequences to position the cursor at specific coordinates in the terminal. Both row and column use 1-based indexing (1 is the first row/column).
Terminal Control Constants
import { ANSI, writeRaw } from '@remotex-labs/xansi';
writeRaw(ANSI.CLEAR_LINE);
writeRaw(ANSI.HIDE_CURSOR);
writeRaw(ANSI.SHOW_CURSOR);
writeRaw(ANSI.SAVE_CURSOR);
writeRaw(ANSI.RESTORE_CURSOR);
writeRaw(ANSI.CLEAR_SCREEN);
writeRaw(ANSI.CLEAR_SCREEN_DOWN);
The object provides common terminal control sequences as constants, making it easier to manipulate terminal state without remembering cryptic escape sequences. ANSI
Shadow Renderer
The Shadow Renderer provides a powerful, optimized rendering system for terminal-based UIs.
It maintains separate buffers for content and display state to minimize terminal operations and improve performance.
Key Features
- Optimized Rendering: Only updates parts of the screen that have changed
- Content Scrolling: Efficiently manages content larger than the viewport
- Viewport Management: Supports positioning and resizing of the display area
- Clean Diffing Algorithm: Minimizes ANSI escape sequences for better performance
Basic Usage
import { ShadowRenderer, writeRaw, ANSI } from '@remotex-labs/xansi';
const topLeft = new ShadowRenderer(5, 40, 0, 0);
const topRight = new ShadowRenderer(5, 40, 0, 40);
const left = new ShadowRenderer(6, 40, 5, 5);
const right = new ShadowRenderer(6, 40, 5, 45);
writeRaw(ANSI.HIDE_CURSOR);
writeRaw(ANSI.CLEAR_SCREEN);
const letters = 'abcdefghijklmnopqrstuvwxyz';
let topIndex = 0;
let index = 0;
setInterval(() => {
for (let i = 0; i < 5; i++) {
const letterIndex = (topIndex + i) % letters.length;
const letter = letters[letterIndex];
topLeft.writeText(i, i, letter);
topRight.writeText(i, i, letter);
}
topLeft.render();
topRight.render();
topIndex = (topIndex + 1) % letters.length;
}, 1000);
setInterval(() => {
for (let i = 0; i < 5; i++) {
const letterIndex = (index - i + letters.length) % letters.length;
const letter = letters[letterIndex];
left.writeText(i, i, letter);
right.writeText(i, i, letter);
}
left.render();
right.render();
index = (index - 1 + letters.length) % letters.length;
}, 1000);
Viewport Management
import { ShadowRenderer } from '@remotex-labs/xansi';
const renderer = new ShadowRenderer(10, 80, 0, 0);
renderer.top = 3;
renderer.left = 2;
renderer.width = 70;
process.stdout.on('resize', () => {
renderer.width = process.stdout.columns - 10;
renderer.height = process.stdout.rows - 5;
renderer.render(true);
});
Content Scrolling
import { ShadowRenderer, xterm } from '@remotex-labs/xansi';
const renderer = new ShadowRenderer(10, 80, 5, 0);
for (let i = 0; i < 30; i++) {
const prefix = i === 0 ? xterm.bold('•') : ' ';
renderer.writeText(i, 0, `${prefix} Item ${i + 1}: This is a scrollable content row`);
}
renderer.render();
function scrollDown() {
if (renderer.scroll < 20) {
renderer.scroll = renderer.scroll + 1;
}
}
function scrollUp() {
if (renderer.scroll > 0) {
renderer.scroll = -1;
}
}
function jumpToPosition(position) {
renderer.scroll = position;
}
Advanced Rendering Techniques
import { ShadowRenderer, writeRaw, ANSI, xterm } from '@remotex-labs/xansi';
const renderer = new ShadowRenderer(10, 50, 5, 20);
writeRaw(ANSI.CLEAR_SCREEN);
function showModal(title, content) {
writeRaw(ANSI.HIDE_CURSOR);
try {
renderer.clear();
const totalWidth = 40;
const labelWithPadding = ` ${ title } `;
const lineWidth = totalWidth - 2;
const dashCount = lineWidth - labelWithPadding.length;
const leftDashes = Math.floor(dashCount / 2);
const rightDashes = dashCount - leftDashes;
const line = '┌' + '─'.repeat(leftDashes) + xterm.hex('#bf1e1e').bold.dim(labelWithPadding) + '─'.repeat(rightDashes) + '┐';
renderer.writeText(0, 0, line);
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
renderer.writeText(i + 2, 2, lines[i]);
}
renderer.writeText(9, 0, '└' + '─'.repeat(38) + '┘');
renderer.writeText(8, 2, xterm.dim('Press any key to continue...'), true);
renderer.render(true);
} finally {
writeRaw(ANSI.SHOW_CURSOR);
}
}
showModal('Information', 'The operation completed successfully.\nAll files have been processed.');
Performance Considerations
The Shadow Renderer is optimized for scenarios where:
- You're building interactive terminal UIs with frequent updates
- You need to manage content larger than the visible viewport
- You want to avoid screen flicker from complete redraws
The diffing algorithm ensures minimal terminal I/O operations by tracking which cells have changed and only updating those specific positions.
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Links