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

terlio.js

v1.2.1

Published

Composable terminal UI for commands, streams, and interactive workflows

Readme

Terlio.js

Terlio.js is a dependency-free declarative terminal UI framework for Node.js. It combines a fixed-frame renderer, ANSI-aware layout, reusable workspace components, normalized keyboard input, overlays, command palettes, scroll state and structured assistant output in one plain-JavaScript package.

Use it for full-screen CLI applications, AI and agent consoles, internal tools, support desks, review workflows, command centers and streaming interfaces that need more structure than sequential console.log() output.

Install

Create or open a Node.js project, then install Terlio.js:

npm install terlio.js

Terlio.js requires Node.js 18 or newer and uses ES modules.

First render

import { Box, Row, Text, renderToString } from 'terlio.js';

const screen = Box(
  { border: true, padding: 1, title: ' Status ' },
  Text('Terlio.js is ready.'),
  Row({ gap: 2 }, Text('tests: passed'), Text('provider: mock')),
);

console.log(renderToString(screen, { width: 48, height: 8 }));

Run packaged examples

After installing terlio.js in the current Node.js project, use its bundled launcher:

npx terlio.js examples
npx terlio.js demo:chat
npx terlio.js demo:support-desk
npx terlio.js example:palette
npx terlio.js example:themes
npx terlio.js example:long-text
npx terlio.js example:syntax
npx terlio.js example:components

The examples command prints the complete grouped catalog, including example:long-text and example:syntax. Interactive examples require a real TTY. example:syntax and example:components are one-shot stdout examples and can be used in CI or redirected to a file.

The launcher accepts short names too:

npx terlio.js chat
npx terlio.js palette
npx terlio.js components

Highlights

  • Declarative primitives: Text, Box, Row, Column, Panel, Grid and SplitPane.
  • Product-level composition: WorkspaceShell, WorkspacePane, WorkspaceFooter, Docked and RequireViewport.
  • Efficient rendering through fixed virtual frames, viewport-only long-text formatting, input batching and row-level ANSI patches.
  • Stateful interaction helpers for lists, scrolling, focus, modes, text editing and command palettes.
  • Pointer input with SGR 1006 mouse reporting, wheel/touchpad events, clicks, coordinates and component hit-testing.
  • Blocking modals and confirmations, non-blocking toast overlays, and bottom-anchored popups that do not consume layout rows.
  • Unicode-aware measurement and clipping for emoji, CJK and styled ANSI text.
  • Structured response blocks for text, code, diffs, commands, warnings and tool results.
  • Opt-in zero-dependency syntax highlighting for common JavaScript, Python, C/C++, Swift, Objective-C and configuration files.
  • No runtime dependencies; Node.js built-ins only.

Secure defaults

Terlio.js treats rendered text, pointer metadata, terminal input, clipboard access and persisted sessions as separate boundaries. Untrusted text cannot emit arbitrary terminal controls, pointer regions never come from displayed text, bracketed paste remains a text transaction, and terminal state is restored when managed runtimes stop or fail. Raw ANSI, OSC 52 fallback and process-global handlers require explicit opt-in.

See Security model for the complete behavior and configuration options. Projects upgrading from an older release can use the separate 1.2.0 migration guide.

Repository development

npm ci
npm run check
npm test
npm run test:coverage
npm run test:package

npm run test:package creates the actual npm tarball, installs it into a clean temporary consumer project, imports terlio.js, lists the packaged examples and runs the one-shot component example.

npm run test:coverage runs the black-box suite through the public API and enforces at least 80% line, branch, and function coverage for src/lib.

Repository-only npm aliases are also available. Run npm run examples for the same grouped catalog used by the packaged launcher.

npm run examples
npm run demo:chat
npm run demo:support-desk
npm run demo:code-review
npm run example:editor
npm run example:palette
npm run example:stream
npm run example:long-text
npm run example:syntax
npm run example:kit
npm run example:keys
npm run example:themes
npm run example:blocks
npm run example:components

Interactive app runtime

For product-style applications, createWorkspaceApp() owns terminal lifecycle, raw input, resize handling, cleanup and invalidation:

import {
  Text,
  WorkspacePane,
  WorkspaceShell,
  createWorkspaceApp,
} from 'terlio.js';

const state = { count: 0 };

const app = createWorkspaceApp({
  title: 'Counter',
  state,
  render: ({ width, height }) => WorkspaceShell({
    title: 'Counter',
    subtitle: 'Press ↑ or ↓',
    main: WorkspacePane({
      title: ' Value ',
      active: true,
      children: [Text(`Count: ${state.count}`)],
    }),
    height,
  }),
  onKey: ({ key, invalidate }) => {
    if (key.name === 'up') state.count += 1;
    if (key.name === 'down') state.count -= 1;
    invalidate();
  },
});

app.start();

The render context also includes a demand-driven animationFrame, suitable for Spinner({ frame: animationFrame }). The runtime starts its animation clock only when the current render reads animationFrame (or calls requestAnimationFrame()), and suspends it as soon as the render becomes static. Configure the active cadence with animationMs, or set it to 0 to disable animation entirely.

Pointer input

createWorkspaceApp() can own mouse reporting together with raw keyboard input. In the default pointer: 'auto' mode, Terlio enables SGR 1006 reporting when the rendered tree contains an auto-enabling pointer region or the runtime has a global onPointer handler, then disables it during cleanup.

