import { type PlacementEntry, type PlacementSection } from './placement.js'; import { type ViewRegistry } from './views.js'; import type { Widget } from './dashboard.js'; import type { Finding } from './types.js'; /** What compose borrows from the host, instead of importing it: the registry * (t1's, never mutated), the leaf-widget evaluator (dashboard.ts's closure * at t2 time — t3 swaps the injection source without changing this shape), * the findings channel, and the ONE `{placeholder}` substitution grammar * (`fillSpec`) so `with:` fills exactly what for_each fills. */ export interface ComposeDeps { registry: ViewRegistry; evalWidget: (w: any, where: string, nested: boolean) => Widget; findings: Finding[]; fillSpec: (node: any, vars: Record) => any; /** E36 t1, review of PR6a — the definition's engineViews() map, so a * PLACEMENT of an installed plugin's view (`place: [{ view: dj/comments, * span: 6 }]`) resolves exactly as the top-level `views: [dj/comments]` * reference does. Before this field the two resolution points in ONE * dashboard disagreed: the top level resolved, the placement stubbed an * unresolved slot — and told the author to install a plugin the format * already installed. OPTIONAL and last, like resolveViews' parameter: a * plugin-less evaluation passes nothing and takes yesterday's path. */ engine?: ReadonlyMap; } /** The breadth valve (§1.3): total placed-view expansions per evaluation. * Depth needs no cap — a repeated name on the stack is a cycle, so the stack * holds distinct names and depth is bounded by registry size — but breadth * multiplies (A placing B fifty times, B placing C fifty times…), and 500 is * far past any page a human scrolls while cheap enough to evaluate. */ export declare const EXPANSION_MAX = 500; /** Per-EVALUATION composition state. One instance rides the whole dashboard: * `stack` is the resolution path (push/pop around each placed body — the * evaluation is synchronous depth-first, so one mutable array IS the path, * and a diamond legitimately evaluates its shared leaf once per path); * `expansions` is monotone across the evaluation, which is what makes the * valve a page budget rather than a per-branch one. */ export interface ComposeCtx { stack: string[]; expansions: number; /** the valve warns ONCE — after it closes, every further placement slots * identically, and 10,000 copies of one sentence answer nothing */ limitWarned: boolean; } export declare function newComposeCtx(): ComposeCtx; /** * Evaluate one VIEW placement into a container widget * `{widget:'view', ref, label, span?, widgets:[...]}` — or an unresolved slot * (`unresolved: 'missing' | 'cycle' | 'expansion-limit'`, no widgets) when the * name cannot be honored. `nested` is the host's tabs-in-tabs flag, threaded * VERBATIM to every leaf (§1.3): placement is transparent to the guard, so a * placed view inside tabs still refuses tabs of its own. */ export declare function evalPlacedView(entry: PlacementEntry, deps: ComposeDeps, where: string, nested: boolean, ctx: ComposeCtx): Widget; /** * A `sections:`-authored body → one 'section' wrapper widget per section * (§2 payload shapes: wrappers exist ONLY for sections-authored bodies — the * `widgets:` sugar emits its entries flat, which is what keeps a placement- * free dashboard's Widgets byte-identical). Section `notes:` rides the * existing note channel; the label is the section heading. */ export declare function evalSections(sections: PlacementSection[], deps: ComposeDeps, where: string, nested: boolean, ctx: ComposeCtx): Widget[];