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

@flowajs/react-viewer

v0.2.1

Published

React components for rendering flowa pipeline outputs: citation-grounded Markdown, PDF viewer with bbox highlights, triage workspace.

Readme

@flowajs/react-viewer

React components for rendering flowa pipeline outputs.

Install

pnpm add @flowajs/react-viewer
# Plus the peer dependencies, if not already installed:
pnpm add react react-dom @mantine/core @mantine/hooks @tabler/icons-react

Exports

  • LlmContent — renders LLM-generated Markdown via react-markdown (GFM tables, raw HTML escaped) with click-to-resolve citation links. Links that aren't a valid #cite:AuthorYear resolving in the supplied mapping render as plain text.
  • PdfHighlightViewer — react-pdf-based viewer with 0–1000 normalized bbox highlight overlays.
  • MarkdownHighlightViewer — the Markdown analogue: renders a paper's assembled Markdown and highlights a citation's code-point anchor span (one <mark> per overlapping text node, so a table-row-spanning quote leaves the table intact).
  • parseCitationsFromMarkdown(md) — extract (paperId, quote) pairs from citation links of the form [display](#cite:AuthorYear "verbatim quote").
  • parseCiteHref(href) / isCitationHref(href) — parse / validate citation fragment URLs.
  • matchFilesToPapers(filenames, papers) — match uploaded files to papers by filename: a main paper PDF is <id>.pdf, a supplement is <id>[_ ]supp… (any extension), where <id> is a PubMed id or an encoded DOI. Returns { mains, supplements, unmatched }, supplements sorted lexicographically. parseSupplementFilename(name) exposes the supplement-naming rule on its own.

Citation contract

Citation links use the form:

[display text](#cite:AuthorYear "verbatim quote")

AuthorYear matches [A-Za-z]+\d+ and must resolve in the supplied PaperIdMapping.byAuthorYear. The title attribute carries the verbatim quote used to resolve a bbox. LlmContent renders any link that fails this format or whose AuthorYear is absent from the mapping as plain text — raw HTML is escaped (no rehype-raw), so untrusted model output can't inject markup.

Bbox coordinate scale

All highlight bboxes use a 0–1000 normalized scale (left/top/right/bottom), 1-indexed pages. The viewer rescales to rendered pixels.

Styles

The package ships a pre-built stylesheet at @flowajs/react-viewer/styles.css. Import it once (e.g. in your top-level page or _app.tsx):

import "@flowajs/react-viewer/styles.css";

The bundle contains only the Tailwind utilities used by the package itself — no Preflight reset, so it won't fight your existing base styles (Mantine's own reset stays in effect). The utilities sit outside any cascade layer, so unlayered component styles such as Mantine's cannot outrank them by layer order, and theme values are inlined rather than defined as variables on :root.

If your app builds its own Tailwind stylesheet, import it after this one. Class names the two share (text-sm, gap-2, …) then resolve to your definitions, whichever Tailwind major your app is on.

Consumers do not need a Tailwind toolchain. The CSS is statically built at package release time; nothing in your tailwind.config needs to point at node_modules/@flowajs/react-viewer.

SSR

PdfHighlightViewer lazy-imports react-pdf on mount, so it is safe to render on a server (it returns a <Loader> placeholder until react-pdf loads in the browser). No dynamic(() => …, { ssr: false }) wrapper needed.

LlmContent is server-render-safe.

Viewing controls

The toolbar in the pane zooms the document and turns it counter-clockwise in quarter turns. Highlights and the scroll to a quote follow the turn. Each page keeps its own /Rotate entry and turns from there, so a landscape table page in a portrait paper stays upright until the user turns it. Like zoom, rotation can be controlled from outside through rotation and onRotationChange; it resets when another document opens.

The find box (the magnifier in the toolbar, or Ctrl+F / Cmd+F with focus in the pane, or on nothing in particular after the pane was the last thing used) searches the text pdf.js reads from the document: case-insensitively, across line breaks and end-of-line hyphenation, with straight quotes and hyphens matching their typographic forms. It marks every match and steps through them with Enter and Shift+Enter, collecting at most 1000 and saying so. Focus elsewhere on the page keeps the browser's own find; pass captureFindShortcut={false} to keep the browser's find everywhere. A page whose text cannot be read is skipped, and a scanned document without a text layer reports "Text unavailable".

Loading feedback

While a document downloads, the pane shows the bytes received and, when the server reports a content length, the total and a progress bar. A download that fails, or a viewer bundle that fails to load, shows the cause with a Retry button, and a download that goes quiet for fifteen seconds offers Retry too. Retry re-uses the same pdfUrl; onLoadError receives the error and a classification (kind, and the HTTP status where there is one), so a consumer that hands out short-lived URLs can mint a new one on a 403, and a changed pdfUrl starts a fresh load. A fresh load that fails calls onLoadError again, so the consumer bounds its own retries. The package does not cache a failed import of react-pdf, so the retry (or the next mount) asks the bundler again; whether the bundler re-fetches a chunk that failed is up to it.

pdf.js fetches a large document in byte ranges, so the first page renders after a few small requests, only when the response lets it: the server has to answer with Accept-Ranges: bytes and a Content-Length, the body must not be content-encoded (a gzip-compressed object disables ranges), and for a cross-origin file the CORS policy has to expose Accept-Ranges, which is not on the browser's safelist (S3 and MinIO need ExposeHeaders in the bucket's CORS rule for this; exposing Content-Range too is harmless). pdf.js also uses ranges only for files above twice its chunk size, about 128 KB. Without ranges pdf.js buffers the whole body before parsing, so nothing renders until the last byte arrives, whether or not the file is linearised.

Worker assets

PdfHighlightViewer requires the consumer to serve pdf.worker.min.mjs and cmaps/ somewhere reachable, then pass URLs via workerSrc and cMapUrl props:

<PdfHighlightViewer
  pdfUrl={url}
  highlights={highlights}
  workerSrc="/pdfjs/pdf.worker.min.mjs"
  cMapUrl="/pdfjs/cmaps/"
/>

Both files ship with react-pdf under node_modules/react-pdf/dist/. Copy them into your public assets at install time (e.g. via a postinstall script).

Provenance

Every published version carries a sigstore provenance attestation. To verify:

npm audit signatures
# Or, for one package:
npm view @flowajs/react-viewer

License

MIT.