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

@justai/pano

v0.1.1

Published

Moving a Street View panorama the way a head moves — the short way round, with weight at both ends, and arriving on foot.

Readme

@justai/pano

Moving a Street View panorama the way a head moves — the short way round, with weight at both ends, and arriving on foot rather than appearing.

Lifted from City in Motion's CMS, where the playlist editor had it right and had it welded into an eight-hundred-line React component. It carries no key, loads no script, and knows nothing about Google: it moves whatever panorama it is handed.

Why it is a package

Six of these questions stopped belonging to one product the moment a second one needed them:

| subpath | the question | |---|---| | turn | which way should a camera rotate, and with what weight | | geo | which way is that place from here, and how far | | timing | how long each move takes — tuned by eye against a real city | | load | get Google's script onto the page once, whoever asks first | | camera | move a live panorama's pose smoothly, and know when it has landed | | walk | how to get from this panorama to that one along the link graph | | pano | what a panorama IS, structurally — so the rest can be checked alone |

The trigger was the ecosystem's own: a second consumer reaching for it, and the tell that you are about to copy rather than to import. guess-back wanted shortestDelta, easeInOutCubic and tweenPov verbatim, and the pano-graph walk is the household's stated differentiator — so a copy would have been O(N) from its first day.

The panorama is a structure, not a class

google.maps.StreetViewPanorama satisfies the Pano interface with no adapter and no cast, and nothing here imports a Google type. That is what lets the camera and the walk be checked alone: the test suite drives them against a forty-line fake with no key, no network and no browser, and builds three little graphs to prove the walk's three refusals —

  • a link pointing away from the target is refused rather than followed;
  • two panoramas pointing at each other do not make an infinite corridor;
  • and after the flourish, the target is set outright, because the walk is not the transport.

The failure that has no error

fires when a request FAILS. It does not fire when a request SUCCEEDS with an empty body — which is what an ad blocker, a corporate proxy or a test harness intercepting third-party hosts does: 204, no content, no error. The script "loads", google.maps is never defined, the callback never runs, and a loader without a ceiling stays pending forever. Nothing throws and nothing logs; the page simply never opens.

loadMaps therefore has a timeout, not as belt and braces but as the guard that catches the common case, and a rejected load clears itself so the next attempt is a real attempt.

A heading always comes back as a compass reads it

shortestDelta returns a signed rotation, so adding it to a heading can land on -160 or 370. Both are correct angles; neither is a bearing. A live panorama normalises and reports [0, 360), so the camera does too — every frame, not just the last. Without it a consumer that clamps a heading behaves differently against a real panorama than against a fake, which is the kind of difference that is found in production. It was found here by the package's own test.

Every duration is a dial

timing exports named constants rather than hiding numbers in the functions, because those numbers are not defaults in the sense of something had to go here — they are what the founder arrived at watching Leiden go past. They travel with the package so the next persona does not re-guess them, and any of them can be passed per call.

Usage

import { goTo } from "@justai/pano/walk";
import { TURN_IN_PLACE_MS } from "@justai/pano/timing";

// turn to face what is behind you — the short way, over 1.4s
await goTo(panorama, { ...pose, heading: (pose.heading + 180) % 360 });