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

flviewer

v0.2.1

Published

Pure client-side file preview for the browser: images, PDF, video/audio, text family and office documents (DOCX/XLSX/PPTX) with built-in minimal UI.

Readme

flviewer

Pure client-side file preview for the browser. Images, PDF, video & audio, the text family (txt / code / JSON / CSV / XML / Markdown-as-HTML) and office documents (DOCX / XLSX / PPTX), with a minimal built-in UI.

  • Zero framework dependencies — vanilla factory API plus a <fl-viewer> web component
  • Overlay & Embed — fullscreen modal via open(), or render inside any container via mount()
  • Streaming media — video/audio URL sources play directly from the URL (Range-request seeking, no full download)
  • Native images — images render as a real <img>, never a <canvas>, with src preferring the original URL; in-memory sources (File/Blob/ArrayBuffer) inline a data: URL inside WeChat/Weixin, whose long-press Save image / Forward menu rejects blob:. Download always ships the original bytes under the original filename
  • Lazy engines per format — image and text previews never download heavy engines: pdf.js (~157 KB gz engine + ~356 KB gz worker), the text pipeline (~86 KB gz), docx-preview (~25 KB gz + shared jszip ~31 KB gz), SheetJS (~171 KB gz) and the pptx renderer (~477 KB gz, incl. echarts for charts) each load only on first use of their format. No consumer configuration required, works in bundled apps, CDN-direct and raw-ESM contexts
  • Typed errors — failures resolve to { code, message, cause? } events, never exceptions

Install

vp install flviewer
# or: npm i flviewer

Quick start

import { open, mount } from "flviewer";

// Fullscreen overlay (one at a time; a new open() replaces it)
const modal = open("https://cdn.site.com/report.pdf");
modal.close();

// Or embed inside your own container
const viewer = mount(document.querySelector("#preview")!, fileBlob);

// Every source works: URL, Blob, File, ArrayBuffer, Uint8Array
viewer.update(someUint8Array); // swap source, same instance
viewer.destroy(); // idempotent teardown

Authenticated URLs:

open(url, {
  requestInit: { headers: { Authorization: "Bearer …" }, credentials: "include" },
});

Web component

<script type="module">
  import "flviewer";
</script>

<fl-viewer src="https://cdn.site.com/photo.png" style="width: 480px; height: 360px"></fl-viewer>

<script>
  const el = document.querySelector("fl-viewer");
  el.source = fileBlob; // complex values via properties
  el.requestInit = { credentials: "include" };
  el.addEventListener("flv:ready", (e) => console.log(e.detail));
</script>

Events

One event stream, two views — controller.on(name, cb) and bubbled flv:<name> CustomEvents:

| Event | Detail | Fired when | | ------------ | ---------------------------- | ------------------------------------------- | | ready | { kind, pages?, name? } | content rendered and interactive | | error | { error: FlvError } | typed failure (see below) | | close | { by: 'user' \| 'api' } | Overlay closed (Esc / button / close()) | | pagechange | { page, total } | PDF page / XLSX sheet / PPTX slide changed | | zoom | { scale } | magnification changed | | truncated | { bytes?, lines?, cells? } | preview hit a truncation cap (kept amounts) |

kind is one of image, pdf, video, audio, text, docx, xlsx, pptx.

Error codes: fetch-error, unsupported-type, encrypted-pdf, encrypted-office, render-error, aborted.

Controls

Toolbar: zoom in/out · fit · 100% · rotate · page prev/next + indicator / sheet dropdown (PDF, XLSX, PPTX) · download · fullscreen · close (Overlay only). Interactions: select mode (default) — wheel scrolls the document; hand mode — drag pans (clamped) and the wheel zooms; double-click 1×↔2×; keyboard (Esc, +/-, arrows, 0, h/v). On touch devices pinch zooms in either mode and swipe pages PDF/PPTX — the continuous zoom buttons and pager arrows make way for those gestures. Text and XLSX views scroll natively. Images keep their native affordances: select mode leaves long-press to the browser's save/copy/share sheet, while hand mode suppresses selection so drag-pans stay clean.

Theming

Light DOM, flv--namespaced classes. Theme via CSS custom properties, scoped to the viewer root (.flv-root) so the defaults are actually overridden:

.flv-root {
  --flv-accent: #6ea8fe;
  --flv-bg: rgba(28, 28, 32, 0.92);
  /* Paper surface for rendered Markdown (PDF/DOCX-style document page). */
  --flv-paper: #ffffff;
  --flv-paper-fg: #1f2937;
  --flv-paper-muted: #4b5563;
  --flv-paper-border: rgba(15, 23, 42, 0.14);
  --flv-paper-code: #f3f4f6;
  --flv-paper-link: #2563eb;
}

Only the rendered Markdown view uses the paper surface — plain text/code/CSV/XML and the Markdown source toggle stay on the dark data/chrome surfaces.

Development

vite-plus toolchain end-to-end:

pnpm install      # dependencies ([email protected], pinned via packageManager)
vp dev            # playground (manual QA rig)
vp test           # unit (happy-dom) + browser (Playwright Chromium)
vp check          # format + lint + typecheck
vp pack --dts --publint --attw   # release gate

Preview deploy

Hand someone a live build to review — an anonymous Cloudflare Worker with a 60-minute TTL and no account involved:

scripts/preview-deploy.sh          # prints the live URL + claim URL

See docs/preview-deploys.md for how the temporary account works, the claim window and caveats.

Regenerate test fixtures / the inlined pdf.js worker + assets after dependency changes:

node scripts/gen-fixtures.ts
node scripts/gen-worker-source.ts
node scripts/gen-pdf-assets.ts

Browser support

Evergreen browsers (Chrome, Firefox, Safari, Edge). Requires dynamic import().

License

MIT