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

@frixaco/letui

v0.5.1

Published

TUI library with a Rust rendering core and TypeScript wrapper API. Written from scratch.

Readme

LeTUI

TUI library with a Rust rendering core and TypeScript wrapper API. Written from scratch.

Demos

Snake

https://github.com/user-attachments/assets/31f3efcc-0c2b-4b85-8fe1-99129e6b8394

Typing Speed

https://github.com/user-attachments/assets/7f4f53f5-deb6-4781-bdfa-bf769cabfbb1

Letty Agent (lty)

Letty Agent moving through connection, streaming, and tool states

Letty Agent is a small, local coding agent built entirely with LeTUI. It uses Codex app-server for ChatGPT subscription authentication, agent execution, and durable local thread history. Letty keeps only its current UI projection and model preferences. There are no API keys, cloud threads, credits, or remote infrastructure.

# Requires Codex CLI 0.157.0+ on PATH.
bun run lty

Press Ctrl+O for commands, Ctrl+S to cycle reasoning effort, Alt+T to expand tool details, @ to mention files, or @@ to reference a previous local thread. The footer reports the Codex connection and five-hour subscription window instead of API cost or credit data. See the Letty Agent guide for architecture, setup, scope, and verification.

Prerequisites

  • Runtime: Bun 1.3+ (Node.js not supported)
  • Prebuilt binaries: darwin-arm64, linux-x64, win32-x64
  • Rust toolchain if building locally

Quick start

git clone https://github.com/frixaco/letui.git
cd letui
bun install
bun run build-ffi

More examples:

bun run examples/typing-speed.ts

(bun run anitrack is for personal testing and requires mpv player configured with Anime4K shaders)

Checks:

bun run typecheck
bun run check:rust

Install as a library

bun add @frixaco/letui

On supported targets, install pulls the matching native binary automatically.

Minimal reactive app:

import { $, Column, Text, appearance, ff, onKey, run } from "@frixaco/letui";

const THEME = {
  fg: 0xf5f7fa,
  muted: 0x94a0b2,
  surface: 0x16181a,
  border: 0x3c4048,
} as const;

const count = $(0);
const counterText = Text({
  text: "count: 0",
  foreground: THEME.fg,
});

ff(() => {
  counterText.setText(`count: ${count()}`);
});

const root = Column(
  {
    flexGrow: 1,
    gap: 1,
    padding: "1 1",
    background: THEME.surface,
    border: { color: THEME.border, style: "rounded" },
  },
  [
    Text({ text: "hello from letui", foreground: THEME.fg }),
    counterText,
    Text({
      text: "+ / - update, q quit, Ctrl+Q default quit",
      foreground: THEME.muted,
    }),
  ],
);

ff(() => {
  const mode = appearance();
  root.setStyle({
    background: mode === "light" ? 0xffffff : THEME.surface,
  });
});

const app = run(root);

onKey("+", () => count(count() + 1));
onKey("-", () => count(count() - 1));
onKey("q", () => app.quit());
bun run app.ts

How it works

  1. Signals-based TypeScript runtime drives updates
  2. Each reactive frame snapshots the current node tree into JS-side sent state
  3. If node shape stays compatible, JS sends only style deltas plus text ops; if shape changes, Rust tree state is rebuilt once
  4. Rust keeps persistent tree state, runs layout + paint, and owns the terminal buffers
  5. Frame data is synced back to JS nodes for frame / frameWidth() / frameHeight(), while Rust also exposes the final visible hitmap for interaction
  6. Terminal output is cell-based and incremental; flush only writes changed cells

Architecture

  • TypeScript — component API, signals, input routing, sent-tree diffing, op encoding
  • Rust — persistent tree state, style/text op application, layout, paint, incremental flush
  • Bun FFI — bridge for op buffers, frame buffers, and lifecycle hooks
  • Packaged native binaries for darwin-arm64, linux-x64, win32-x64
  • TypeScript runtime deps: none. Rust deps: crossterm, taffy, unicode-width, and unicode-segmentation.

Text wrapping, clipping, and overflow are resolved in the Rust renderer. Explicit newlines are treated as hard row boundaries after text normalization.

Vertical scrolling is available on ScrollView:

const viewport = ScrollView(
  {
    flexGrow: 1,
    minHeight: 0,
    scrollY: 12,
  },
  [content],
);

ScrollView always scrolls vertically. scrollY is a row offset; Rust clamps oversized values, floors fractional values to whole rows, and owns the final hit-testing for the visible scrolled region.

Debug metrics split the frame into js, render, sync, and flush, plus a worst-frame breakdown. Enable with run(root, { debug: true }) to print the summary on quit. If you also want a file, pass run(root, { debug: true, metricsPath: "dump/metrics.txt" }).

Appearance detection requests the current terminal color scheme at startup and listens for live theme updates on terminals that support DEC 2031. Startup detection asks for DEC color-scheme status first and also sends an OSC 11 background-color query as a fallback. appearance() returns "light", "dark", or "unknown".

import { Column, Text, appearance, ff, onKey, run } from "@frixaco/letui";

const label = Text({ text: "theme: unknown" });
const root = Column({ flexGrow: 1, padding: "1 1" }, [label]);

ff(() => {
  const mode = appearance();
  label.setText(`theme: ${mode}`);
  root.setStyle({
    background: mode === "light" ? 0xf6f6f6 : 0x101215,
  });
  label.setStyle({
    foreground: mode === "light" ? 0x111111 : 0xf5f7fa,
  });
});

const app = run(root, { appearance: "auto" });
onKey("q", () => app.quit());

Pass appearance: "light", "dark", or "unknown" to run() to override detection.

Benchmarks

Generally speaking it's on par if not faster than OpenTUI, Ink and probably any other popular TUI library at its current state. It loses to Ratatui though, as it is pure Rust.

Docs

TODO

  • [x] All essential features except ones below
  • [x] Text styling with StyledText spans
  • [x] Text wrap, overflow, clipping, and explicit newline layout in the renderer
  • [x] Persistent Taffy tree
  • [x] Vertical scrolling with ScrollView
  • [x] Minimal theming support with startup detection and DEC 2031 live updates
  • [ ] Full grapheme rendering support: store/render whole grapheme strings per lead cell instead of a single codepoint
  • [ ] Full cursor-based Input editor: mid-buffer insertion/deletion, selection, multiline navigation, caret-following scroll, shortcuts, and placeholder rendering
  • [ ] Add configurable multiline submission: submit on Return while preserving Ctrl+J or Shift+Enter for newline
  • [ ] Normalize keyboard and paste events: structured keys/modifiers, bracketed paste, and terminal-independent escape-sequence handling
  • [ ] Add ScrollView conveniences: optional scrollbar, follow-end mode, scroll anchoring, and post-layout scrolling
  • [ ] Expose renderer-owned text measurement: visual row count, wrap boundaries, source ranges, and styled-text width
  • [ ] Complete overlay support: top/left insets, explicit layer ordering, overlay hosts, and modal focus containment
  • [ ] Expand border styling: custom glyph sets, heavy/double styles, embedded labels, styled segments, and wrapped-text gutters
  • [ ] Safer quit/cleanup when used as a library
  • [ ] Experiment: Neovim as text input via a Bun-compatible PTY workflow
  • [ ] Refactor flush with BatchWriter pattern
  • [ ] Performance stats overlay

Releasing

See docs/releasing.md.