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

@pompidup/cligrid

v2.0.0

Published

A powerful library designed to provide a flexible and extensible system for managing and rendering terminal user interfaces.

Readme

@pompidup/cligrid

npm license

A terminal UI framework for building responsive, interactive, component-based CLI applications in TypeScript.

Changelog

Features

  • Component-based architecture — build UIs with reusable, composable components
  • Built-in components — TextBox, SelectList, ProgressBar, InputField, Table, Spinner, Tabs, Modal, Checkbox
  • Functional componentscreateComponent() factory, no class boilerplate needed
  • Responsive layout — percentage and fixed sizing, relative positioning, flex layout (row/column) with gap, justify, alignItems
  • Min/Max constraintsminWidth, maxWidth, minHeight, maxHeight for responsive layouts
  • Styling — borders (single/double/rounded), colors (named + hex), padding, bold/dim/underline/italic/strikethrough/inverse
  • Styled segments — multiple styles within a single line via segments (StyledSegment[])
  • Text alignmentalign: "left" | "center" | "right" per line
  • Text overflow — hidden, ellipsis, wrap, wrap-word modes (segment-aware)
  • Unicode-aware — correct rendering of emoji, CJK characters, and zero-width joiners
  • Keyboard input — focus management (Tab/Shift+Tab), event bubbling, declarative onKeyPress
  • Mouse support — click, scroll, hover detection with mouseenter/mouseleave events
  • Scrolling — vertical and horizontal scrolling with keyboard control and visual indicators
  • Overlays — z-index layering, showOverlay()/hideOverlay() for modals
  • Reactive state — app-level state store with setState()/connect() for automatic re-renders
  • Animation system — built-in app.animate() with easing functions (linear, easeIn, easeOut, bounce, elastic)
  • Color utilitiesgradient(), lighten(), darken(), mix() for programmatic color manipulation
  • Theme system — design tokens (primary, danger, surface, etc.) with dark/light presets
  • Focus visiblefocusStyle for automatic visual focus indicators
  • Lifecycle hooksonMount, onDestroy, onResize
  • Performance — double-buffered rendering, diff-based updates, partial re-renders on prop changes

Installation

npm install @pompidup/cligrid
# or
yarn add @pompidup/cligrid
# or
pnpm add @pompidup/cligrid

Quick Start

import { App, createComponent } from "@pompidup/cligrid";

const app = new App({ alternateScreen: true });

const hello = createComponent({
  id: "hello",
  position: { x: 0, y: 0 },
  width: "100%",
  height: 3,
  margin: 0,
  style: {
    border: { style: "rounded", fg: "cyan" },
    fg: "cyan",
    padding: { left: 2 },
  },
  props: {},
  render: () => "Hello from cligrid!",
});

app.add(hello);

app.onKey("ctrl+c", () => {
  app.stop();
  process.exit(0);
});

app.start();

Built-in Components

TextBox

Displays text with optional borders, padding, and overflow handling.

import { TextBox } from "@pompidup/cligrid";

const box = new TextBox({
  id: "info",
  position: { x: 0, y: 0 },
  width: "50%",
  height: 5,
  props: { text: "Hello\nWorld" },
  style: { border: { style: "single" }, overflow: "wrap-word" },
});

SelectList

Navigable list with keyboard support (Up/Down to move, Enter to select).

import { SelectList } from "@pompidup/cligrid";

const menu = new SelectList({
  id: "menu",
  position: { x: 0, y: 0 },
  width: 30,
  height: 6,
  props: {
    items: ["New Game", "Load Game", "Settings", "Quit"],
    selectedIndex: 0,
  },
  style: { border: { style: "rounded" } },
  onSelect: (index, item) => {
    console.log(`Selected: ${item}`);
  },
});

ProgressBar

Horizontal progress indicator.

import { ProgressBar } from "@pompidup/cligrid";

