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

@bobfrankston/wallplater

v1.0.7

Published

JSON-driven wall-plate generator (Decora / duplex / blank). Direct-to-3MF via manifold-3d, with an optional OpenSCAD engine.

Readme

@bobfrankston/wallplater

JSON-driven wall-plate generator (Decora / duplex / blank / button gangs). Reads a small JSON (JSON5) config and emits a multi-object 3MF (plate + labels as separate, individually colourable objects) or per-part STLs.

Engines

| Engine | Flag | Needs OpenSCAD? | Speed | Notes | |---|---|---|---|---| | manifold (default) | — | no | ~1 s | Direct geometry via manifold-3d (Clipper2 + robust CSG). Text via opentype.js. | | scad (legacy) | -scad or "engine":"scad" | yes | ~30 s | Writes a .scad and shells out to OpenSCAD. Reference / fallback. Renders all hole geometry (incl. button pips) but not rocker/header/per-button labels. |

The manifold engine has no system dependency beyond Node and is the path forward; -scad is kept for parity/verification.

If the -scad engine can't find OpenSCAD (neither the openscad config path nor openscad on PATH), it offers to install it for you — winget on Windows, brew --cask on macOS, apt-get on Linux — then re-checks. Decline (or a non-interactive stdin) exits with a hint to install manually or drop -scad.

Build / install

TypeScript compiled to co-located .js / .d.ts / .map (no src/dist split). tsc is the global install.

npm install
npm run build        # tsc  (or: npm run watch  for tsc -w)

Installed globally (npm i -g @bobfrankston/wallplater) it exposes a wallplater command. defaults.json is resolved relative to the module, so it runs correctly from any working directory.

Usage

Requires Node 24+ (ESM). Run the compiled entry:

node index.js ../KitchenBack.json          # manifold engine -> KitchenBack.3mf
node index.js ../KitchenBack.json -scad    # OpenSCAD engine (same result)
wallplater   ../KitchenBack.json           # if installed globally
  • Outputs are written next to the config file, named after the config's name.
  • Unknown flags are rejected. -scad (or --scad) selects the OpenSCAD engine.

The 3MF holds up to three named, individually-selectable objects — <name> plate (filament 1), <name> labels (all accent text, filament 2), <name> breaker (filament 2). The filament assignment is baked in via Metadata/model_settings.config (Bambu/Orca per-object extruder), so the text slices in your second colour without manual assignment — provided the project has ≥2 filaments. The label objects sit flush in the plate's front-face pockets (printed front-face-down), so they look hidden in the viewport until coloured.

The 3MF also embeds a preview thumbnail (Metadata/thumbnail.png, wired via the OPC thumbnail relationship) so Windows Explorer and slicers show an image of the plate. It's a pure-JS orthographic render of the front face in the configured plateColor / accentColor — no extra dependency, no STL/mesh rendering by the shell. (Without it, the file previews blank: the shell never renders the geometry itself, it only displays this embedded PNG.)

Config

Each specific config is deep-merged over defaults.json (in this directory), so a specific file carries only what differs — often just name, gangs, and a breaker.switch. Every default lives in defaults.json, not in code.

