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

@realitycollective/xrblocks-uiextensions

v0.1.0

Published

EXPERIMENTAL Google XR Blocks / plain three.js adapter for the Reality Collective UI Extensions - hosts the engine-free @realitycollective/webxr-uiextensions windowing core (UIKitML panels, window chrome, follow mode) in any three.js WebXR scene, includin

Readme

@realitycollective/xrblocks-uiextensions

EXPERIMENTAL adapter for Google XR Blocks and plain three.js. It hosts the same @realitycollective/webxr-uiextensions core as the IWSDK adapter, with the same UIKitML panels, window chrome and window manager, inside any three.js WebXR scene. An XR Blocks Script gives you exactly that kind of scene.

Maturity: the IWSDK adapter is the most complete one, and this adapter is built to match it. It now has nearly all the same windowing features, and its desktop path is verified in a real browser (panels render, mouse clicks reach uikit controls, WASD/jump/ crouch move the camera). It has still had NO on-device pass on Android XR hardware - treat the XR Blocks path specifically as unverified.

Feature matrix vs the IWSDK adapter

| Feature | IWSDK | XR Blocks / three.js (this package) | | --- | --- | --- | | UIKitML panel hosting (runtime interpret, scale-to-fit) | ✅ | ✅ UixPanelDocument | | Window lifecycle + chrome (focus/PIN/DOCK/MIN/X, all opt-in, pin labels) | ✅ | ✅ UixWindowHost | | Driving windows from code (WindowManager: hide/show, dockTo/undock/returnHome, setChrome, close) | ✅ | ✅ every manager event applied | | Portable scene descriptors (applyScene) | ✅ | ✅ implements SceneTarget | | Panel-ready wiring (onPanelReady) | ✅ | ✅ implements WindowHost | | Follow mode (body-follow, yaw-only, eased) | ✅ | ✅ pure follow-math | | Hand menus (hand-locked, palm gate, anchors) | ✅ from the player rig | ✅ from a HandPoseSource; webxrHandPoseSource(renderer.xr) reads the session's input sources, and without hands the menu follows the body | | Dock regions (wall/belt, slots, follow) | ✅ | ✅ createRegion, manager.dockTo (host.dock forwards) | | Desktop mouse input (hover, click, drag-to-look) | ✅ | ✅ via @pmndrs/pointer-events | | Desktop locomotion (WASD, jump, crouch, sprint) | n/a | ✅ DesktopControls | | XR select-ray click forwarding | ✅ | ✅ minimal (forwardClick) | | Bare panels (createPanel) | ⬜ ECS owns the lifecycle | ✅ supportsStandalonePanels is true | | Title-bar ray drag (@pmndrs/handle) | ✅ | ⬜ roadmap (movable is accepted and ignored) | | Title-bar near grab (squeeze / pinch) | ✅ | ⬜ roadmap (needs drag) | | Guarded poke (one press per touch, front only) | ✅ UITouchGuardSystem over IWSDK's touch pointers | ⬜ no near touch here yet; the core TouchPress is ready for it | | Drop-to-dock by dragging | ✅ | ⬜ roadmap (needs drag) | | System keyboard text input | ✅ | ⬜ untested on Android XR |

Required renderer setup (read this first)

uikit draws panel backgrounds, borders and text glyphs all as transparent meshes, stacked by renderOrder. three.js sorts transparent objects by camera distance by default, which is meaningless for coplanar UI layers - at grazing angles or close range a panel background can sort in front of its own text and labels silently vanish. uikit also clips panel content with local clipping planes, which three.js ignores unless enabled.

Apply both settings to any renderer you create:

import { configureRendererForUikit } from '@realitycollective/xrblocks-uiextensions';

const renderer = new WebGLRenderer({ antialias: true });
configureRendererForUikit(renderer);   // transparent sort + local clipping

IWSDK does this internally, which is why panels look right there with no setup. A hand-rolled three.js host must do it explicitly, and under XR Blocks you should apply it to the renderer xb.init() creates.

Usage in an XR Blocks Script

import * as xb from 'xrblocks';
import {
  DockMode,
  connectUIExtensions,
  forwardClick,
} from '@realitycollective/xrblocks-uiextensions';

class MyScript extends xb.Script {
  async init() {
    this.uix = connectUIExtensions({ scene: this, camera: xb.camera });
    const config = await fetch('./ui/my-window.json').then((r) => r.json());
    this.uix.createWindow({
      id: 'status',
      title: 'Status',
      config,
      dockMode: DockMode.BodyFollow,
    });
  }
  update() {
    this.uix.update(xb.getDeltaTime());
  }
  onSelectStart(event) {
    /* raycast from event.target, then forwardClick(intersections) -
       see demos/webxr-multiplatform for the complete wiring */
  }
}

xb.add(new MyScript());
await xb.init();

Nothing here imports xrblocks - the glue binds to plain three.js shapes (scene: Object3D, camera), so the same host works in a hand-rolled three.js WebXR app.

Window options and handles

createWindow takes the portable WindowOptionsBase fields plus config, so an option means here what it means on the IWSDK adapter. Two notes specific to this host:

  • id is optional. Omit it and the window is named uix-window-<n>.
  • movable is accepted and recorded, but nothing acts on it yet: this host has no title-bar drag of its own, so there is no gate to close. It is in the options so a scene descriptor written for IWSDK loads here unchanged.
  • The four chrome flags (closable, minimizable, pinnable, dockable) are off unless set, as on IWSDK; host.manager.setChrome(id, {...}) changes them later. host.manager.hide/show, dockTo/undock/returnHome and close all take effect here, so a menu written against the manager needs no host-specific code.
  • handMenu and dockMode: 'hand-locked' make a hand menu. Pass xr: renderer.xr to connectUIExtensions (or handPose to the host) so it rides the session's tracked hands; on a page that also serves a desktop the source reports no hands outside a session and the menu follows the body until one starts.

The handle it returns satisfies the core WindowHandle and adds the three.js specifics:

const handle = host.createWindow({ title: 'Status', config });
handle.id;        // 'uix-window-1'
handle.group;     // the scene-graph node - position and rotate freely
handle.document;  // the UixPanelDocument
handle.panel;     // the same document, under the portable name
handle.onReady((panel) => wire(panel));   // fires straight away here

onReady fires synchronously because uikitml interprets the markup during createWindow; only LAYOUT is async. It still returns an unsubscribe function, so code that runs on both adapters has one shape. supportsStandalonePanels is true: createPanel(config) gives an unmanaged panel with no chrome and no window record.

Known constraint: three versions

[email protected] declares a peer of three@^0.184, while IWSDK mandates the [email protected] fork used workspace-wide. Vite resolves a single three per bundle so the pairing works in practice, but npm's peer check cannot express it - this workspace uses legacy-peer-deps (see the root .npmrc). Revisit when IWSDK's three catches up.

Testing

npm test   # scale/follow/pointer math + a headless host lifecycle suite

Demo

demos/webxr-multiplatform - detects the platform and boots this adapter on Android XR (or via ?uix-engine=xrblocks anywhere, including XR Blocks' desktop simulator).

Live demos

License

MIT © Reality Collective