const bar = new ProgressBar({
  id: "loading",
  position: { x: 0, y: 0 },
  width: 40,
  height: 3,
  props: { value: 0.65, label: "Loading" },
  style: { border: { style: "single", fg: "green" } },
});
// Renders: Loading [████████░░░░░░] 65%

InputField

Text input with cursor, placeholder, and callbacks.

import { InputField } from "@pompidup/cligrid";

const input = new InputField({
  id: "name",
  position: { x: 0, y: 0 },
  width: 30,
  height: 3,
  props: { value: "", placeholder: "Enter your name..." },
  style: { border: { style: "single" } },
  onChange: (value) => { /* called on each keystroke */ },
  onSubmit: (value) => { /* called on Enter */ },
});

Table

Data table with column alignment, headers, and vertical scrolling.

import { Table } from "@pompidup/cligrid";

const table = new Table({
  id: "users",
  position: { x: 0, y: 0 },
  width: 60,
  height: 10,
  scrollable: true,
  props: {
    columns: [
      { key: "name", label: "Name", width: 20 },
      { key: "role", label: "Role", width: 15 },
      { key: "status", label: "Status", width: 10, align: "right" },
    ],
    rows: [
      { name: "Alice", role: "Admin", status: "Active" },
      { name: "Bob", role: "User", status: "Idle" },
    ],
  },
  style: { border: { style: "single" } },
});

Spinner

Animated loading indicator with multiple styles.

import { Spinner } from "@pompidup/cligrid";

const spinner = new Spinner({
  id: "loader",
  position: { x: 0, y: 0 },
  width: 30,
  height: 1,
  props: { label: "Loading...", style: "dots" },
  // Styles: "dots" | "line" | "arc" | "bouncingBar"
});

Tabs

Tab navigation with Left/Right keyboard control.

import { Tabs } from "@pompidup/cligrid";

const tabs = new Tabs({
  id: "nav",
  position: { x: 0, y: 0 },
  width: 40,
  height: 1,
  props: {
    tabs: [
      { label: "Home", id: "home" },
      { label: "Settings", id: "settings" },
    ],
    activeTab: "home",
  },
  onTabChange: (id) => { /* switch content */ },
});

Modal

Dialog with title, body, navigable buttons, and overlay support.

import { Modal } from "@pompidup/cligrid";

const modal = new Modal({
  id: "confirm",
  position: { x: 10, y: 5 },
  width: 40,
  height: 10,
  style: { border: { style: "double" } },
  props: {
    title: "Confirm",
    body: "Are you sure?",
    buttons: [
      { label: "OK", action: () => app.hideOverlay(modal) },
      { label: "Cancel", action: () => app.hideOverlay(modal) },
    ],
  },
});

app.showOverlay(modal); // z-index 100, focus trap

Checkbox

Toggle input with Space/Enter keyboard control.

import { Checkbox } from "@pompidup/cligrid";

const checkbox = new Checkbox({
  id: "agree",
  position: { x: 0, y: 0 },
  width: 30,
  height: 1,
  props: { checked: false, label: "I agree to the terms" },
  onChange: (checked) => { /* handle toggle */ },
});

Layout & Positioning

Fixed and Percentage Sizing

width: 30,          // 30 columns
width: "50%",       // 50% of terminal width
height: "auto",     // computed from content

Min/Max Constraints

minWidth: 20,       // minimum 20 columns
maxWidth: "80%",    // maximum 80% of terminal width
minHeight: 5,
maxHeight: 30,

Relative Positioning

Position components relative to each other:

const sidebar = new TextBox({
  id: "sidebar",
  position: { x: 0, y: 0 },
  width: 24,
  height: "100%",
  // ...
});

const content = createComponent({
  id: "content",
  position: {
    x: { position: "right", relativeTo: "sidebar" },
    y: { position: 0 },
  },
  width: "70%",
  height: "100%",
  // ...
});

Relative positions: "right", "left" (x-axis), "top", "bottom" (y-axis).

