Splits the single bundle into two: `app.js` (entry for /index.html —
tab renderers + tab routing + refreshState) and a new `flow.js`
(entry for /flow.html — operator inbox derived store + inbox pill
flyout + broker terminal + @-mention composer). Both bundles inline
`./common.js` (DOM helpers, Panel, NOTIF, path linkification).
## What `flow.js` owns
- Operator inbox derived store (`operatorInbox`, `INBOX_LIMIT`,
`inboxAppendFromEvent`, `buildInboxListNode`, `renderInbox`) +
inbox-pill click wiring
- Broker terminal init (`HiveTerminal.create({ logEl: msgflow, ... })`)
with `renderMsg`, `pulseBanner`, `msgRowMap` reply-thread indicator,
and the renderers map for `sent` / `delivered` broker rows
- @-mention composer (`#op-compose-input` IIFE — sticky recipient,
autocomplete, parseAddressed, /op-send POST)
- A small local `flowContainers` cache for the composer's
autocomplete, refreshed on cold load + on every SSE reconnect via
`onStreamOpen`, and live-updated by `container_state_changed` /
`container_removed` SSE events (the dashboard's `containersState`
lives in `app.js` and isn't available here)
## What `app.js` no longer does
- Drops the inbox derived store, the bindFlowInboxPill IIFE, the
broker-terminal IIFE, and the composer IIFE — all moved
- Drops the `renderInbox()` call in `refreshState` (dashboard has
no #inbox-section element)
- Drops `setTabCount('flow', operatorInbox.length)` — the FL0W tab
count lives in flow.js now (cross-page count broadcasting is a
future follow-up; the slot currently stays hidden on /index.html)
- Drops the `window.HiveTerminal` global — the bare-import pattern
in common.js / flow.js made it unused on the dashboard
## What changes for /flow.html
- `<script src>` switches from `/static/app.js` → `/static/flow.js`
- Mutation events on the dashboard stream (`approval_added`,
`container_state_changed`, etc.) are silently ignored on /flow.html
via a `_default: () => {}` renderer (the dashboard tabs aren't on
this page; firing the legacy applyXxx handlers from here just
mutated dead stores). #408 follow-up filters this at the SSE level
## Validation
- `npm run build` clean.
- Bundle deltas:
- `app.js`: 154kb → 135kb (dropped ~19kb of flow code)
- `flow.js`: NEW 29kb (was bundled into the old 154kb app.js)
- `flow.html` page total: 154kb → 29kb (flow.js + inlined common,
no tab renderers shipped)
- Source: `app.js` 2287 → 1907 lines (-380); `flow.js` 423 lines (new)
- No HTML / CSS changes besides the `<script src>` swap on /flow.html.
## Known limitations (out of scope; tracked separately)
- /index.html still has no live SSE subscription — the dashboard
updates only on cold load + after async-form submits. Pre-existing
behaviour; the SSE wiring also lived in the flow IIFE before.
Step 3 of #406 (or its own bug fix) re-wires it.
- /flow.html's `_default: noop` drops mutation events; #408 fixes
the duplicate-traffic by splitting the SSE endpoint server-side.
- The FL0W tab-strip count pill on /index.html stays hidden — the
count source is now in flow.js. Broadcast via localStorage /
BroadcastChannel is a small follow-up if both pages are open.
Browser smoke test isn't possible from inside iris's container.
Worth eyeballing post-deploy:
- /flow.html: terminal renders broker rows; inbox pill shows count
+ opens flyout; composer autocomplete suggests known agents
+ sends successfully
- /index.html: tab renderers all work; notification toggle still
binds; side panel still opens for diffs / file previews / logs
62 lines
2.3 KiB
JavaScript
62 lines
2.3 KiB
JavaScript
// esbuild build for @hive/dashboard. Output layout (`dist/`):
|
|
//
|
|
// dist/index.html served by the Rust router at GET /
|
|
// dist/flow.html served at GET /flow.html
|
|
// dist/static/app.js /index.html entry — tab renderers +
|
|
// tab routing + refreshState
|
|
// dist/static/flow.js /flow.html entry — broker terminal +
|
|
// operator inbox + @-mention composer
|
|
// dist/static/{app,flow}.js.map source map siblings
|
|
// dist/static/dashboard.css served at /static/dashboard.css
|
|
// (@import resolved from @hive/shared)
|
|
//
|
|
// Both JS entries inline `./common.js` (DOM helpers, Panel singleton,
|
|
// NOTIF, path linkification) — esbuild dedupes the shared module
|
|
// between bundles. The Rust binary mounts `dist/` as a
|
|
// `tower_http::ServeDir` fallback; the layout above keeps every URL
|
|
// the HTML files reference reachable without rewriting paths in the
|
|
// HTML.
|
|
|
|
import { build } from 'esbuild';
|
|
import { mkdirSync, copyFileSync, rmSync } from 'node:fs';
|
|
import { dirname, resolve } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const src = (p) => resolve(here, 'src', p);
|
|
const dist = (p) => resolve(here, 'dist', p);
|
|
const staticDir = (p) => resolve(here, 'dist', 'static', p);
|
|
|
|
rmSync(dist(''), { recursive: true, force: true });
|
|
mkdirSync(staticDir(''), { recursive: true });
|
|
|
|
// Bundle both JS entries. ES-module output, browser target, no minify
|
|
// (line-aligned source aids debugging; minification belongs in a later
|
|
// follow-up once asset sizes warrant it). esbuild writes each entry
|
|
// to `static/<name>.js` based on the entryPoint basename.
|
|
await build({
|
|
entryPoints: [src('app.js'), src('flow.js')],
|
|
outdir: staticDir(''),
|
|
bundle: true,
|
|
format: 'esm',
|
|
platform: 'browser',
|
|
target: ['es2022'],
|
|
sourcemap: true,
|
|
logLevel: 'info',
|
|
});
|
|
|
|
// Bundle the CSS — esbuild resolves @import including the package
|
|
// re-exports from @hive/shared.
|
|
await build({
|
|
entryPoints: [src('dashboard.css')],
|
|
outfile: staticDir('dashboard.css'),
|
|
bundle: true,
|
|
loader: { '.css': 'css' },
|
|
logLevel: 'info',
|
|
});
|
|
|
|
for (const html of ['index.html', 'flow.html']) {
|
|
copyFileSync(src(html), dist(html));
|
|
}
|
|
|
|
console.log('dashboard build ok →', dist(''));
|