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

@ansonphong/360-viewer-library

v6.0.1

Published

Batteries-included sidebar / toolbar / info-bar chrome for 360 panorama experiences — drop-in UI for @ansonphong/360-viewer

Readme

@ansonphong/360-viewer-library

Batteries-included sidebar / toolbar / info-bar chrome for 360° panorama experiences.

Drop-in UI layer that sits on top of @ansonphong/360-viewer — adds section-based browsing, toolbar with controls, glassmorphic info bar, help modal, deep-linking, theme toggle, and a slot/decorator extension system. No framework dependencies.

npm version npm downloads License: MIT Engine Bundle size

Live demo → · Full docs → · Engine package →


What this gives you

  • Section-based sidebar with 9 built-in templates (grid, feed, accordion, hero, list, carousel, avatar-row, avatar-grid, empty)
  • Toolbar with projection toggle, resolution switcher, theme toggle, fullscreen, info, help
  • Glassmorphic info bar with prev/next navigation, image title/caption
  • Help modal triggered by the pv-help keyboard event and toolbar button
  • Deep-linking?img=<slug> reads/writes by default, fully configurable
  • Theme system — light / dark / auto with CSS custom properties + accent-color
  • Decorator API — extend thumbnails, section headings, sidebar, info bar, toolbar without forking
  • Slot system — replace named UI regions with your own factory functions
  • JSON-driven config — one 360-viewer.json controls title, theme, accent, panel width, favicon, social links

Use the engine alone if you're embedding a panorama into a marketing hero or wiring up your own chrome. Use this package when you want a full gallery experience out of the box.


Install

npm install @ansonphong/360-viewer @ansonphong/360-viewer-library three

The engine and Three.js r128 are peer-resolved dependencies. The library-ui pins the engine version to ensure CSS prefixes and event names stay in sync.


Quick start

Plain HTML + UMD (no build step)

<!-- Peer deps -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/@phosphor-icons/[email protected]/src/regular/style.css" />

<!-- Engine + Library UI -->
<link rel="stylesheet" href="https://unpkg.com/@ansonphong/360-viewer@6/dist/viewer.css" />
<link rel="stylesheet" href="https://unpkg.com/@ansonphong/360-viewer-library@6/dist/viewer-library.css" />
<script src="https://unpkg.com/@ansonphong/360-viewer@6/dist/viewer.umd.js"></script>
<script src="https://unpkg.com/@ansonphong/360-viewer-library@6/dist/viewer-library.umd.js"></script>

<div id="viewer" style="width: 100%; height: 100vh;"></div>

<script>
  const gallery = new Phong360ViewerLibrary({
    containerId: 'viewer',
    libraryUrl: 'library/library.json',
    configUrl: '360-viewer.json',
    baseUrl: 'library/',
    theme: 'auto',
  });
</script>

ESM (Vite / webpack / Rollup / Next.js)

import 'three';
import { Phong360ViewerLibrary } from '@ansonphong/360-viewer-library';
import '@ansonphong/360-viewer/dist/viewer.css';
import '@ansonphong/360-viewer-library/dist/viewer-library.css';

const gallery = new Phong360ViewerLibrary({
  containerId: 'viewer',
  libraryUrl: '/library.json',
  configUrl: '/360-viewer.json',
  theme: 'auto',
});

Phosphor Icons is required for the toolbar / sidebar / info-bar glyphs. Without it you'll see empty boxes where icons should be — there is no built-in fallback.


Configuration via 360-viewer.json

Everything visible — title, theme, accent, sections, layout — is driven by a single JSON file:

{
  "context": {
    "type": "profile",
    "title": "Your Name",
    "subtitle": "360 Photography",
    "avatar": "assets/avatar.jpg",
    "theme": "dark",
    "accent": "#6366f1",
    "panelWidth": 420,
    "infoBar": "center",
    "favicon": "🎨",
    "links": [
      { "url": "https://yoursite.com", "label": "Website" },
      { "url": "https://instagram.com/you", "label": "Instagram" }
    ]
  },
  "sections": {
    "Landscapes": { "title": "Landscapes", "icon": "mountains", "template": "grid" },
    "Cities":     { "title": "Cities",     "icon": "city",      "template": "feed" }
  }
}

library.json (v4.0) carries the actual image manifest — the library-ui reads it through the engine. See LIBRARY-FORMAT.md.


Public API

Constructor

new Phong360ViewerLibrary({
  containerId,          // string id of the container element (required)
  libraryUrl,           // path to library.json (v4.0 manifest)
  configUrl,            // path to 360-viewer.json
  baseUrl,              // base path prepended to relative image URLs
  theme: 'auto',        // 'dark' | 'light' | 'auto'
  accent: '#e13e13',
  urlSync: true,        // true | false | { read, write } for custom deep-linking
  panelWidth: 420,
  infoBar: 'center',    // 'center' | 'left' | 'right' | 'off'
  // ...all engine config also forwarded
})

Decorator API

Extend the UI without forking:

// Add a custom decorator to every thumbnail (e.g. owner badge, view count)
gallery.addThumbnailDecorator(({ image, el }) => {
  if (image.featured) el.classList.add('is-featured');
});

