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

partforge

v0.104.0

Published

Turn a declarative part definition into a parametric-CAD web app (three.js + Manifold/Replicad). Requires a Vite-based consumer.

Readme

partforge

AI-built parametric CAD for 3D printing. Describe a part to an AI agent and get a forge — a self-contained web app for that one part: a 3-D viewer, a control panel with just the parameters that matter, and STL / STEP / 3MF export. Share the forge and anyone can dial in their own version and print it.

A forge isn't a frozen STL. It's open-source code that renders and regenerates the model live, so the part stays editable — resize it for a different bearing, screw, or motor without relearning a heavy CAD tool — and anyone (or their own agent) can read the source and extend it.

partforge is the engine behind the forge: a small npm framework that an LLM tuned for tool use can drive to author, test, and measure a part, then ship it as a browser app.

See it

  • Live showcase — https://scottsykora.github.io/partforge/ — example forges (Faceted Planter, Spacer, Filleted Box) you can open, adjust, and export.
  • A real forge — https://scottsykora.github.io/Drum-Machine/ — a parametric capstan drum built with partforge for a robotics project.

Build your own forge

You don't write CAD by hand. Point a tool-using AI agent (Claude Code, or the Claude / ChatGPT desktop apps with file access) at this repo and describe what you want:

Using the partforge framework at https://github.com/scottsykora/partforge, build me a <your part><the dimensions, fits, and features that matter>.

Expect a few turns. The first attempt is often rough, but you refine it in plain language — "make the bore fit an M3", "add a 2 mm fillet", "thinner walls", "taller by 10" — until the part is right. You end up with a forge you can host anywhere and hand to anyone.

How it works

You — or an agent — write one part definition: geometry build functions plus a parameter schema. partforge renders everything else: the 3-D viewer, the control panel, the geometry workers, and the export buttons. Each control can carry a description, a preset, or be hidden, so the interface stays simple while the part stays deeply adjustable.

Two geometry backends run in Web Workers, and partforge routes each part to whichever it needs:

  • Manifold — fast preview meshes and STL / 3MF, including mesh-native fillet and chamfer for straight and circular edges.
  • Replicad (OpenCASCADE-in-WebAssembly) — exact B-rep for STEP, shell, and automatic fallback for blend geometry Manifold cannot handle.

The viewer is three.js.

Because a part is just code, an agent can build it and check its own work: the partforge/testing helpers measure volume and bounding box, probe geometry, detect self-overlap between sub-parts, and render the model so a multimodal agent can see what it made. A part can also declare a verify block — a manufacturing (DFM) profile plus design-intent assertions — that flags problems like a too-thin wall or a part that won't fit the print bed before anyone hits export.

Requires a Vite-based app. partforge is published as plain ESM source and relies on Vite's worker / WASM / CSS import handling.

Install

npm install partforge

Use

// app.js
import { mount } from "partforge";
import part from "./parts/my-part.js";
mount(part, {
  createWorker: (name) =>
    new Worker(new URL("./part-worker.js", import.meta.url), { type: "module", name }),
});

// part-worker.js
import { runWorker } from "partforge/worker";
import part from "./parts/my-part.js";
runWorker(part);

Test your parts headlessly with partforge/testing (bootManifoldKernel, bootOcctKernel, assemblyOverlaps, measure, verify, meshVolume, bboxSize).

Smoke-test that an app actually boots (real Chromium, real worker/WASM): npm run check (or node scripts/check-app.mjs <entry>.html) — it loads the app and verifies the kernel boots with no errors. Needs Playwright: npm i -D playwright && npx playwright install chromium.

Embedding (0.12.0+)

mount() returns a runtime handle and accepts element references, so an embedding app (React, iframe, multiple mounts) can size, await, and tear down the viewer without global IDs:

const runtime = mount(part, {
  createWorker,
  elements: {
    viewer, controls,                       // canvas host + param-panel host
    rail,                                   // full-height resizable/collapsible controls rail
    status: { status, busy, phase },        // status chrome
    tabs,                                   // view-tab segmented control
    exports: { stl, step, threeMf },        // export buttons
    chrome: { reframe, theme, railToggle }, // viewer buttons + rail collapse/restore
  },
  onBuild: ({ status, ms, error }) => {},   // per accepted build: "success" | "error"
  onPick: ({ selection, label, prompt, token }) => {}, // programmatic click-to-select
});
await runtime.ready;   // first successful build (rejects on a first-build error)
runtime.setHostPane("rail");  // narrow layout only: show just the controls
                               // rail ('stage' | 'rail'), suppressing the
                               // built-in tab bar. null hands selection back.
runtime.setRailLayout({ mode: "dock", inset: 380, railHeight: 316 });
                               // where the rail SITS when the host draws chrome over
                               // the frame: docked into its bottom sheet (inset = the
                               // sheet's height, railHeight = the slice of it the rail
                               // may paint into), or { mode: "overlay" } for a
                               // right-edge drawer over a full-width stage. null (or
                               // any shape partforge can't read) restores partforge's
                               // own layout; resize and collapse are suspended while
                               // a layout is leased.
runtime.setActive(false);      // park the viewer: stop the render loop, release the
                               // drawing buffer. setActive(true) restores both.
