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

@divriots/h2d-electron-sdk

v0.0.4

Published

Capture an Electron app's renderer into an html.to.design payload. Dev builds only.

Readme

@divriots/h2d-electron-sdk

Capture any Electron app's renderer into an html.to.design .h2d payload. Add one line to your main process, trigger a capture from your own UI, and save the bytes (or convert them with the ‹div›RIOTS public API in your own code).

Dev builds only. mountCapture no-ops in packaged production builds unless you opt in.

Install

pnpm add -D @divriots/h2d-electron-sdk

electron is a peer dependency (any version ≥ 22).

Usage

// In your Electron main process
import { app, BrowserWindow, dialog } from 'electron';
import { writeFile } from 'fs/promises';
import { mountCapture, captureWebContents } from '@divriots/h2d-electron-sdk';

// Call once, after `app` is available — registers the capture preload in
// the default session. No UI is added; you wire your own trigger.
mountCapture(app);

// From your own menu item / shortcut / button:
async function captureToFile(win: BrowserWindow): Promise<void> {
  const { bytes, filename } = await captureWebContents(win.webContents);
  const { filePath } = await dialog.showSaveDialog(win, {
    defaultPath: `${filename}.h2d`,
  });
  if (filePath) await writeFile(filePath, Buffer.from(bytes));
}

Drop the resulting .h2d file into the html.to.design Figma plugin — or POST the bytes to the ‹div›RIOTS public API yourself for a Figma-pasteable (figma-clipboard) conversion.

API

mountCapture(app, options?)

Registers the renderer preload in the default session. Call once, after app is available.

| Option | Type | Default | Description | | --- | --- | --- | --- | | enabledIn | (app) => boolean | dev only | Opt capture into packaged production builds. By default mountCapture no-ops when app.isPackaged is true. |

captureWebContents(webContents, options?)

Captures a webContents and resolves with the .h2d payload. mountCapture must have been called first.

| Option | Type | Default | Description | | --- | --- | --- | --- | | url | string | wc.getURL() | Override the URL baked into the capture. |

Returns:

| Field | Type | Description | | --- | --- | --- | | bytes | Uint8Array | The .h2d payload. | | filename | string | Suggested filename (no extension). |

Renderer requirements

None — the SDK works with Electron's default webPreferences. The preload is bundled sandbox-compatible (its only external is require('electron')), so sandbox: true and contextIsolation: true are both fine.

Security model

The capture engine (the proprietary dom-serializer) runs entirely in the renderer's sandboxed isolated world — the same context the preload already lives in. It has no Node, no fs/net, and no access to your app beyond the page it's capturing. It reaches the main process only through a small, enumerable set of IPC channels for the three main-only Electron capabilities it needs:

  • CDP (the debugger protocol) — h2d:cdp / h2d:cdp-event
  • cross-origin iframe execution (WebFrameMain) — h2d:iframe
  • frame enumerationh2d:frames

The main-process side of the SDK (dist/index.js) is shipped un-obfuscated — it's a thin proxy that forwards those calls and adds no IP. You can read 100% of what runs in your main process. The capture is fully local; the SDK makes no network calls (conversion to Figma is left to your own code via the public API).

Limitations

  • Dev only by default. mountCapture no-ops in packaged builds unless you set enabledIn.
  • Same-origin iframes are captured inline. Cross-origin iframes are captured too, but lossy on a few details (platform-font specifics fall back to getComputedStyle's family; forced pseudo-states are approximated). A cross-origin iframe nested inside another cross-origin iframe is dropped at the second level.
  • Open shadow roots only. Closed shadow DOM is captured as opaque.

Troubleshooting

TypeError: Cannot read properties of undefined (reading 'on') — your shell has ELECTRON_RUN_AS_NODE=1 set, which makes the Electron binary run as plain Node with no Electron APIs. Unset it:

unset ELECTRON_RUN_AS_NODE