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

@ganeshdole/brainbox-core

v0.3.0

Published

The Brainbox capture engine: screenshots, marks, session recording, console errors and a Transport that sends the report. No React, no UI.

Readme

@ganeshdole/brainbox-core

The Brainbox capture engine. Plain TypeScript, runs in the browser, no React and no UI. It takes the screenshot, bakes the user's marks into it, records the session, keeps the last console errors, and hands the finished report to a Transport. Every Brainbox UI, including our own widget, is built on it.

Use it directly when you are building your own feedback UI. If you want Brainbox's UI, see @ganeshdole/brainbox-widget; if you are in React, @ganeshdole/brainbox-react holds an instance for you.

Install

Inside this monorepo the package is consumed from source:

{ "dependencies": { "@ganeshdole/brainbox-core": "workspace:*" } }

Outside it, from npm:

npm install @ganeshdole/brainbox-core

pnpm build emits dist/ (JS plus .d.ts); the published package's exports point at it (publishConfig).

Use

import { createBrainbox, brainboxTransport } from "@ganeshdole/brainbox-core";

const brainbox = createBrainbox({
  // Brainbox Cloud, or your own backend (see Transport below)
  transport: brainboxTransport({ endpoint: "https://app.brainbox.sh/ingest", projectKey: "pk_..." }),
  identity: { id: user.id, email: user.email },
  onSubmitted: ({ id }) => console.log("filed", id),
});

// One report in progress. `exclude` is your own UI's root element, left out
// of the screenshot.
const draft = brainbox.draft({ exclude: panelEl });
draft.subscribe(() => render(draft.getState()));

draft.captureScreenshot();            // start rasterising the page now
draft.markUp(marks);                  // the marks' bounds become the region; baked in the background
await draft.submit({ text, audio });  // waits for the bake, then transport.send()
draft.cancel();                       // drop everything; the draft can be used again

brainbox.destroy();                   // restores console.error, cancels live drafts

A recording instead of a screenshot:

const rec = draft.startRecording({ onAutoStop: () => void rec.stop() });
rec.annotations.show(mark);           // drawn into the page, so it shows in the replay
rec.setMicMuted(true);
await rec.stop();                     // attaches the session and voice, takes a thumbnail

DraftState is what a UI renders from: screenshotUrl, screenshotPending, screenshotFailed, hasSession, hasVoice. It is a new object on every change, so useSyncExternalStore and friends can compare by reference.

Transport

Core does not know about project keys or endpoints. brainboxTransport() speaks the Brainbox backend's /ingest format. To send reports somewhere else, implement the one-method interface:

import type { Transport } from "@ganeshdole/brainbox-core";

const mine: Transport = {
  async send(report) {
    const fd = new FormData();
    fd.append("json", JSON.stringify({ text: report.text, region: report.region, metadata: report.metadata }));
    if (report.screenshot) fd.append("screenshot", report.screenshot, "screenshot.png");
    const res = await fetch("/api/feedback", { method: "POST", body: fd, headers: { Authorization: token } });
    return { id: (await res.json()).id };
  },
};

Report carries text, region, metadata (CapturedMetadata from @ganeshdole/brainbox-shared), and the screenshot, session and audio blobs.

Rules

  • Importing does nothing. createBrainbox() is the only way in, and destroy() undoes everything it set up.
  • No module-level state, so two instances on one page do not share buffers. The one shared thing is the console.error hook, installed when the first instance asks and removed when the last one goes.
  • Privacy defaults in recordings: inputs are masked; .rr-block, .rr-mask and .rr-ignore on host elements are honoured.