/** * The cockpit DOM renderer — S8. * * Renders a {@link CockpitView} into a host element: the demand×supply networks * matrix, a red light per *missing agent type*, and the diversity-SLO light. * Clicking a worker instance calls {@link RenderOptions.onDrill} with that * instance's relay stream id. This renders only the *volatile* part of the page * (the part that refreshes each poll); the drill-in terminal is owned by * {@link mountCockpit} in a persistent region so it survives a matrix refresh. * * It renders against a **structural** DOM subset ({@link ElementLike} / * {@link DocumentLike}) rather than the global `document`, for two reasons that * matter to this slice: * 1. it is the *same* function the standalone shell and the embedded (App View) * host both call — via the plain-JS `page/mount.js` adapter, which passes the * browser's real `document` and their own host element — so the two render * **identically** by construction (one code path, no standalone/embedded * branch); and * 2. a plain in-memory fake satisfies the structural type, so the renderer is * unit-tested on Node with no DOM library and no `as` cast. * * These interfaces are a deliberately *minimal* subset, NOT lib.dom's `Element`/ * `Document`: the real DOM satisfies them at runtime (as `page/mount.js` — plain * JS, untyped — relies on), but a real `HTMLElement` is not TS-assignable to * {@link ElementLike} (lib.dom's `appendChild` is `Node`-constrained). Keeping the * subset this narrow — rather than widening to lib.dom, which the in-memory fake * could not satisfy without a banned `as` cast — is the tradeoff that buys the * DOM-free Node tests. A TypeScript browser caller wanting to invoke * {@link bootCockpit} directly supplies a thin structural adapter over the real DOM. */ import type { CockpitView } from "./view.ts"; /** The minimal element surface the renderer builds against (the in-memory fake, and the real DOM at runtime, satisfy it — see file header). */ export interface ElementLike { className: string; textContent: string | null; setAttribute(name: string, value: string): void; appendChild(child: ElementLike): ElementLike; replaceChildren(): void; addEventListener(type: string, handler: () => void): void; } /** The minimal document surface the renderer builds against (the in-memory fake, and the real DOM at runtime, satisfy it — see file header). */ export interface DocumentLike { createElement(tagName: string): ElementLike; } export interface RenderOptions { /** Called with a worker instance's relay stream id when the operator drills in. */ readonly onDrill?: (stream: string) => void; } /** Handles into the rendered tree the caller may need. */ export interface CockpitDom { /** The freshly built root the view was rendered into. */ readonly root: ElementLike; } /** * Render `view` into `host`, replacing whatever was there. Idempotent: call it * again on every refresh to reflect the latest demand×supply snapshot. */ export declare function renderCockpit(host: ElementLike, doc: DocumentLike, view: CockpitView, options?: RenderOptions): CockpitDom;