Configs are parsed with JSON5 (the most lenient reader), so comments (//, /* */), trailing commas, and unquoted keys are all allowed — plain JSON still works unchanged.

Lengths are written CSS-style with a unit suffix. Supported units:

| Suffix | Meaning | mm | |---|---|---| | in | inch | 25.4 | | ft | foot | 304.8 | | cm | centimetre | 10 | | mm | millimetre | 1 |

e.g. "0.375in", "5mm", "2cm", "0.5ft", "-42mm". A bare number (no suffix) is treated as millimetres. Angles (e.g. cskAngle) are plain numbers.

// KitchenBack.json — only what differs from defaults.json
{
  "name": "KitchenBack",
  "gangs": [
    { "type": "decora", "legend": "Disposal" },
    { "type": "blank", "screws": false },     // middle blank, no screw holes
    { "type": "decora" }
  ]
  // add later: "breaker": { "switch": "A8" }
}
  • gangs[].type: "decora" | "duplex" | "blank" | "button".
  • gangs[].screws: false to omit that gang's screw holes (default true).
  • gangs[].filler: on a blank gang, marks it as backed by a filler/insert plate, so its cover screws sit on the Decora line (wider spacing) instead of the box-ear line.
  • gangs[].legend: single label centred above the gang's opening (positioned clear of the screw holes; legendY / legendSize).
  • gangs[].breaker: per-gang breaker/circuit label, lower-right of the opening and clear of the screws (gangBreakerY / gangBreakerSize).
  • breaker: { "switch": "A8", "size": "0.375in", "inset": "0.25in" } — the panel-wide breaker label in the lower-right corner. switch is the text; size/inset come from defaults.json, so a config usually sets only breaker.switch. Per-gang (gangs[].breaker) and panel-wide (breaker) labels are independent — use either or both.

Stacked rocker labels (Leviton 1755 triple rocker, etc.)

The 1755 is three rockers stacked in one standard Decora opening, so the opening is unchanged — only the labelling differs. Per gang:

{
  "type": "decora",
  "header": "ON / OFF",                       // centred above the opening
  "rockers": [                                // top -> bottom (1-3+ entries)
    { "left": "1", "right": "FAN" },          // a label each side of every rocker
    { "left": "2", "right": "LIGHT" },
    { "left": "3", "right": "HEAT" }
  ]
}

Rockers are spaced evenly down the opening; left labels are right-aligned snug to the opening, right labels left-aligned. legend / header / rockers / per-gang breaker all print in the accent colour (the labels object). Relevant defaults: rockerSize (3.5mm), rockerGap (1.5mm), headerSize (4mm), headerGap (5mm). Keep side labels short on multi-gang plates — the side margin is ~18 mm on an end/single gang but only ~6 mm between interior gangs; long labels can overflow. See ../triple_demo.json for a worked single-gang example.

Rocker/header labels are rendered by the manifold engine only. The -scad engine ignores them (it warns).

Button gangs (round holes in a pip layout)

A "button" gang punches a set of round button holes (default 0.5 in diameter) arranged symmetrically like the pips on a die / playing card — odd counts put a single button in the centre. Set the count with buttons (1–9):

{
  "type": "button",
  "legend": "FAN",                       // the legend "on top" (above the buttons)
  "buttons": 3,                          // 1-9; pip layout, single = centre
  "buttonD": "0.5in",                    // optional per-gang diameter (default dims.buttonD)
  "buttonLegends": ["HI", "MED", "LO"]   // optional small per-button labels
}
  • buttons: number of holes (1–9). Setting it implies type:"button". The pip pattern is the standard die/domino layout, read top → bottom, left → right: 1=centre, 2/3=diagonal, 4/5=corners (+centre for 5), 6/7=two columns (+centre for 7), 8/9=full ring/grid. Counts above 9 are an error.
  • buttonD: per-gang hole diameter; defaults to dims.buttonD (0.5in).
  • buttonLegends: small labels, one per button in pip reading order ("" or a short array skips some). They are in addition to the gang legend on top, and print in the accent colour. Sizing/placement: buttonLegendSize (3 mm), buttonLegendGap (1 mm, below each hole).
  • Pip spacing is dims.buttonPitch (0.85in centre-to-centre). A button gang mounts like a device — it gets a screw pair on the Decora line unless "screws": false.

See ../buttons_demo.json for a worked Decora-plus-buttons example.

Per-button legends are rendered by the manifold engine only (the -scad engine emits the button holes but skips the small labels, and warns).

See ../decora_wallplate_spec.md for the dimensional reference and print notes (print front-face-down; PETG/ASA recommended for heat near devices; keep plate and labels the same material family — see the chat note on ABS+PLA).

Appearance, output & engine

Top-level keys (all have defaults in defaults.json):

| Key | Default | Meaning | |---|---|---| | output | "3mf" | "3mf" (multi-object + thumbnail), "stl" (one binary STL per part), or "scad" (write the .scad only, no render — implies the scad engine). | | engine | "manifold" | "manifold" (direct) or "scad" (OpenSCAD). -scad on the CLI forces scad. | | plateColor | "#35589f" | Plate (filament 1) colour — used for the embedded preview and the scad-engine preview. | | accentColor | "#101010" | Label (filament 2) colour, same uses. | | printReady | true | Flip the model front-face-down (rotate 180° about X) for printing. The preview camera follows the flip, so labels stay upright either way. | | fontFile | Arial Bold | Path to a .ttf/.otf for the manifold engine's text. | | font | Liberation Sans Bold | OpenSCAD fontconfig name for the scad engine's text. | | openscad | Program Files path | OpenSCAD executable; only used by the scad engine (auto-install offered if missing). | | bevel | 1.2mm | Front-edge chamfer width. | | labelDepth | 0.6mm | Recess depth of the label pockets / height of the label solids. |

Plate geometry, opening sizes, screw spacing, etc. live under the dims object (see defaults.json for the full list, all unit-suffixed lengths). Override any single dim by deep-merge, e.g. "dims": { "depth": "5mm" }.

Files

  • index.ts — CLI entry / engine dispatch / arg validation
  • config.ts — schema, unit parsing, defaults.json merge, loader
  • defaults.json — all default values (merged under each specific config)
  • geometry.ts — manifold engine (direct geometry)
  • text.ts — glyph outlines -> filled contours (opentype.js)
  • scad.ts — legacy OpenSCAD engine (.scad generation + STL readback)
  • threemf.ts — multi-object 3MF writer, binary STL writer