import { ScrollPane, createWorkspaceApp, scrollBy } from 'terlio.js';

const state = {
  lines: Array.from({ length: 100 }, (_, index) => `row ${index + 1}`),
  scroll: 0,
};

const app = createWorkspaceApp({
  state,
  render: ({ height }) => ScrollPane({
    title: ' History ',
    lines: state.lines,
    height,
    scroll: state.scroll,
    pointerId: 'history',
    pointerAutoEnable: false,
    onWheel: (event) => {
      const max = Math.max(0, state.lines.length - height + 3);
      state.scroll = scrollBy(state.scroll, event.deltaY, max);
    },
    onClick: (event) => {
      // event.localY can be mapped to a custom scrollbar thumb or track.
    },
  }),
});

app.start();

Pointer events expose zero-based screen coordinates as x and y, original one-based terminal coordinates as column and row, wheel deltas, modifiers, button information, and hit-tested target, currentTarget, localX, and localY fields. Touchpad scrolling is reported by terminals through the same wheel-up, wheel-down, wheel-left, and wheel-right events.

Set pointerAutoEnable: false on a passive region when its handlers should remain registered without enabling global mouse reporting by themselves. Such regions become active whenever another component enables pointer reporting or the application temporarily forces it with app.setPointerOverride(true).

For text that must stay selectable while wheel input remains active, use SelectableText or ScrollPane({ selection }). Dragging creates an in-app selection in content coordinates, so it remains visible and valid while the pane scrolls. Clicking inside the highlighted range invokes onCopy; a successful copy clears the selection, while a failed copy keeps it for retry. Clicking elsewhere clears the selection without copying. Ctrl+C always keeps its terminal meaning (SIGINT). Ctrl+T remains an exceptional fallback for temporarily disabling mouse reporting and using the terminal emulator's native selection.

ScrollPane virtualizes line preparation: only rows inside the current viewport are converted, ANSI-clipped and rendered. A 10,000-row array therefore has the same per-wheel formatting cost as a short source with the same viewport height. For generated data, lines may also be an array-like object or a line source created with createTextLineSource(). Run npx terlio.js example:long-text to exercise wheel scrolling and multi-viewport selection against 10,000 rows.

Any node can define onPointer, onClick, onWheel, onDrag, onMove, or onRelease. Use PointerRegion() to wrap a component that does not expose pointer props directly. Call app.togglePointerOverride() or app.setPointerOverride(true | false | null) to temporarily override automatic ownership without changing the configured pointer preference.

Syntax highlighting

Terlio.js includes an opt-in zero-dependency lexical highlighter. Use SyntaxText for a component, highlightSyntax() for ANSI output, or enable highlighting in structured code blocks:

import { createBlock, renderBlockLines, themes } from 'terlio.js';

const block = createBlock({
  type: 'code',
  filename: 'server.js',
  syntaxHighlight: true,
  content: 'export const port = 3000;',
});

const lines = renderBlockLines({ block, width: 72, theme: themes.ocean });

You can also pass syntaxHighlight: true to renderBlocksLines(), ChatScreen, createChatScreen() or RichTerminalApp. Explicit language names, common aliases, filenames and shebangs are supported. Run npx terlio.js example:syntax for a packaged snapshot, and see Syntax highlighting for the supported file types and theme tokens.

Bottom-anchored overlays

BottomOverlay places a non-modal surface over the bottom portion of an existing frame without reducing the space assigned to the underlying content. Pointer regions rendered inside the overlay participate in normal hit-testing and take precedence over covered regions, while uncovered background components remain interactive.

import { BottomOverlay, Box, Column, Text } from 'terlio.js';

const screen = BottomOverlay({
  content: Column(
    Text('Transcript row 1'),
    Text('Transcript row 2'),
    Text('Transcript row 3'),
  ),
  overlay: Box(
    { border: true, pointerId: 'autocomplete' },
    Text('› /help', { pointerId: 'autocomplete:help', onClick: acceptHelp }),
    Text('  /theme', { pointerId: 'autocomplete:theme', onClick: acceptTheme }),
  ),
  height: terminalRows,
  bottom: composerHeight,
});

Use bottom to reserve fixed chrome below the popup, and left, right, width, and align to position narrower surfaces. Closing the overlay returns to the normal row-diff renderer; no full-screen repaint is required. The packaged chat demo uses this component for slash-command autocomplete, so suggestions physically cover the latest transcript rows instead of shrinking the transcript viewport.

Documentation

Package exports

The public API is exported from the package root:

import { TerminalRenderer, WorkspaceShell, parseKey } from 'terlio.js';

Examples can also be imported explicitly for inspection or testing:

import { createComponentsShowcaseView } from 'terlio.js/examples/components-showcase';

Use the terlio.js CLI rather than importing an interactive example when the goal is to run it.

License

MIT

Terminal security

Terlio.js renders public text with the safe terminal policy by default. Arbitrary CSI, OSC, DCS, APC, PM, SOS and C1 controls are blocked at both layout and terminal-sink boundaries, while validated SGR styling remains available. See Safe terminal rendering.

Pointer hit regions are stored as structured layout metadata rather than encoded in rendered text. Geometry is validated at the frame boundary, overlays retain structural z-order, and each frame has a configurable region limit. See Pointer metadata isolation.

The security behavior is covered by the ordinary test suite and the focused checks described in Security contract testing. The complete trust boundaries and configuration options are documented in Security model.