Flex Layout

Distribute children automatically in rows or columns:

const row = createComponent({
  id: "row",
  position: { x: 0, y: 0 },
  width: "100%",
  height: 10,
  layout: "row",
  gap: 1,
  justifyContent: "space-between", // "start" | "center" | "end" | "space-between" | "space-around"
  alignItems: "center",            // "start" | "center" | "end" | "stretch"
  children: [
    createComponent({ id: "a", width: 20, height: "auto", /* ... */ }),
    createComponent({ id: "b", width: "100%", height: "auto", flex: 1, /* ... */ }),
  ],
  // ...
});

Styling

style: {
  border: {
    style: "single" | "double" | "rounded" | "none",
    fg: "cyan",        // border color
  },
  fg: "white",         // text foreground (named, "#RRGGBB", or theme token)
  bg: "blue",          // text background
  bold: true,
  dim: true,
  underline: true,
  italic: true,
  strikethrough: true,
  inverse: true,
  padding: { top: 1, right: 2, bottom: 1, left: 2 },
  overflow: "hidden" | "ellipsis" | "wrap" | "wrap-word",
}

Colors: black, red, green, yellow, blue, magenta, cyan, white, bright variants (brightRed, ...), hex ("#FF00FF"), and theme tokens ("primary", "danger", etc.).

Styled Segments

Apply different styles to parts of the same line using segments:

render: () => [
  {
    text: "",
    segments: [
      { text: " INFO ", style: { bg: "blue", fg: "white", bold: true } },
      { text: " Server started on ", style: { fg: "white" } },
      { text: ":3000", style: { fg: "cyan", bold: true } },
    ],
  },
]

Text Alignment

Align text within a component's content area:

render: () => [
  { text: "Left-aligned (default)" },
  { text: "Centered title", align: "center" },
  { text: "Right-aligned value", align: "right" },
]

Interactivity

Focus Management

Register focusable components and navigate with Tab/Shift+Tab:

app.add(input1, true);   // true = focusable
app.add(input2, true);
app.add(label, false);   // not focusable (default)

Use focusStyle for automatic visual focus indicators:

const comp = createComponent({
  id: "panel",
  focusStyle: { fg: "yellow", bold: true }, // applied when focused
  // ...
});

Keyboard Events

Global key handlers:

app.onKey("ctrl+c", () => { app.stop(); process.exit(0); });
app.onKey("ctrl+s", () => { /* save */ });

Component-level key handlers:

const comp = createComponent({
  id: "panel",
  // ...
  onKeyPress: (event, component) => {
    if (event.key === "enter") {
      event.handled = true; // stop bubbling
    }
  },
});

Events bubble up from child to parent until event.handled = true.

Mouse Support

Enable mouse tracking for click, scroll, and hover events:

const app = new App({ mouse: true });

const button = createComponent({
  id: "btn",
  // ...
});

button.on("click", (event) => { /* { x, y, button, type } */ });
button.on("mouseenter", () => { /* hover start */ });
button.on("mouseleave", () => { /* hover end */ });
button.on("scroll", (event) => { /* { direction: -1 | 1 } */ });

Components receive hovered: boolean in RenderContext to adapt their rendering.

Advanced Features

Animation System

Animate component props with easing functions:

import { easeOut, bounce } from "@pompidup/cligrid";

app.animate(progressBar, { value: 1.0 }, {
  duration: 2000,
  easing: easeOut,
  onComplete: () => { /* done */ },
});

// Frame-based callbacks
const unsubscribe = app.tick((dt) => { /* called each frame */ });

Available easings: linear, easeIn, easeOut, easeInOut, bounce, elastic.

Color Utilities

import { gradient, lighten, darken, mix } from "@pompidup/cligrid";

const colors = gradient("#FF0000", "#0000FF", 10); // 10-step gradient
const lighter = lighten("#333333", 0.3);
const darker = darken("#CCCCCC", 0.2);
const blended = mix("#FF0000", "#00FF00", 0.5);

