# @hoardodile/workbench

> Offline dev workbench for hoardodile content plugins.

Latest version **0.2.0** (published 2026-09-08) · MIT license · 0 weekly downloads

## Install

```sh
npm install @hoardodile/workbench
pnpm add @hoardodile/workbench
yarn add @hoardodile/workbench
bun add @hoardodile/workbench
```

## Health

**Score 65/100 (B)** — status: active.

Positive: esm support; no vulnerabilities; has provenance; recently updated; high maintenance score.

Warnings: low downloads; no types; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.2.0 |
| Published | 2026-09-08 |
| First published | 2026-08-25 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM |
| Node | >=24 |
| Dependencies | 0 |
| Unpacked size | 7.4 MB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Provenance | attested (GitHub Actions) |
| GitHub stars | 4 |
| Maintainers | wooloo26 |
| Keywords | hoardodile, plugin, workbench, dev, mock |

## Links

- npm: https://www.npmjs.com/package/@hoardodile/workbench
- Repository: https://github.com/hoardodile/hoardodile
- npm.io page: https://npm.io/package/@hoardodile/workbench

## Alternatives

- [pagerjs](https://npm.io/package/pagerjs.md) — 60 weekly downloads
- [whistle.savefor-mock](https://npm.io/package/whistle.savefor-mock.md) — 4 weekly downloads
- [@ireneliaoliao/vite-plugin-mock](https://npm.io/package/@ireneliaoliao/vite-plugin-mock.md) — 0 weekly downloads
- [fakenamely](https://npm.io/package/fakenamely.md) — 0 weekly downloads
- [@onify/fake-amqplib](https://npm.io/package/@onify/fake-amqplib.md) — 0 weekly downloads

## Recent versions

- 0.2.0 (latest) — 2026-09-08
- 0.1.15 — 2026-09-05
- 0.1.14 — 2026-09-04
- 0.1.13 — 2026-09-03
- 0.1.12 — 2026-09-02
- 0.1.11 — 2026-09-02
- 0.1.10 — 2026-09-01
- 0.1.9 — 2026-09-01
- 0.1.8 — 2026-09-01
- 0.1.7 — 2026-08-31
- 0.1.6 — 2026-08-31
- 0.1.5 — 2026-08-30
- 0.1.4 — 2026-08-29
- 0.1.3 — 2026-08-29
- 0.1.2 — 2026-08-27
- … 3 more at https://npm.io/package/@hoardodile/workbench/versions

## README

# @hoardodile/workbench

Offline dev workbench for hoardodile content plugins: mounts one plugin
iframe against the offline mock host (`@hoardodile/host-web`), fed with
real data by the dev server — no hoardodile server needed. The workbench
page itself is a small React app built on [`@hoardodile/ui`](https://www.npmjs.com/package/@hoardodile/ui) (the same
design system and theme tokens the app and plugins use).

You normally never touch this package directly. `hoardodile plugin dev`
(resolved from the plugin's own `@hoardodile/workbench` devDependency)
watch-builds a plugin, captures its server-side hook results from the
real worker sandbox, renders preview variants and video frames with the
production pipeline, and serves the workbench at
http://127.0.0.1:5199. If that port is already in use, the server rebinds
to the next free port (and prints the real URL after it) instead of
aborting, so a stale workbench never blocks a new one.

Terminal dev tooling (MIT) — like `@hoardodile/host`, it is not part of
the SDK closure and never enters a shipped plugin bundle.

## Page

The chrome strip shows the plugin name, the resource list, the hook
status line and the current viewport. With multiple resources the list
is a left sidebar (`w-sidebar`, dense single-line rows) at the `sidebar`
breakpoint and collapses to a horizontal chip row in the toolbar below
it; a single resource renders one row/chip. The plugin iframe floats as
the design system's card surface on the canvas.

### Iframe settings

The Configure popover edits every iframe configuration item, and their
defaults are the main app's hardcoded defaults (so the workbench shows
the plugin exactly as it ships):

| Setting | Default | App source |
| --- | --- | --- |
| Theme mode | System (follows the OS) | `ThemeProvider` `defaultTheme` |
| Palette | Mono | `ThemeProvider` `defaultPalette` |
| Icon style | Duotone | `IconStyleProvider` `defaultStyle` |
| Language | System (`navigator.language`, "en" fallback) | app i18n detection |
| Font family | Empty (app system stack) | the app's unset font pref |
| Viewport | Fill (the app preview surface) | the preview dialog |

The workbench chrome itself is localized in the same five official
languages (en/zh/ja/de/es): the chosen language drives both the chrome
and the `languageChanged` push to the mounted iframe. Shared option names
(palette/icon style/language) come from the app catalogs; the workbench's
own copy lives in the `workbench` namespace of `@hoardodile/i18n`.

Manual acceptance after an i18n change (`hoardodile plugin dev`): switch
through all five languages and check that (1) the workbench chrome
follows immediately, (2) the plugin iframe switches without a reload,
(3) plugin strings the bundle lacks fall back to English.

Theme, palette, icon style, language and font changes are pushed to the
mounted iframe (theme/fonts/language pushes) without a reload — the same
protocol the app's theme broadcast uses. A plugin built against a
vanilla SDK that never subscribed to those pushes keeps its initial
context; the Reload button re-posts the context with the current values.
Element-level iframe attributes (sandbox, referrer policy, fullscreen,
title) and the injected viewport meta stay fixed at the app's values.

Settings persist in localStorage under a workbench-only key, so they
survive a `plugin dev` restart.

## Serve entry

`@hoardodile/workbench` exports `serveWorkbench(opts)`, an HTTP server
that serves the prebuilt workbench page plus the plugin's built `dist/`
at `/plugin`. Everything about the *resources* arrives through provider
callbacks, which is what keeps the serve entry dependency-free while
still reaching real data (the React UI is prebuilt and inlined in the
published `dist/`):

| Provider | Feeds |
| --- | --- |
| `resources()` | the resource list (sidebar / toolbar chips) |
| `files` | `/data` reads and the plugin file URL shape |
| `snapshot(resId)` | sandboxed `detect` / `sourceMeta` / `searchMeta` / `listFiles` / `coverLocal` / `imageHashes` |
| `state(resId)` | seeds the mock host with the resource's comments, danmaku, prefs and cache |
| `preview(resId, path)` | `?size=preview` variants |
| `frame(resId, path, timeMs)` | video seek-preview thumbnails |

`hoardodile plugin dev` supplies all of them. Omit one and the matching
capability degrades honestly: without `preview` the original bytes are
served, without `frame` the route stays unmounted, and the page's status
line says so.

Run standalone against a plain directory when you only need the client
side:

```bash
node dist/serve.mjs --plugin ./dist --data ./testdata --port 5199
```

`--data` serves one resource (the directory itself). To switch between
many items from one folder, use `--resource-dir` — every direct subfolder
becomes a resource, named by its basename, and the page lists them in the
resource list:

```bash
node dist/serve.mjs --plugin ./dist --resource-dir ./testdata --port 5199
```

Both are the directory shape of the same `resources()`/`files` providers;
`--data` keeps its single-resource meaning, `--resource-dir` is the
consolidated "folder of many resources" shape.

### Routes

```
GET /plugin/*                                  built plugin bundle
GET /data/<path>[?res=]                        raw entry bytes
GET /data/?list=1[&res=]                       entry names
GET /data/?stat=<path>[&res=]                  entry size
GET /api/workbench/resources                   resource picker list
GET /api/workbench/context?res=<id>            hooks + seeded state
GET /api/resources/:id/files/:token/*          plugin file URLs
GET /api/resources/:id/frame/:token/:name/:ms  video seek frame
```

The routing lives in one module (`scripts/mounts.mjs`) shared by the
vite dev server and the published standalone server, so a route can
never exist in only one of them.

---
_Source: https://npm.io/package/@hoardodile/workbench · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
