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

if-automap

v0.1.1

Published

A Trizbort-style live auto-map for interactive fiction: direction-true layout, region tabs, labelled passages, dashed unexplored exits. Extracted from zork-ui.

Readme

if-automap

npm CI license

A Trizbort-style live auto-map for interactive fiction shells. Hand it a room graph, feed it discover() events as the player moves, and it draws the map the way players drew them by hand in 1980: labelled boxes on graph paper, direction-true layout, dashed trails at exits not yet taken, arrowheads on one-way passages, stair marks, and region tabs with labelled passages between regions.

Live demo (a scripted walk through Zork I's world data) and the real thing at zork.arobase.co, where it maps the Great Underground Empire against an untouched Z-machine. Zero dependencies, ~9 kB gzipped, TypeScript types included.

if-automap rendering Zork's Frigid River region

Install

npm install if-automap

ESM-only, shipped unminified with source maps; your bundler does the rest. (No build step? import { createAutomap } from "https://esm.sh/if-automap" works.)

Use

import { createAutomap } from "if-automap";

const map = createAutomap(document.getElementById("map"), {
  rooms: {
    "FIELD": { id: "FIELD", name: "Open Field", exits: { EAST: { to: "FOREST" }, DOWN: { to: "CAVE" } } },
    "FOREST": { id: "FOREST", name: "Forest", exits: { WEST: { to: "FIELD" } } },
    "CAVE": { id: "CAVE", name: "Cave", exits: { UP: { to: "FIELD" } } },
  },
  saveKey: "mygame:map",           // omit or null for no persistence
  onGo: (dir) => game.submit(dir), // tap-to-walk from the exits panel
});

map.discover("FIELD"); // call as the player moves; the map is a diary

The container just needs a height.

Styles don't leak, either direction

By default the widget renders inside a shadow root. Its stylesheet is adopted by that root and never touches your document; your page's CSS (resets, button { } rules, utility frameworks) never touches the widget. Mounting adds zero stylesheets and zero classes to your document.

You still control the look from outside:

  • CSS custom properties pierce the boundary: set --automap-font or --automap-scrollbar on the container (or anywhere above it).
  • ::part() hooks for structural theming: #map::part(head), ::part(tabs), ::part(stage), ::part(legend), ::part(info), ::part(tip).

If you would rather style everything directly with plain CSS, opt out with isolation: "light", the widget then renders in your DOM with a single class-scoped <style data-automap> in the head (all selectors live under .automap; injected once no matter how many maps you mount).

Regions

Big worlds rarely fit one coherent sheet (a cellar's geometry should never tug on the house above it). Split the world and each region lays out independently, with tabs and labelled stub arrows at every passage between them, labelled only once the player has actually been through:

createAutomap(el, {
  rooms,
  sections: [
    { id: "SURFACE", name: "SURFACE", anchor: "FIELD" },
    { id: "CAVES", name: "CAVES", anchor: "CAVE" },
  ],
  regions: { FIELD: "SURFACE", FOREST: "SURFACE", CAVE: "CAVES" },
  portals: [["FIELD", "DOWN", "CAVE"]], // passages your engine resolves in code
});

API

createAutomap(el, options) returns the instance:

| method | purpose | | --- | --- | | discover(roomId, items?) | register the player entering a room (optionally with visible item names) | | visited() | Set of discovered room ids | | refresh() | re-render (call after resizing the container) | | dump() | layout oracle: placed nodes, live stub hit-rects, active section | | destroy() | stop the heartbeat, remove listeners |

Options: rooms (required), sections, regions, portals, saveKey, storage, onGo, title, heartbeatMs (auto-disabled under prefers-reduced-motion). Directions understood: N/S/E/W, NE/NW/SE/SW, IN, OUT, LAND, UP, DOWN (stairs flatten onto the sheet as diagonals).

The layout engine is invariant-tested against the full 110-room Zork I graph (shuffled discovery orders, random subsets, random walks) in the zork-ui repository's test suite.

License

MIT