Theme System

Use design tokens for consistent styling across components:

import { darkTheme, lightTheme } from "@pompidup/cligrid";

app.setTheme(darkTheme);

// Use token names as colors
const comp = createComponent({
  id: "panel",
  style: { fg: "primary", bg: "surface", border: { style: "single", fg: "border" } },
  // ...
});

// Switch theme at runtime
app.setTheme(lightTheme);

Tokens: primary, secondary, danger, success, warning, surface, text, border, muted, accent.

Reactive State Store

app.setState({ score: 0, level: 1 });

const display = createComponent({
  id: "score",
  props: { score: 0 },
  render: (props) => `Score: ${props.score}`,
  // ...
});

app.add(display);
app.connect(display, (state) => ({ score: state.score }));

// Later: auto-updates the component
app.setState({ score: 42 });

Scrolling

Vertical and horizontal scrolling with keyboard and mouse support:

const list = createComponent({
  id: "log",
  scrollable: true, // Up/Down for vertical, Left/Right for horizontal
  // ...
});

app.add(list, true); // focusable for keyboard control

// Programmatic scrolling
list.scrollTo(10);     list.scrollBy(5);      // vertical
list.scrollToX(10);    list.scrollByX(5);     // horizontal

Visual scroll indicators (track + thumb) appear when content overflows.

Overlays

app.showOverlay(modal);   // z-index 100, saves previous focus
app.hideOverlay(modal);   // restores focus

Lifecycle Hooks

const comp = createComponent({
  id: "comp",
  // ...
  onMount: () => { /* first render */ },
  onDestroy: () => { /* removed */ },
});

comp.on("resize", (width, height) => { /* dimensions changed */ });

Demos

The package includes 6 runnable demos showcasing the library features:

pnpm demo:dashboard    # Interactive dashboard with navigation menu
pnpm demo:form         # Form with input fields, select list, focus management
pnpm demo:advanced     # State store, scrolling, overlays, live updates
pnpm demo:castle       # ASCII art castle with lightning animation
pnpm demo:segments     # Styled segments, text alignment, segment overflow
pnpm demo:showcase     # v2 showcase: Table, Tabs, Spinner, Modal, Checkbox, themes, mouse

API Reference

Core

| Export | Description | |--------|-------------| | App | Main application orchestrator (manages rendering, input, focus, state, themes, mouse) | | createComponent() | Functional component factory | | Component | Base class for custom components | | Template | Layout manager | | Renderer | Terminal rendering engine |

Built-in Components

| Export | Description | |--------|-------------| | TextBox | Multi-line text display | | SelectList | Navigable list with selection | | ProgressBar | Horizontal progress indicator | | InputField | Text input with cursor | | Table | Data table with column alignment and scroll | | Spinner | Animated loading indicator | | Tabs | Tab navigation with keyboard control | | Modal | Dialog with buttons and overlay support | | Checkbox | Toggle input |

Animation & Color

| Export | Description | |--------|-------------| | Animator | Animation engine | | linear, easeIn, easeOut, easeInOut, bounce, elastic | Easing functions | | gradient(), lighten(), darken(), mix() | Color manipulation | | darkTheme, lightTheme, resolveThemeColor() | Theme system |

Utilities

| Export | Description | |--------|-------------| | stylize() | Apply ANSI styles to text | | fgCode() / bgCode() | Get ANSI color codes | | stringWidth() | Unicode-aware string width | | InputManager | Low-level keyboard and mouse input | | FocusManager | Focus navigation | | ScreenBuffer | Double-buffered screen |

Types

Style, ComponentConfig, RenderContext, RenderOutput, RenderLine, StyledSegment, TextAlign, Position, Size, KeyEvent, MouseEvent, Theme, TerminalDimensions, StyleAttrs

License

MIT