npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

voonex

v0.1.0

Published

A zero-dependency Terminal UI Library for Node.js.

Downloads

143

Readme

Voonex

Voonex is a modern, zero-dependency Terminal UI (TUI) library for Node.js, built with TypeScript. It provides a robust virtual buffer, reactive rendering system, and a rich set of widgets to build complex command-line interfaces with ease.

Features

  • Zero Dependencies: Lightweight and easy to audit.
  • Double Buffering & Diffing: Efficient rendering that eliminates flickering and minimizes I/O.
  • Component System: Built-in widgets like Box, Menu, ProgressBar, Input, Table, and more.
  • Reactive Rendering: Automated screen updates via Screen.mount() and Screen.scheduleRender().
  • Focus Management: Built-in keyboard navigation and focus delegation.
  • Styling Engine: Simple yet powerful API for ANSI colors and text modifiers.
  • TypeScript Support: Written in TypeScript with full type definitions included.

Installation

Install via npm:

npm install voonex

Quick Start

Here is a minimal example showing how to initialize the screen and display a simple box.

import { Screen, Box, Styler, Input } from 'voonex';

// 1. Enter the alternate screen buffer
Screen.enter();

// 2. Define your render function
function render() {
    // Render a Box at (5, 5) with a title
    Box.render([
        "Welcome to Voonex!",
        "Press 'q' to exit."
    ], {
        title: "Hello World",
        x: 5,
        y: 5,
        padding: 1,
        style: 'double',
        borderColor: 'cyan'
    });
}

// 3. Mount the render function to the screen loop
Screen.mount(render);

// 4. Handle Input
Input.onKey((key) => {
    if (key.name === 'q') {
        // Leave the screen buffer properly before exiting
        Screen.leave();
        process.exit(0);
    }
});

Run it with:

npx ts-node my-app.ts

Core Concepts

The Screen

The Screen class is the heart of Voonex. It manages the terminal buffer, handles resizing, and optimizes rendering using a diffing algorithm.

  • Screen.enter(): Switches to the alternate buffer (like vim or nano).
  • Screen.leave(): Restores the original terminal state. Always call this before exiting.
  • Screen.mount(renderFn): Registers a function to be called during the render cycle. Voonex uses a "Painter's Algorithm", so functions mounted later are drawn on top.
  • Screen.scheduleRender(): Triggers a screen update. This is automatically called by most interactive components, but you can call it manually if you update state asynchronously (e.g., inside a setInterval).

Input Handling

Voonex provides a global input listener wrapper around Node's process.stdin.

import { Input } from 'voonex';

Input.onKey((key) => {
    console.log(key.name); // 'up', 'down', 'enter', 'a', 'b', etc.
    console.log(key.ctrl); // true if Ctrl is pressed
});

Styling

The Styler class provides utilities for coloring and formatting text.

import { Styler } from 'voonex';

const text = Styler.style("Success!", 'green', 'bold', 'underline');

Components

Voonex comes with several built-in components. Components can be used in two ways:

  1. Static Rendering: Using static methods like Box.render().
  2. Stateful Instances: Creating an instance (e.g., new Menu()) and calling its methods.

Box

A container for text with optional borders, padding, and titles.

Box.render([
    "Line 1",
    "Line 2"
], {
    x: 2, y: 2,
    width: 30,
    borderColor: 'green',
    style: 'round' // 'single', 'double', or 'round'
});

Menu

A vertical list of selectable items.

const menu = new Menu({
    title: "Main Menu",
    x: 4, y: 4,
    items: ["Start", "Options", "Exit"],
    onSelect: (index, item) => {
        console.log(`Selected: ${item}`);
    }
});

// In your input handler:
Input.onKey((key) => {
    menu.handleKey(key); // Passes key events to the menu
});

// In your render function:
menu.render();

ProgressBar

Displays a progress bar.

const bar = new ProgressBar({
    width: 20,
    total: 100,
    x: 4, y: 10,
    completeChar: '█',
    incompleteChar: '░'
});

// Update progress
bar.update(50); // 50%

Popup

A modal dialog that overlays other content.

// Shows a message and waits for user to press Enter/Esc
await Popup.alert("This is an important message!", { title: "Alert" });

// Asks for confirmation (returns boolean)
const confirmed = await Popup.confirm("Are you sure?", { title: "Confirm" });

Input Field

A text input field for capturing user input.

const nameInput = new InputField({
    x: 2, y: 2,
    width: 20,
    placeholder: "Enter name..."
});

Input.onKey(key => {
    if (key.name === 'tab') {
        nameInput.focus();
    }
    nameInput.handleKey(key);
});

// In render loop
nameInput.render();

Advanced Usage

Manual Layout

Voonex relies on absolute positioning (x, y). For complex layouts, you can calculate coordinates dynamically based on Screen.size.

const { width, height } = Screen.size;
const centerX = Math.floor(width / 2);
const centerY = Math.floor(height / 2);

Creating Custom Components

Any class can be a component. To integrate with the Voonex ecosystem, it's recommended (but not required) to implement the Focusable interface if the component handles input.

import { Screen, Styler, Focusable } from 'voonex';

class MyWidget implements Focusable {
    focus() { /* handle focus */ }
    blur() { /* handle blur */ }
    
    handleKey(key) {
        // return true if key was consumed
        return false;
    }

    render() {
        Screen.write(10, 10, "My Custom Widget");
    }
}

License

This project is under the MIT License.