runtime.attachTooltips([{ element: myButton }]);  // host chrome buttons join the mount's
                               // shared hover tooltip (label = the button's title or
                               // aria-label, or a per-entry getLabel()); returns
                               // { sync, hide, detach }, auto-detached on dispose()
const off = runtime.onContextLost(() => {});  // WebGL context loss; returns an unsubscribe
const carried = runtime.getViewerState();  // camera + projection + cutaway, as plain JSON
runtime.dispose();     // stops loops, workers, observers, listeners; frees GPU resources
mount(nextPart, { createWorker, elements, viewerState: carried });  // resumes the view

Carry the view across a remount. A host that applies edits by mounting a new part — rather than by setParams — starts each mount from the part's default framing, so the camera snaps back and the cutaway closes every time the user changes anything. Snapshot with runtime.getViewerState() before dispose() and hand the result to the next mount() as viewerState: the part changes, the user's view of it does not.

Restore is best-effort per field, so a state that no longer fits is dropped rather than fatal. The cut plane's world pose is restored exactly; its on-screen size is re-derived from the new geometry, since that is a property of the part rather than of the user's choice. Omit viewerState on a first mount — the viewer then restores its own persisted camera and projection, as it always has.

Park the viewer when you hide it. A host that hides the canvas with display: none needs nothing — the container collapses and the ResizeObserver shrinks the drawing buffer for free. A host that hides it any other way (visibility: hidden, an inactive tab, an off-screen pane) gets no such signal: the full-resolution MSAA buffer stays resident and the render loop keeps redrawing an unchanging scene at 60fps that nobody can see (a part with an autoplay animation running is the same story, just with something actually moving off-screen). On a phone that is tens of megabytes plus continuous GPU work, and it has been enough on its own to get a tab killed. Call runtime.setActive(false) when the viewer goes off-screen and setActive(true) when it comes back.

Parking releases both large GPU allocations — the drawing buffer and the cached 1024² capture target — and stops the render loop. Offscreen captures (captureCurrent, captureViews) keep working while parked and framing is unchanged, so a host can still take a build screenshot of a hidden viewer; the first capture after parking just re-allocates its target.

Every elements entry defaults to the legacy global ID (#app, #controls, #panel for rail, #status/#busy/#phase, #part, #download/#download-step/#download-3mf, #reframe/#theme/#rail-toggle), so a classic host page needs no changes. The viewer sizes from its container via ResizeObserver — no window coupling.

rail is the full-height controls rail introduced by the resizable-panel layout (docs/superpowers/specs/2026-07-26-controls-rail-layout-design.md); chrome.railToggle is its optional collapse/restore button. Both are optional — a host that lays out the framework itself (no rail markup) gets a no-op. The rail's resize seam is positioned against rail.parentElement by default, so the rail must be a direct child of the positioned .pf-shell unless the host also supplies elements.shell to point at the real containing block (e.g. when a wrapper div sits between them, as is common in a React layout).

runtime.setParams(partial) edits parameters programmatically — the entry point for animating a part from host code:

runtime.setParams({ openAngle: 45 });   // merges into the live params, syncs the panel

The partial is merged into the current params and the control panel updates to match. Keys the part doesn't define are silently ignored. When every changed parameter only moves geometry (a rotation or translation in place()), the viewer re-poses the meshes it already has — instantly, with no worker rebuild; onBuild does not fire for those pose-only edits. Anything that changes the geometry itself rebuilds as usual.

When any view declares animations, runtime.animation exposes the viewer's playback engine (null otherwise). Animations are view-owned (views.<name>.animations), so the engine is scoped to the active view — call setView first to reach another view's set:

runtime.animation.play("open");   // switch + play, within the ACTIVE view (camera cue and all)
runtime.animation.seek(0.5);      // scrub, normalized 0..1 (pauses)
runtime.animation.pause();
runtime.animation.stop();         // reset + restore pre-animation params and clear opacity overrides
runtime.animation.state();        // { view, animation, status, t, stepIndex }

A view can mark at most one of its animations autoplay: true to start it automatically on first show and again on every view switch — until the user touches the transport — or anything writes params (runtime.setParams included) or calls a runtime.animation method; any of those disarms auto-start for the session. This replaces the old idle turntable as the "something is moving" cue for a part-app; see the authoring guide for the full contract.

onPick arms click-to-select permanently: label is the feature label (falling back to the sub-part label/name) for compact UI, prompt is the LLM-ready sentence, token the compact form, selection the raw object. When onPick is set, the ?pick / ?pickserver URL modes are ignored (one click listener ever live); hover labels stay always-on.

Authoring guide

docs/AUTHORING-PARTS.md is the full guide — the part contract, the geometry kernel API, the parameter schema, app wiring, testing, and gotchas. See Designing the control panel in that guide for how to write descriptions, hide internal params, and keep the interface simple while staying deeply adjustable. src/parts/demo.js is a minimal worked example; src/parts/planter.js is a richer one (facets, taper, twist, even walls, an optional feature, and a verify block).

Locally, npm run dev then open /demo.html, /planter.html, or /filleted-box.html.

  • Agent clarification (request-a-pick): an external tool can ask the user to click geometry and get the Selection back — serve with ?pickserver&picktoken=<token>, drive with partforge pick-serve (it prints the token) + partforge pick "<prompt>" …. The server is loopback-only and token-gated on every route. See skills/partforge/SKILL.md and the authoring guide.

License

MIT