# @greendrake/dash

> Dashboard SPA shell: app bootstrap, splash root layout, tab-registry dashboard, a live service-state control and the Vite/env conventions a dashboard needs

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

## Install

```sh
npm install @greendrake/dash
pnpm add @greendrake/dash
yarn add @greendrake/dash
bun add @greendrake/dash
```

## Health

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

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

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.2.0 |
| Published | 2026-09-24 |
| First published | 2026-09-23 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 9 |
| Unpacked size | 19.1 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 0 |
| Author | Eugene Greendrake <eugene@greendrake.nz> |
| Maintainers | greendrake |

## Links

- npm: https://www.npmjs.com/package/@greendrake/dash
- Repository: https://github.com/greendrake/lib
- Homepage: https://github.com/greendrake/lib/tree/main/dash#readme
- Issues: https://github.com/greendrake/lib/issues
- npm.io page: https://npm.io/package/@greendrake/dash

## Dependencies (9)

- [@greendrake/ui](https://npm.io/package/@greendrake/ui.md) 0.2.0
- [@greendrake/rpc](https://npm.io/package/@greendrake/rpc.md) 0.2.0
- [@greendrake/theme](https://npm.io/package/@greendrake/theme.md) 0.2.0
- [@greendrake/vue-api](https://npm.io/package/@greendrake/vue-api.md) 0.2.0
- [@greendrake/vue-app](https://npm.io/package/@greendrake/vue-app.md) 0.2.0
- [@greendrake/vue-kit](https://npm.io/package/@greendrake/vue-kit.md) 0.2.0
- [@greendrake/font-inter](https://npm.io/package/@greendrake/font-inter.md) 0.2.0
- [@greendrake/vite-preset](https://npm.io/package/@greendrake/vite-preset.md) 0.2.0
- [@greendrake/service-state](https://npm.io/package/@greendrake/service-state.md) 0.2.0

## Recent versions

- 0.2.0 (latest) — 2026-09-24
- 0.1.0 — 2026-09-23

## README

# @greendrake/dash

Everything an operations dashboard needs before it has any panels of its own: app bootstrap, the splash root layout, a tab-registry dashboard, the theme entry and the Vite/env conventions. What it deliberately does not own is the wire — transport, auth and method map stay with the host, because a dashboard's API surface is the one thing no shell can generalise.

## Booting

```ts
import { createDash, TabDashboard, type DashTab } from '@greendrake/dash'

const tabs: DashTab[] = [
    { id: 'users', label: 'Users', component: UsersPanel },
    { id: 'errors', label: 'FE errors', component: ErrorsPanel, props: { api } }
]

createDash({
    splash: ['L1', 'ADMIN'],
    routes: [{ path: '/', name: 'Dashboard', component: TabDashboard, props: { tabs } }]
})
```

`createDash` returns the `SpaApp` (see `@greendrake/vue-app`), so a host can act on it after boot — open a WebSocket eagerly, say. Pass `suspense: true` when a route component has async setup — the splash then serves as the Suspense boundary's initial content, so there is no blank frame while the route resolves. `ready` takes init work the splash must outlast.

`TabDashboard` renders one panel at a time under a `TabBar`. The registry is the single source for the strip and the panels both: adding a tab is one row. Where a dashboard needs its routes code-split, keep the registry inside the lazily-imported route module, so a lightweight route does not pull in the dashboard panels.

Dashboard-wide styling — the denser `--font-md`, headings as section dividers, the shell chrome — lives in `@greendrake/theme/dashboard`, which this package's style entry pulls in together with `@greendrake/font-inter` as the document font. The consuming app adds nothing for styling beyond `index.html`'s `<body class="splash">`.

## The service-state control

`ServiceStateControl` is a panel for one backend service that is `OFF`, `STARTING`, `ON`, `STOPPING` or `ERROR` — the state, why it failed if it did, and the one command that state accepts:

```ts
import { ServiceStateControl } from '@greendrake/dash'

const tabs: DashTab[] = [{ id: 'service', label: 'Service', component: ServiceStateControl, props: { api, transport } }]
```

`api` is an `ApiClient<ServiceStateMethods>` and `transport` a `WsTransport<ServiceStatePushes>`; both contracts come from [`@greendrake/service-state`](../service-state), whose `./server` entry is the machine a backend runs behind them. The control reads the status once, then follows the server's `service.state` pushes — so a transition another operator started shows here too — and re-reads after a dropped socket returns.

`ON` offers *Turn OFF*, `OFF` offers *Turn ON*, `ERROR` offers *Refresh*; `STARTING` and `STOPPING` offer nothing, there being no interfering with a transition already under way. The current state is on the root element as `data-state`, which is what the border colour is drawn from and what an end-to-end test asserts on. `examples/service-pair` is a complete dashboard and backend built on it.

## Vite and env

```ts
import { dashApp } from '@greendrake/dash/vite'

export default defineConfig(
    dashApp({
        dirname: import.meta.dirname,
        define: env => ({ __API_URL__: requireEnv(env, 'API_URL') }),
        overrides: (env, { command }) => (command === 'serve' ? { server: { proxy: /* … */ } } : {})
    })
)
```

`sourcePackages` passes through to `@greendrake/vite-preset` for a dashboard that pulls in source-shipped packages beyond the `@greendrake` scope. `define` and `overrides` receive the app's resolved env; `requireEnv` turns a missing build input into a failed build rather than a default. `overrides` also receives the config env, so a dev-only input is demanded only when there is a dev server to demand it for. `VITE_PORT` is required when serving.

**The env file names are load-bearing.** bun injects `.env.development` / `.env.production` into `process.env` at startup, and a `process.env` value outranks the file Vite's loader reads — with those names a production build silently bakes in development credentials. Dashboards therefore use `.env.dev` / `.env.prod`, names bun ignores, and pass the mode explicitly:

```json
"dev": "bun --bun vite --mode dev --host",
"build": "bun --bun vite build --mode prod",
"preview": "bun --bun vite preview --mode dev"
```

Omitting `--mode` fails the run (there is no `.env.development` to find) rather than producing a mis-configured bundle. The short mode name costs nothing: Vite derives `isProduction` from `NODE_ENV`, which it sets per command. Preview serves an already-built `dist/` and only needs a port, hence `--mode dev`.

`process.env` still outranking the files is what lets dev-stack launchers and deploy pipelines inject their own ports, hosts and keys into an otherwise unchanged app — including e2e runs, where the harness supplies the test backend's ports.

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