
Research
Malicious fezbox npm Package Steals Browser Passwords from Cookies via Innovative QR Code Steganographic Technique
A malicious package uses a QR code as steganography in an innovative technique.
@remotex-labs/xansi
Advanced tools
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.
writeRaw
function.npm install @remotex-labs/xansi
xAnsi supports subpath imports, allowing you to import only the specific components you need to minimize your application's bundle size:
// Import specific components instead of the entire package
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';
The xterm
component provides advanced terminal styling capabilities with a chainable API for applying colors and text styles.
import { xterm } from '@remotex-labs/xansi';
// Basic color styling console.log(xterm.red('This text is red')); console.log(xterm.blue('This text is blue'));
// Chain multiple styles console.log(xterm.bold.yellow('Bold yellow text')); console.log(xterm.green.bgBlack.inverse('Styled text'));
// Use with template literals const name = 'world'; console.log(xterm.cyan); `Hello ${name}!`
console.log(xterm.yellow('Hello %s'), name);
// RGB colors
console.log(xterm.rgb(255, 100, 50)('Custom RGB colored text'));
console.log(xterm.bgRgb(30, 60, 90)('Text with RGB background'));
// Hex colors
console.log(xterm.hex('#ff5733')('Hex colored text'));
console.log(xterm.bgHex('#3498db')('Text with hex background'));
// Combining RGB/Hex with other styles
console.log(xterm.hex('#ff5733').bold.bgHex('#3498db')('Custom styled text'));
The component uses TypeScript to enforce proper style combinations: xterm
// Valid combinations
xterm.red.bold.inverse('Valid styling');
xterm.green.bgBlue.dim('Valid styling');
xterm.rgb(100, 150, 200).bgHex('#333').bold('Valid styling');
// Invalid combinations (TypeScript will show errors)
// xterm.red.green('Invalid - two foreground colors');
// xterm.bgBlue.bgRed('Invalid - two background colors');
The component includes common ANSI text styles:
bold
, dim
, inverse
, hidden
, reset
black
, red
, green
, yellow
, blue
, magenta
, cyan
, white
, gray
blackBright
, redBright
, greenBright
, yellowBright
, blueBright
, magentaBright
, cyanBright
, whiteBright
bgBlack
, bgRed
, bgGreen
, bgYellow
, bgBlue
, bgMagenta
, bgCyan
, bgWhite
, bgGray
bgBlackBright
, bgRedBright
, bgGreenBright
, bgYellowBright
, bgBlueBright
, bgMagentaBright
, bgCyanBright
, bgWhiteBright
The ANSI component provides essential utilities for terminal control operations through raw ANSI escape sequences, allowing for low-level manipulation of terminal output.
import { writeRaw } from '@remotex-labs/xansi';
// Write text directly to the terminal
writeRaw('Hello, world!');
// Works with styled text from the xterm component
import { xterm } from '@remotex-labs/xansi';
writeRaw(xterm.bold.green('Success!'));
// Handle multiline content
writeRaw(`First line
Second line`);
The writeRaw
function provides a consistent output method that automatically adapts to your JavaScript environment:
process.stdout.write
console.log
import { moveCursor, writeRaw } from '@remotex-labs/xansi';
// Move cursor to row 5, column 10
const cursorPosition = moveCursor(5, 10);
writeRaw(cursorPosition);
writeRaw('Text at specific position');
// Move to beginning of line 3
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).
import { ANSI, writeRaw } from '@remotex-labs/xansi';
// Clear the current line
writeRaw(ANSI.CLEAR_LINE);
// Hide the cursor (useful for animations)
writeRaw(ANSI.HIDE_CURSOR);
// ...perform operations...
writeRaw(ANSI.SHOW_CURSOR); // Show it again when done
// Save cursor position
writeRaw(ANSI.SAVE_CURSOR);
// ...move cursor elsewhere and write content...
writeRaw(ANSI.RESTORE_CURSOR); // Return to saved position
// Clear the entire screen
writeRaw(ANSI.CLEAR_SCREEN);
// Clear screen below current cursor position
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
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.
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); // example: row = col = i
topRight.writeText(i, i, letter); // example: row = col = i
}
topLeft.render();
topRight.render();
topIndex = (topIndex + 1) % letters.length;
}, 1000); // 1000ms = 1 second
setInterval(() => {
for (let i = 0; i < 5; i++) {
// Go backward from index: index, index-1, ..., index-4
const letterIndex = (index - i + letters.length) % letters.length;
const letter = letters[letterIndex];
left.writeText(i, i, letter); // Display diagonally
right.writeText(i, i, letter); // Display diagonally
}
left.render();
right.render();
// Move backward in the alphabet
index = (index - 1 + letters.length) % letters.length;
}, 1000);
import { ShadowRenderer } from '@remotex-labs/xansi';
// Create a renderer that occupies the top part of the terminal
const renderer = new ShadowRenderer(10, 80, 0, 0);
// Reposition the renderer to create space for a header
renderer.top = 3; // Now starts at row 3 (below a header area)
renderer.left = 2; // Indented by 2 columns
// Resize the renderer to accommodate a sidebar
renderer.width = 70; // Reduce width to leave space for a sidebar
// Handle terminal resize events
process.stdout.on('resize', () => {
// Adjust to new terminal dimensions
renderer.width = process.stdout.columns - 10; // Leave 10 columns for sidebar
renderer.height = process.stdout.rows - 5; // Leave 5 rows for header/footer
// Force redraw after resize
renderer.render(true);
});
import { ShadowRenderer, xterm } from '@remotex-labs/xansi';
// Create a fixed-height viewport for a larger content area
const renderer = new ShadowRenderer(10, 80, 5, 0); // 10-row viewport
// Generate more content than fits in the viewport
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`);
}
// Initial render shows first 10 rows (0-9)
renderer.render();
// Implement scrolling behavior
function scrollDown() {
if (renderer.scroll < 20) { // Prevent scrolling past content
renderer.scroll = renderer.scroll + 1; // Automatically re-renders
}
}
function scrollUp() {
if (renderer.scroll > 0) {
renderer.scroll = -1; // Negative values are relative to current position
}
}
function jumpToPosition(position) {
renderer.scroll = position; // Positive values set absolute position
}
import { ShadowRenderer, writeRaw, ANSI, xterm } from '@remotex-labs/xansi';
// Create a renderer for a modal dialog
const renderer = new ShadowRenderer(10, 50, 5, 20);
writeRaw(ANSI.CLEAR_SCREEN);
// Create a modal dialog with title and content
function showModal(title, content) {
// Hide cursor during rendering to prevent flicker
writeRaw(ANSI.HIDE_CURSOR);
try {
// Clear previous content
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) + '┐';
// Draw border and title
renderer.writeText(0, 0, line);
// Draw content
const lines = content.split('\n');
for (let i = 0; i < lines.length; i++) {
renderer.writeText(i + 2, 2, lines[i]);
}
// Draw bottom border
renderer.writeText(9, 0, '└' + '─'.repeat(38) + '┘');
// Replace content with clean flag to ensure no artifacts
renderer.writeText(8, 2, xterm.dim('Press any key to continue...'), true);
// Force complete redraw
renderer.render(true);
} finally {
// Always restore cursor visibility
writeRaw(ANSI.SHOW_CURSOR);
}
}
// Usage example
showModal('Information', 'The operation completed successfully.\nAll files have been processed.');
The Shadow Renderer is optimized for scenarios where:
The diffing algorithm ensures minimal terminal I/O operations by tracking which cells have changed and only updating those specific positions.
This TypeScript program creates a visually engaging terminal animation featuring multiple colored blocks (█) that bounce around your terminal window.
import { ShadowRenderer, writeRaw, ANSI, xterm } from '@remotex-labs/xansi';
import { createInterface } from 'readline';
interface Ball {
x: number;
y: number;
dx: number;
dy: number;
colorStep: number;
prevColor: string;
nextColor: string;
}
class BouncingBalls {
private renderer: ShadowRenderer;
private width = 0;
private height = 0;
private intervalId: NodeJS.Timeout | null = null;
private balls: Ball[] = [];
private fadeSteps = 30;
private ballCount = 15;
constructor() {
this.renderer = new ShadowRenderer(this.height, this.width, 0, 0);
this.updateTerminalSize();
process.stdout.on('resize', () => this.updateTerminalSize());
writeRaw(ANSI.HIDE_CURSOR);
writeRaw(ANSI.CLEAR_SCREEN);
// Initialize multiple balls
this.initializeBalls();
}
private initializeBalls(): void {
this.balls = [];
for (let i = 0; i < this.ballCount; i++) {
const x = Math.floor(Math.random() * (this.width - 4));
const y = Math.floor(Math.random() * (this.height - 2));
const dx = Math.random() > 0.5 ? 1 : -1;
const dy = Math.random() > 0.5 ? 1 : -1;
const initialColor = this.getColor(x, y);
this.balls.push({
x,
y,
dx,
dy,
colorStep: 0,
prevColor: initialColor,
nextColor: initialColor
});
}
}
private updateTerminalSize(): void {
this.width = process.stdout.columns || 80;
this.height = process.stdout.rows || 24;
this.renderer.width = this.width;
this.renderer.height = this.height;
// Reinitialize balls when terminal size changes
this.initializeBalls();
}
private getColor(x: number, y: number): string {
const r = (x * 5) % 255;
const g = (y * 5) % 255;
const b = ((x + y) * 3) % 255;
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
private hexToRgb(hex: string): [number, number, number] {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
]
: [255, 255, 255];
}
private interpolateColor(c1: string, c2: string, t: number): string {
const [r1, g1, b1] = this.hexToRgb(c1);
const [r2, g2, b2] = this.hexToRgb(c2);
const r = Math.round(r1 + (r2 - r1) * t);
const g = Math.round(g1 + (g2 - g1) * t);
const b = Math.round(b1 + (b2 - b1) * t);
return `#${[r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
}
private getFadedColor(ball: Ball): string {
const t = ball.colorStep / this.fadeSteps;
const color = this.interpolateColor(ball.prevColor, ball.nextColor, t);
ball.colorStep++;
if (ball.colorStep > this.fadeSteps) {
ball.colorStep = 0;
ball.prevColor = ball.nextColor;
ball.nextColor = this.getColor(ball.x, ball.y);
}
return color;
}
private drawBalls(): void {
for (const ball of this.balls) {
const color = this.getFadedColor(ball);
this.renderer.writeText(ball.y, ball.x, xterm.hex(color).bold('█'), true);
}
this.renderer.render();
}
private updateBallPositions(): void {
for (const ball of this.balls) {
ball.x += ball.dx;
ball.y += ball.dy;
// Bounce off walls
if (ball.x <= 0 || ball.x >= this.width - 1) {
ball.dx = -ball.dx;
ball.x = Math.max(0, Math.min(this.width - 1, ball.x));
}
if (ball.y <= 0 || ball.y >= this.height - 1) {
ball.dy = -ball.dy;
ball.y = Math.max(0, Math.min(this.height - 1, ball.y));
}
}
}
public start(frameRate = 50): void {
if (this.intervalId) clearInterval(this.intervalId);
this.intervalId = setInterval(() => {
this.updateBallPositions();
this.drawBalls();
}, frameRate);
const rl = createInterface({ input: process.stdin, output: process.stdout });
process.stdin?.setRawMode?.(true);
process.stdin.on('data', (key) => {
if (key.toString() === '\u0003' || key.toString().toLowerCase() === 'q') {
this.stop();
process.exit(0);
}
});
}
public stop(): void {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
writeRaw(ANSI.CLEAR_SCREEN);
writeRaw(ANSI.SHOW_CURSOR);
}
}
const balls = new BouncingBalls();
balls.start(15); // smoother animation
console.log('Press q or Ctrl+C to exit');
A mesmerizing terminal-based animation featuring a colored block character (█) that bounces around your terminal window. This program creates a simple yet captivating visual effect using ANSI terminal capabilities.
import { ShadowRenderer, writeRaw, ANSI, xterm } from '@remotex-labs/xansi';
import { createInterface } from 'readline';
class BouncingBall {
private x = 0;
private y = 0;
private dx = 1;
private dy = 1;
private width = 0;
private height = 0;
private intervalId: NodeJS.Timeout | null = null;
private colorStep = 0;
private fadeSteps = 30;
private prevColor = '#000000';
private nextColor = '#ffffff';
private renderer: ShadowRenderer;
constructor() {
this.renderer = new ShadowRenderer(this.height, this.width, 0, 0);
this.updateTerminalSize();
process.stdout.on('resize', () => this.updateTerminalSize());
writeRaw(ANSI.HIDE_CURSOR);
writeRaw(ANSI.CLEAR_SCREEN);
this.resetPosition();
this.prevColor = this.getColor(this.x, this.y);
this.nextColor = this.prevColor;
}
private updateTerminalSize(): void {
this.width = process.stdout.columns || 80;
this.height = process.stdout.rows || 24;
this.renderer.width = this.width;
this.renderer.height = this.height;
if (this.x >= this.width - 4 || this.y >= this.height - 2) {
this.resetPosition();
}
}
private resetPosition(): void {
this.x = Math.floor(this.width / 2);
this.y = Math.floor(this.height / 2);
}
private getColor(x: number, y: number): string {
const r = (x * 5) % 255;
const g = (y * 5) % 255;
const b = ((x + y) * 3) % 255;
return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
}
private hexToRgb(hex: string): [number, number, number] {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result
? [
parseInt(result[1], 16),
parseInt(result[2], 16),
parseInt(result[3], 16)
]
: [255, 255, 255];
}
private interpolateColor(c1: string, c2: string, t: number): string {
const [r1, g1, b1] = this.hexToRgb(c1);
const [r2, g2, b2] = this.hexToRgb(c2);
const r = Math.round(r1 + (r2 - r1) * t);
const g = Math.round(g1 + (g2 - g1) * t);
const b = Math.round(b1 + (b2 - b1) * t);
return `#${[r, g, b].map((v) => v.toString(16).padStart(2, '0')).join('')}`;
}
private getFadedColor(): string {
const t = this.colorStep / this.fadeSteps;
const color = this.interpolateColor(this.prevColor, this.nextColor, t);
this.colorStep++;
if (this.colorStep > this.fadeSteps) {
this.colorStep = 0;
this.prevColor = this.nextColor;
this.nextColor = this.getColor(this.x, this.y);
}
return color;
}
private drawBall(): void {
const color = this.getFadedColor();
this.renderer.writeText(this.y, this.x, xterm.hex(color).bold('█'), true);
this.renderer.writeText(this.y, this.x + 1, xterm.hex(color).bold('█'), true);
this.renderer.render();
}
private updateBallPosition(): void {
this.x += this.dx;
this.y += this.dy;
if (this.x <= 0 || this.x >= this.width - 3) {
this.dx = -this.dx;
this.x = Math.max(0, Math.min(this.width - 3, this.x));
}
if (this.y <= 0 || this.y >= this.height - 2) {
this.dy = -this.dy;
this.y = Math.max(0, Math.min(this.height - 2, this.y));
}
}
public start(frameRate = 50): void {
if (this.intervalId) clearInterval(this.intervalId);
this.intervalId = setInterval(() => {
this.updateBallPosition();
this.drawBall();
}, frameRate);
const rl = createInterface({ input: process.stdin, output: process.stdout });
process.stdin?.setRawMode?.(true);
process.stdin.on('data', (key) => {
if (key.toString() === '\u0003' || key.toString().toLowerCase() === 'q') {
this.stop();
process.exit(0);
}
});
}
public stop(): void {
if (this.intervalId) {
clearInterval(this.intervalId);
this.intervalId = null;
}
writeRaw(ANSI.CLEAR_SCREEN);
writeRaw(ANSI.SHOW_CURSOR);
}
}
const ball = new BouncingBall();
ball.start(15); // smoother animation
console.log('Press q or Ctrl+C to exit');
Contributions are welcome! Please feel free to submit a Pull Request.
FAQs
A lightweight ANSI utility library for styling terminal output
The npm package @remotex-labs/xansi receives a total of 174 weekly downloads. As such, @remotex-labs/xansi popularity was classified as not popular.
We found that @remotex-labs/xansi demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
A malicious package uses a QR code as steganography in an innovative technique.
Research
/Security News
Socket identified 80 fake candidates targeting engineering roles, including suspected North Korean operators, exposing the new reality of hiring as a security function.
Application Security
/Research
/Security News
Socket detected multiple compromised CrowdStrike npm packages, continuing the "Shai-Hulud" supply chain attack that has now impacted nearly 500 packages.