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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@nightgrey/ansi

v0.1.4

Published

A library providing utilities for working with ANSI escape sequences (ported from charmbracelet/x/ansi)

Downloads

20

Readme

ANSI

An almost complete TypeScript port of the excellent charmbracelet/x/ansi library, providing utilities for working with ANSI escape sequences. To have a complete overview, check out the API reference.

Note: This project does not have complete feature-parity with the original Go library. See comparison & differences for more. Additionally, I would probably not recommend using it for production use.

Installation

bun install @nightgrey/ansi
# or
pnpm install @nightgrey/ansi
# or
yarn add @nightgrey/ansi
# or
npm add @nightgrey/ansi

Table of Contents

Usage

You can find the complete API reference at nightgrey.github.io/ansi.

import {
    Attributes,
    BEL,
    BracketedPasteMode,
    cursorPosition,
    cursorUp,
    FocusEventMode,
    IndexedColor,
    KeyboardActionMode,
    Mode,
    parser,
    Style,
    stringWidth,
    strip,
    tokenizer,
} from "./src";

const stdout = process.stdout;

// Handle SGR attributes with a fast, immutable class based on a bitfield
const curly = new Attributes().underline().curlyUnderline().italic();

const colors = new Attributes()
    .backgroundColor(IndexedColor.Blue)
    .underlineColor(IndexedColor.BrightBlue);

// Use logical operations to combine attributes
const combined = curly.and(colors);
stdout.write(combined.toString()); // CSI 4:3:1;48;5;94m (underline + italic + blue background + bright blue foreground)

// Create `Style` instance(s) from it
const style = Style.from(combined);
stdout.write(style.format("Hello World"));

// Style text easily!
// Colors can be specified by almost any CSS notation (hex, rgb, rgba, hsl, etc.), ANSI indexes, or vectors.
const red = new Style().foregroundColor(IndexedColor.Red);
const alsoRed = new Style().foregroundColor(1);
const blue = new Style().foregroundColor("rgb(0, 100, 255)");
const grey = new Style().foregroundColor([0.2, 0.2, 0.2]);

// `Style` instances are immutable and chainable
const italic = blue.italic();
stdout.write(italic.format("I'm blue and italic"));

const fancy = red
    .bold()
    .curlyUnderline()
    .blink()
    .reverse()
    .faint()
    .backgroundColor(IndexedColor.Blue);

stdout.write(
    fancy.format(
        "I'm reversed - blue in color and red in background, bold, curly underlined, blinking and faint!",
    ),
);

// Cursor control
stdout.write(cursorUp(5)); // Move cursor up 5 rows
stdout.write(cursorPosition(10, 10)); // Set absolute cursor position to (10, 10)

// Text processing
stdout.write(strip("\u001B[4mUnicorn\u001B[0m")); // `Unicorn`
stdout.write(stringWidth("\u001B[1m古\u001B[22m").toString()); // 2

// Parsing
const result = [...parser(tokenizer(String.raw`\x1b[31mHello\x1b[0m World`))];
// Result:
// [
//   {
//     type: "CSI",
//     pos: 0,
//     raw: "\x1b[31m",
//     command: "m",
//     params: ["31"],
//   },
//   {
//     type: "TEXT",
//     pos: 8,
//     raw: "Hello",
//   },
//   {
//     type: "CSI",
//     pos: 13,
//     raw: "\x1b[0m",
//     command: "m",
//     params: ["0"],
//   },
// ]

// Manage terminal modes
stdout.write(BracketedPasteMode.set); // Set bracketed paste mode, enabling bracketed paste
stdout.write(KeyboardActionMode.set); // Set keyboard action mode, locking the keyboard

// .. or request and parse responses
stdout.write(FocusEventMode.request); // Request focus event mode
process.stdin.on("data", (data) => {
    const setting = Mode.match(data.toString());
    if (Mode.isSet(setting)) {
        stdout.write("Event mode is set");
    } else if (Mode.isReset(setting)) {
        stdout.write("Event mode is reset");
    } else if (Mode.isPermanentlySet(setting)) {
        stdout.write("Event mode is permanently set");
    } else if (Mode.isPermanentlyReset(setting)) {
        stdout.write("Event mode is permanently reset");
    } else if (Mode.isNotRecognized(setting)) {
        stdout.write("Event mode is not recognized");
    }
});

// Reference control characters in various representations
stdout.write(`${BEL}`); // "\x07"
stdout.write(BEL.toString()); // "\x07"
stdout.write(Buffer.from([BEL.toHex()])); // 7 (0x07)
stdout.write(BEL.toLiteral()); // "\\x07"
stdout.write(BEL.toCharacter()); // "G"
stdout.write(BEL.toCaret()); // "^G"

// ... and more :)

Comparison & Differences

In general

  • For functions that just return strings, we use the exact same implementation and return the same string.
  • For anything else, we try to match the Go implementation, but how that is achieved can sometimes not be identical, just from the language differences. For example, Go's runes vs. built-in iterators & Grapheme handling vs Intl.Segmenter.
  • I took a little bit of liberty in a few places, sometimes to make it more JS-idiomatic, but unless it's specifically marked as different, the functionality offered should be the same, with maybe a slightly different API.
  • We use pascalCase, they use CamelCase. That said, the naming is still a bit iffy, might need some tweaking / changes.
  • VT100-related comments are identical.

Missing features

  • Graphics ([x/ansi/graphics.go] (https://github.com/charmbracelet/x/tree/main/ansi/graphics.go))
  • iTerm2 (x/ansi/iterm2)
  • Kitty (x/ansi/kitty)
  • Sixel (x/ansi/sixel)
  • Tests are missing almost completely.

Feature (un)parity

Features that are not implemented the same way.

  • String width In many of Go's string processing functions, you can set a method to measure the width. The difference between WcWidth and GraphemeWidth is, as far as I can see, in which package is used to determine the width:

    It seems like they both try to accomplish the same thing, but in different ways, with the uniseg library being property-based, grapheme-aware and more modern.

    So far, we only implemented WcWidth - mainly because it was easier. In the test suite, you can see that it counts 🇸🇦 as 1, not 2 cells wide like GraphemeWidth.

    I'm not sure how many other symbols that might be the case for, nor if I understand the whole Unicode thing correctly yet. Let me know if something is off! :)

  • Wrapping

    As mentioned in the point above, we have only implemented the WcWidth method so far, so it's not possible to choose another string width method.

  • Truncating

    We do not use the parser to do this yet, but rather rely on a third party package.

  • Parser (x/ansi/parser)

    The parser is fundamentally different, though it should work and achieve the same. We're using the excellent [@ansi-tools/parser] (https://www.npmjs. com/package/@ansi-tools/parser)!

  • Color (x/ansi/color)

    x/ansi uses the native Go color type and github.com/lucasb-eyer/go-colorful to parse and convert colors. We use @thi.ng/color under the hood.

Contributing

Contributions are welcome! Please feel free to submit issues and pull requests.

License

MIT License

This project is a TypeScript port of charmbracelet/x/ansi. See LICENSE.md for full licensing information.

Acknowledgments