// Decorate section headings
gallery.addSectionHeadingDecorator(({ section, el }) => { /* ... */ });

// Inject a custom sidebar section
gallery.addSidebarSection({
  id: 'about',
  title: 'About',
  template: 'hero',
  render: (root) => { /* paint into root */ },
});

// Mount custom content into the info bar
gallery.setInfoBarSlot('left',  document.querySelector('#left-extras'));
gallery.setInfoBarSlot('right', document.querySelector('#right-extras'));

// Add a toolbar button
gallery.addToolbarButton({
  id: 'share',
  icon: 'share-network',
  title: 'Share',
  onClick: () => navigator.share?.({ url: location.href }),
});

Memory bounds: decorators are capped — warning at 20 per kind, hard cap at 100. This protects against accidental N² registration in re-render loops.

Slot system

Swap out entire UI regions with your own factory functions:

import { SLOT_NAMES } from '@ansonphong/360-viewer-library';

gallery.setSlot(SLOT_NAMES.SIDEBAR, ({ container, gallery }) => {
  // return cleanup fn, or undefined
  const el = document.createElement('aside');
  el.innerHTML = '<nav>...</nav>';
  container.appendChild(el);
  return () => container.removeChild(el);
});

gallery.clearSlot(SLOT_NAMES.SIDEBAR);

Available slot names: SIDEBAR, TOOLBAR, INFO_BAR, HELP, plus per-template slots.

Methods

gallery.getViewer()                // underlying Phong360Viewer instance
gallery.selectImage(id)
gallery.openSidebar() / closeSidebar() / toggleSidebar()
gallery.openHelp() / closeHelp()
gallery.setTheme('light')
gallery.setAccent('#6366f1')
gallery.refresh()                  // re-fetch library.json + 360-viewer.json
gallery.destroy()

Events

The library-ui re-emits engine events and adds its own:

| Event | Source | Payload | |---|---|---| | image:visible | engine | { url, image } | | image:loaded | engine | { url, image } | | section:click | library-ui | { section } | | sidebar:open / sidebar:close | library-ui | — | | help:open / help:close | library-ui | — | | theme:change | engine | { theme } |

Plus the bubbling CustomEvent on the container: pv-help toggles the help modal.


Templates

The sidebar renders sections via a pluggable template engine. Pick a template per section in your 360-viewer.json:

| Template | Description | Use case | |---|---|---| | grid | Responsive thumbnail grid | Default browsing | | feed | Vertical list with large thumbnails | Recent / featured | | accordion | Collapsible group containing an inner template | Category organization | | hero | Single large featured image | Spotlight | | list | Compact rows with small thumbnails | Search results, dense lists | | carousel | Horizontal scrolling strip | Trending, related | | avatar-row | Horizontal circular avatars | Creator highlights | | avatar-grid | Grid of avatar cards | Creator directory | | empty | Placeholder | No-content state |

Custom templates can be registered — see TEMPLATES.md.


Theming

All visuals key off CSS custom properties under the --pv-* namespace (shared with the engine):

.my-gallery {
  --pv-accent: #e13e13;
  --pv-bg: #0a0a0a;
  --pv-surface: rgba(20, 20, 20, 0.85);
  --pv-text: #fff;
  --pv-text-muted: rgba(255, 255, 255, 0.6);
  --pv-border: rgba(255, 255, 255, 0.1);
  --pv-panel-width: 420px;
}

Or swap built-in themes at runtime: gallery.setTheme('dark' | 'light' | 'auto').

See THEMING.md for the full custom-property reference.


Bundles in dist/

| File | Purpose | Engine | |---|---|---| | viewer-library.esm.js | ESM | peer dep | | viewer-library.umd.js | UMD | reads window.Phong360Viewer | | viewer-library.standalone.umd.js | Self-contained UMD | reads window.Phong360Viewer | | viewer-library.css | Sidebar / toolbar / info-bar styles | — |

All bundles expose Phong360ViewerLibrary as a global (UMD) or named export (ESM).


Migration from 5.x → 6.x

Pure rename, no behavior changes:

| 5.x | 6.x | |---|---| | @ansonphong/360-library-ui | @ansonphong/360-viewer-library | | Phong360LibraryUI class | Phong360ViewerLibrary class | | dist/library-ui.* | dist/viewer-library.* | | --p360-* / .p360-* CSS | --pv-* / .pv-* | | p360-help event | pv-help | | peer engine @ansonphong/[email protected] | @ansonphong/360-viewer@^6.0.0 |

npm uninstall @ansonphong/360-library-ui @ansonphong/360-engine
npm install @ansonphong/360-viewer-library @ansonphong/360-viewer
- import { Phong360LibraryUI } from '@ansonphong/360-library-ui';
+ import { Phong360ViewerLibrary } from '@ansonphong/360-viewer-library';
sed -i 's/--p360-/--pv-/g; s/\.p360-/.pv-/g' your-styles.css

The old package remains installable indefinitely with a deprecation notice — existing lockfiles do not break.


Browser support

Modern evergreen browsers (Chrome, Firefox, Safari, Edge). Requires WebGL 1.0 and ES2017+.


Links


License

MIT — see LICENSE.