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

kitty-keyboard

v0.1.0

Published

DOM-like keyboard events for the terminal over the Kitty keyboard protocol, with a keydown-only legacy fallback. Zero dependencies.

Readme

kitty-keyboard

DOM-like keyboard events for the terminal over the Kitty keyboard protocol, with a keydown-only legacy fallback. Zero runtime dependencies.

Terminals normally hand a program a stream of raw bytes on stdin. You get a keydown for a printable character and nothing else. No key release, no auto repeat, and modifier-only presses are invisible. The Kitty keyboard protocol fixes that. On a terminal that speaks it (kitty, ghostty, foot, WezTerm) you get real keyup events, key-repeat events, the full modifier set (Ctrl, Shift, Alt, Meta/Super/Hyper), and the associated text a key produced. On terminals without it, the library degrades to keydown-only events parsed from the legacy byte stream, so your code runs everywhere.

Install

npm install kitty-keyboard

Quick start

import { createTerminalKeyboard } from "kitty-keyboard";

const keyboard = await createTerminalKeyboard();
const held = new Set<string>();

keyboard.on("keydown", (event) => {
  // Ctrl-C arrives as a key event, not a signal, so handle exit yourself.
  if (event.key === "q" || (event.ctrlKey && event.key.toLowerCase() === "c")) {
    keyboard.dispose();
    process.exit(0);
  }
  held.add(event.code);
  console.log(`down ${event.key} (${event.code}) held: ${[...held].join(", ")}`);
});

keyboard.on("keyup", (event) => {
  held.delete(event.code);
});

if (!keyboard.protocolEnabled) {
  console.log("legacy terminal: keydown only, no keyup or repeat");
}

keyboard.protocolEnabled tells you whether the enhanced protocol is active. It is true only on a real TTY whose terminal supports the protocol.

Events

Each listener receives a TerminalKeyboardEvent, shaped after the DOM KeyboardEvent.

  • type - "keydown" or "keyup".
  • key - the typed value, for example "a", "A", "ArrowUp", or "Enter".
  • code - the physical key, for example "KeyA", "ArrowUp", or "ShiftLeft". Stable across keyboard layouts.
  • repeat - true when the event is an auto-repeat (protocol terminals only).
  • ctrlKey, shiftKey, altKey, metaKey - whether each modifier was held. Super and Hyper collapse into metaKey, matching the DOM.
  • getModifierState(name) - per-modifier query for "Shift", "Control", "Alt", "Meta", "Super", "Hyper", "CapsLock", and "NumLock".

Use key when you care about the character the user typed and code when you care about the physical key position (for example WASD or arrow movement).

Graceful degradation

The parser handles both the disambiguated Kitty form and legacy byte sequences, so keydown events arrive on every terminal. Keyup and key-repeat events arrive only where the protocol is active. Check keyboard.protocolEnabled to decide which model to use.

  • Protocol on: track held keys with a Set, add on keydown, remove on keyup, and drive continuous motion from the held set.
  • Protocol off: no keyup ever arrives, so treat each keydown as one discrete key press and act on it immediately.

Terminal state

Constructing a keyboard puts stdin into raw mode and, when the protocol is supported, writes the enable sequence. Call keyboard.dispose() to detach the input listener, pop the protocol flags, and restore stdin to its prior raw and paused state. dispose() is idempotent.

There is no auto-dispose. The app owns its exit, including Ctrl-C, which arrives as a key event (event.ctrlKey with event.key === "c") rather than a signal. Handle it in your keydown listener, call dispose(), then exit.

TerminalKeyboard implements Symbol.dispose, so a using binding restores the terminal when the scope ends.

using keyboard = await createTerminalKeyboard();
// ... raw mode is restored automatically when this scope exits

API

  • TerminalKeyboard / createTerminalKeyboard() - the event source. Prefer the async factory, which runs the support probe before constructing.
  • TerminalKeyboardEvent - the DOM-shaped event passed to listeners. toKeyboardEvent() maps a RawKeyEvent to one directly.
  • detectKittyKeyboardSupport() / getKittyKeyboardSupported() / resetKittyKeyboardDetection() - the protocol support probe trio.
  • parseKeyEvents() - the pure byte-to-event parser, usable on its own.
  • buildKittyKeyboardEnableSequence() / buildKittyKeyboardDisableSequence() / buildKittyKeyboardQuery() - protocol escape-sequence builders, plus the KITTY_KB_* and KITTY_KEYBOARD_FLAGS_FULL flag constants.

Further reading