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

@gliwy/input

v0.1.0-preview.1

Published

Optional Gliwy input and application-owned camera controls

Readme

Optional input and orbit camera

@gliwy/input is an experimental, private first-party module. It has no package, renderer, lifecycle, service, player or game dependency. The portable entry exports createOrbitCamera; @gliwy/input/browser exports createInput and createOrbitControls. Browser and native qualification are separate. Native DOM input is unsupported. These are provisional APIs, not stable save formats.

Application-owned composition

import {createOrbitCamera} from '@gliwy/input';
import {createOrbitControls} from '@gliwy/input/browser';

const camera = createOrbitCamera({distance: 5, minDistance: 1, maxDistance: 20});
// App owns this focusable overlay above its renderer container.
const controls = createOrbitControls({
  target: document.querySelector('#input-surface'),
  camera,
  onChange: async (desiredState, transform) => {
    await app.submit([{op: 'setTransform', id: 'camera', transform}]);
    // Only an actual successful receipt means the renderer is synchronized.
  },
  onError: error => showPreviewError(error),
});
// Spawn the app's camera from camera.transform() before attaching input.
controls.attach();
// Before stopping/replacing the application or removing the overlay:
controls.detach();
await controls.settled();
await app.stop();

The target must have tabindex="0", an accessible label, explicit dimensions, and touch-action: none when using direct-touch manipulation. The application owns that element and its styling; the module does not alter them. A transparent overlay prevents the renderer iframe from taking scene-input focus. Pointer and keyboard events inside an iframe do not bubble into its parent's input target. There is no private canvas, renderer handle or cross-frame event forwarding.

Input begins idle. attach() and detach() are repeatable. createInput can be used independently of the camera: configure 1–32 unique KeyboardEvent codes, then inspect snapshot() or consume accumulated deltas with read(). It owns one captured pointer, at most 32 held keys, six button/Shift drag buckets and two wheel totals, rather than an unbounded event queue. Each accumulated axis is clamped to maxDelta (default 2048 CSS pixels). Pointer capture is released and all listeners removed on detach; failed attachment rolls back acquired listeners.

Keyboard and wheel input require the target itself to be focused. Text inputs, textareas, select controls, content-editable paths, composition and OS shortcuts are respected. Blur, focus departure and a hidden document clear held state and pending deltas. Pointer cancel/lost capture discard pending drag deltas. Only accepted events are prevented. Single-pointer mouse, pen and touch dragging is supported by the adapter; multi-touch/pinch is intentionally absent.

Camera values and controls

snapshot() returns detached {yaw, pitch, distance, target} values. setState validates the complete replacement atomically. orbit(yawDelta, pitchDelta) uses radians; pan(right, up) uses camera-relative world units; zoom(logDelta) uses logarithmic distance, so zoom(Math.log(2)) doubles the distance. transform() returns a detached Y-up, -Z-forward translation/quaternion/unit-scale transform compatible with the public runtime scene command contract. Headless/native code can reuse this math without importing DOM code.

Finite settings reject invalid ranges and unknown fields. Defaults are yaw/pitch 0, distance 5, target [0,0,0], pitch [-1.5,1.5], distance [.1,10000]. Configured pitch limits stay strictly between the poles; distance limits are [1e-6,1e6]. Target components and operation arguments are bounded to ±1e9. Orbit wraps yaw, motion clamps pitch/distance/target, and invalid operations do not mutate state. Snapshots can reconstruct the same camera, but no durable persistence/migration promise is made.

  • Primary drag: orbit; Shift-primary, middle or secondary drag: pan
  • Wheel: zoom; line/page modes normalize to bounded CSS-pixel deltas
  • Arrow keys: orbit; W/A/S/D: pan; = and -: zoom

Controls update once per animation-frame opportunity and await at most one onChange callback. Input arriving during that callback is accumulated, then coalesced into the next update. Keyboard time gaps are capped at 50 ms; this is a safety bound, not fixed-tick gameplay or a frame-rate/performance promise. A custom asynchronous schedule/cancel pair and monotonic millisecond now can provide deterministic test timing. Desired camera state changes before the callback; applications own acknowledgement, error UI and any persistence or rollback policy. Callback failure detaches input and reports onError; an explicit attach can retry. Detach cancels unsent work, but cannot revoke an already-started external callback. Await settled() before disposing its destination. Reattachment waits for an old in-flight callback before permitting another, and fences stale errors.

Verification

node --test packages/input/*.test.js
npm run typecheck
npm run test:input:browser

The last command requires the prebuilt real runtime and installed Chromium. Its isolated packed consumer checks public runtime inspection and actual changed pixels after pointer orbit, pan, wheel zoom and keyboard input, plus typing focus, touch cancellation and repeated attach/detach. Logic/pack/type checks alone never qualify rendered behavior. Existing renderer/example browser proofs stay separate.

Web API references: Pointer Events, UI Events, and HTML focus.