import { DashboardError } from './errors.js'; import type { HistoryResolver } from './history.js'; import type { CompiledDefinition, Finding, Inventory, IR } from './types.js'; export interface Tile { id: string; label: string; value: number | string; caption: string; /** E28 t2 — the blocks this tile's selection read, so a live client can * recompute ONLY the tiles a `{path, block}` frame can have changed. Absent * or empty means no row block was read (`select: concepts` counts documents, * which no row frame invalidates). */ blocks?: string[]; } export interface CoverageRow { type: string; documented: number; total: number; built: number; tested: number; byState: Record; /** what THIS format calls the two counts, from bind.built_label / * bind.tested_label. Undefined means the format did not say — the engine * has no word of its own, so the renderer shows a meaning-missing marker * rather than inventing one (the same rule as the coverage legend). */ labels?: { built?: string; tested?: string; }; } export interface GroupedResult { count: number; documented: number; kinds: number; sections: { name: string; total: number; documented: number; groups: { name: string; total: number; documented: number; uris?: string[]; }[]; }[]; /** meaning slots from the definition (catalog I1) */ legend?: { on: string; off: string; }; callout?: string; } /** A property a rows view can be filtered on. `kind` decides which operators * make sense and whether the value is picked or typed — the same split the * component's operator-value types make. */ export interface SearchProperty { key: string; label: string; kind: 'enum' | 'text'; /** enum: the vocabulary. Absent for text properties. */ values?: string[]; /** where the value comes from: a block column, the derived state, or the * document a row belongs to */ source: 'column' | 'state' | 'document' | 'type'; } export interface GraphResult { /** `path` is the node's IDENTITY key: a document path in concept mode, a * record uuid in record mode (E38 t3). `hash` — record mode only — is the * node's opening address (`#/r/…`, owner.ts recordHash); a concept node * carries none and the client keeps its `#/c/` construction. */ nodes: { path: string; title: string; type: string; hash?: string; }[]; edges: { from: string; to: string; kind: string; label?: string; }[]; } /** One faceting control over a view. `as` decides how it renders — a tab strip * (`tabs`, the default) or a compact `dropdown` for a high-cardinality field; * `multi: true` makes the control multi-select (values OR within the facet). * `root: true` rolls each row/concept up to the topmost ancestor of the * self-referencing field `by` (`roots` is the precomputed path → root map). */ export interface FacetSpec { by: string; label?: string; empty?: string; default?: string; root?: boolean; roots?: Record; as?: 'tabs' | 'dropdown'; multi?: boolean; } export interface NavItem { label: string; view?: string; count?: number | null; /** a name from the engine's icon catalogue (core/icons.ts); optional */ icon?: string; grouped?: GroupedResult | { unavailable: string; }; /** relationship graph over the IR (declarative `graph:` nav item) */ graph?: GraphResult; /** concept paths, when evaluated with members: true */ paths?: string[]; /** row members for select: rows, grouped by document + block — the key is * the row's first-column value, how renderers and forms target a row */ rows?: { path: string; block: string; keys: string[]; }[]; /** declarative facet filter — a concept field on `select: concepts`, a row * column on `select: rows` (catalog: segmented-control). With `root: true` * the facet rolls up to the topmost ancestor along the self-referencing * frontmatter field `by`; `roots` is the precomputed concept-path → root * title map the client facets against (both concepts and rows). */ filter?: FacetSpec; /** additional facets, ANDed with `filter` and each other, so a view can * carry a low-cardinality facet as tabs AND a high-cardinality one as a * dropdown. Each entry is validated exactly as `filter` is. */ filters?: FacetSpec[]; /** nest the selected concepts by a self-referencing metadata field, so a * hierarchy already present in the documents renders as one */ tree?: { by: string; empty?: string; }; /** multi-property filtering over the selected rows: each entry is a * filterable property with its operators and, where the vocabulary is * known, its values. Built from what the format DECLARES. */ search?: { properties: SearchProperty[]; }; children?: NavItem[]; } /** One widget in a composed view: its kind, its label, and whatever the kind * needed evaluating (tiles, coverage rows, members, a graph…). The E32 t2 * fields below are CONDITIONAL — emitted only when a placement authored or * needed them, so a placement-free dashboard's Widgets stay byte-identical * (the step-0 pins prove it; JSON.stringify drops nothing that was never * set, and this evaluator never sets these outside the placement paths). */ export interface Widget { widget: string; label?: string; note?: string; /** evaluated payload — shape depends on the widget kind */ data?: any; /** widgets that select rows/concepts carry the same nav-item fields */ item?: NavItem; /** `tabs` only: the widgets it switches between, one shown at a time */ tabs?: Widget[]; /** E32 t2 containers ('view' = a placed named view, 'section' = one * `sections:` entry): the evaluated children, in authored order */ widgets?: Widget[]; /** 'view' only: the name this placement resolved (or failed to) */ ref?: string; /** placement width, 1..12 of a 12-column grid; absent = full row */ span?: number; /** 'view' only, INSTEAD of `widgets`: why the slot could not be filled — * the finding of the same name tells the full story, this key is what * lets the renderer draw a labelled hole rather than a blank */ unresolved?: 'missing' | 'cycle' | 'expansion-limit'; } export interface ComposedView { id: string; label: string; widgets: Widget[]; /** set when the view came from a `for_each:` template — the template's id, * so navigation can point at the whole set rather than at each instance */ template?: string; } export interface DashboardResult { title: string; subtitle?: string; /** views composed widget-by-widget (dashboard.yaml `views:`) */ views?: ComposedView[]; /** bespoke fields below exist only for the deprecated `use:` pack path * (plus `tiles` for top-level `indicators:`) — a views-only dashboard * carries its numbers inside the composed widgets instead */ tiles?: Tile[]; coverage?: CoverageRow[]; /** meaning slot: derive-state → words, from the coverage pack's bind */ coverageLegend?: Record; reverseGap?: { kind: string; count: number; }[] | { unavailable: string; }; navigation: { group: string; items: NavItem[]; }[]; /** declared theme-token overrides (validated names), per mode */ theme?: { light: Record; dark: Record; }; /** how much the surface animates. `full` (the default) spends the Astryx * motion tokens on view transitions, list reflow and entrance; `reduced` * keeps only opacity fades; `off` is still. A viewer's own * prefers-reduced-motion always downgrades `full` to `reduced` — the * declaration sets the ceiling, the reader sets the floor. */ motion?: 'full' | 'reduced' | 'off'; } /** The page's design tokens — the real Astryx set (MIT), generated by * scripts/tokens.mjs. A dashboard.yaml `theme:` block may override any of them. */ export declare const THEME_TOKENS: Set; export interface EvalOptions { /** include member lists (concept paths, artifact URIs) for renderers */ members?: boolean; /** the compiled format — lets `search:` resolve properties against declared columns */ def?: CompiledDefinition | null; /** include-path → fragment yaml text, mirroring runEngine's `files` map — * the CLI reads the vault, the engine stays a pure text evaluator */ fragments?: Map; /** E32 t1 — the named-view library: views-file path → yaml text, symmetric * with `fragments` (the CLI walks views/, the engine stays a pure text * evaluator). Absent or empty means every reference is `view-missing`. */ views?: Map; /** E41 (P31) — the HISTORY resolver: the host's answer to a compiled * history ask, threaded to every `renderFormat` call site that has an * operational event log within reach. Absent everywhere else, and a * history chart then renders its static note — never a broken page and * never a picture of nothing. */ history?: HistoryResolver; } export { DashboardError }; /** The `include:` list of a dashboard.yaml, without evaluating anything — how * a caller learns which fragment files to read into `opts.fragments`. */ export declare function includePaths(yamlText: string): string[]; export declare function evaluateDashboard(yamlText: string, ir: IR, inventory: Inventory | null, findings: Finding[], opts?: EvalOptions): DashboardResult;