api reference
Dashboard API Reference
createDashboard: a dashboard described as a JSON spec, a layout, grid, chart, KPI, map and html panels, a source per panel, links between them, built into the wired page, handed back by spec(), and proposed by the AI.
API reference › Dashboard composition
All 19 pages Everything on one page → Developer guide →
Dashboard composition
modules/dashboard describes a dashboard as data. One JSON spec names a layout, the panels in it - a grid, a chart, a KPI panel, a map or static html - the source each panel reads and the links between them, and createDashboard(el, spec, factories) builds the wired page. dashboard.spec() hands the same spec back, with every window where the user has since dragged it, so a host can store, diff and version a dashboard like any other document. It is a composition layer and nothing more: there is no builder UI, no persistence and no users, and everything it draws is drawn by a module you already have.
import { createGrid, createHeadlessGrid } from '@toclocoinc/lattice-grid';
import { createLayout } from '@toclocoinc/lattice-grid/modules/layout';
import { createChart } from '@toclocoinc/lattice-grid/modules/charts';
import { createDashboard } from '@toclocoinc/lattice-grid/modules/dashboard';
const dashboard = createDashboard(document.querySelector('#dash'), {
layout: { columns: 2, rows: 2 },
sources: { sales: { kind: 'rows', rows, rowKey: 'id' } },
panels: [
{ id: 'table', kind: 'grid', title: 'Sales', source: 'sales', options: { selection: 'multiple' } },
{ id: 'byRegion', kind: 'chart', source: 'grid:table', options: { type: 'bar', x: 'region', y: 'sales' } },
],
}, { createGrid, createHeadlessGrid, createLayout, createChart });
The viewers are handed in, never imported. The third argument names the factories the dashboard builds with - createGrid, createHeadlessGrid, createPushdownSource, createLayout, createChart, createKPI, bindLeaflet, bindDeck - so the module stays a few kilobytes and carries no second copy of any of them. On a script-tag page any factory not named is found on its own global (LatticeGrid, LatticeGridLayout, LatticeGridKPI, LatticeGridLeaflet, LatticeGridDeck). A panel whose factory is on neither is refused by name and the rest of the dashboard is built; without createLayout nothing is.
| Spec key | What it says |
|---|---|
| layout | Any createLayout option (columns, rows, gap, compact, movable, …), handed to the layout as written, plus windows: one entry per panel id with its placement (xPos, yPos, xSize, ySize) and any window option. A panel with no entry is auto-placed; a panel's title names its window when the entry gives none. |
| panels[] | { id, kind, title, source, binding, options }. kind is grid, chart, kpi, map or html. options go to the viewer's own call unchanged - createGrid, createChart, createKPI, bindLeaflet or bindDeck - and the dashboard adds only the element and the grid. A map panel draws through a map chart (binding: 'chart', the default: markermap, choropleth, …), 'leaflet' or 'deckgl'; a Leaflet panel's options.map (a deck.gl panel's options.deck) may be a function of the panel's element that makes one. An html panel takes { html } - your own markup, set as it is - or { text }. |
| sources | Named sources: { kind: 'rows', rows }, { kind: 'pushdown', adapter, ... } (every other key is a createPushdownSource option), { kind: 'router', router, predicate, route } (each grid reading it is attached with router.attach(grid, predicate, route); load the router after the dashboard is built), or { kind: 'grid', of: '<panel id>' }. Each may carry the columns and rowKey every grid over it is given; a rows source with no columns gets one per key of its first rows. A panel names one as source: '<id>', or source: 'grid:<panel id>' to view that grid panel's grid. |
| links[] | { from, to, on }: the rows selected in the from grid panel filter the to panel. on is a column both share, or { from, to } naming each side's column. |
A source is a grid. A grid panel is the grid over its source. A chart, KPI or map panel over a source gets a headless grid of its own, so two panels on different sources - or on the same one - stay independent until a link says otherwise. grid:<panel id> makes a panel a viewer of that panel's grid instead: filter the table and the chart over it narrows, exactly as a chart bound to a grid always has.
Links become what already exists. When both panels' grids are routes of one Data Router, a link is a router.relate() edge (and on may be the router's own relation function). Otherwise it is a named filters.where() predicate on the target's grid, rewritten on every selection:changed of the source grid and cleared with the selection - with a pushed-down in condition when the target reads a pushdown engine. Two pushdown sources with no router between them, two different routers, and a relation function without a router are refused by name, with the reason.
The AI proposes; you apply. With an AI callback in the options - the same ask(payload) createAI takes - dashboard.propose(prompt, { source }) asks for a spec over one source. The model is sent the source's column names and types and the panel kinds this page can draw, never a row unless you pass rows: true (at most maxRows, default 50). The answer is checked against the spec schema and the source's columns: a panel naming a column the source does not have, a kind the page cannot draw or another source is dropped, and each drop is a line in warnings; markup a model put in an html panel is removed. Nothing is built until you call dashboard.apply(spec). Without a callback, propose declines by name.
Views and presentation. saveView(name) stores the spec and every grid panel's grid.state.get() as one view; applyView(id) - or a view your application stored - puts the layout back with layout.setLayout() and each grid's state with grid.state.apply(), rebuilding first when the view's panels differ. present() steps through the panels with the layout's own maximise(); step(by) moves, stopPresenting() puts the dashboard back.
Refused by name. Every refusal is a line in dashboard.problems() ({ id, path, message }) and a console warning under a catalogued id: dashboard:panel:<id> for a panel that was not built, or was built but its viewer refused one of its options (heard from the viewer's own [lattice] warning, which quotes the option); dashboard:link:<from>-><to>; dashboard:spec:<path> for any other problem the validator finds; and dashboard:spec, dashboard:el, dashboard:layout when nothing could be built. validateDashboardSpec(spec) runs the same checks without building anything. destroy() removes the links, every viewer, grid and router attachment the dashboard made, and the layout.
Five panels from one spec, a chart narrowing with its grid, spec() rebuilding the same dashboard, and destroy
const { createTestDom, flushFrames } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid, createHeadlessGrid } = await import('../packages/dom/src/index.js');
const { createLayout } = await import('../packages/modules/layout/index.js');
const { createChart } = await import('../packages/modules/charts/index.js');
await import('../packages/modules/chart-markermap/index.js');
const { createKPI } = await import('../packages/modules/kpi/index.js');
const { createDashboard } = await import('../packages/modules/dashboard/index.js');
const { document, root } = createTestDom({ width: 1200, height: 800 });
const el = document.createElement('div');
root.appendChild(el);
const rows = Array.from({ length: 12 }, (_, i) => ({
id: 'r' + i, region: ['north', 'south', 'east'][i % 3], sales: 10 * (i + 1),
lon: -1 + i * 0.1, lat: 50 + i * 0.1,
}));
// Five panels, one source. The factories are handed in (on a script-tag page
// they are found on LatticeGrid, LatticeGridLayout and LatticeGridKPI).
const spec = {
layout: { columns: 3, rows: 2 },
sources: { sales: { kind: 'rows', rows, rowKey: 'id' } },
panels: [
{ id: 'table', kind: 'grid', title: 'Sales', source: 'sales', options: { selection: 'multiple' } },
{ id: 'byRegion', kind: 'chart', source: 'grid:table', options: { type: 'bar', x: 'region', y: 'sales' } },
{ id: 'total', kind: 'kpi', source: 'grid:table', options: { tiles: [{ id: 'sum', field: 'sales', aggregation: 'sum' }] } },
{ id: 'where', kind: 'map', source: 'sales', options: { type: 'markermap', lon: 'lon', lat: 'lat' } },
{ id: 'note', kind: 'html', options: { text: 'Provisional figures.' } },
],
};
const dashboard = createDashboard(el, spec, { createGrid, createHeadlessGrid, createLayout, createChart, createKPI });
const seen = [dashboard.panels().length, dashboard.problems().length];
// The chart views the table's grid, so filtering the table narrows it.
dashboard.panel('table').grid.filters.set({ col: 'region', op: 'eq', value: 'north' });
flushFrames(); // the next frame
seen.push(dashboard.panel('byRegion').viewer.data().categories.join('+'));
// spec() is what is on screen, placements included; it rebuilds the same dashboard.
const once = dashboard.spec();
dashboard.apply(once);
seen.push(JSON.stringify(dashboard.spec()) === JSON.stringify(once));
dashboard.destroy(); // every viewer, grid and window
seen.push(el.children.length);
return seen.join(' | '); // 5 | 0 | north | true | 0
Mixed sources, the Leaflet and deck.gl bindings, a selection filtering a chart, and a link refused by name
const { createTestDom, flushFrames } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid, createHeadlessGrid, createPushdownSource } = await import('../packages/dom/src/index.js');
const { createLayout } = await import('../packages/modules/layout/index.js');
const { createChart } = await import('../packages/modules/charts/index.js');
const { bindLeaflet } = await import('../packages/modules/leaflet/index.js');
const { bindDeck } = await import('../packages/modules/deckgl/index.js');
const { createDashboard, validateDashboardSpec } = await import('../packages/modules/dashboard/index.js');
const { document, root } = createTestDom({ width: 1200, height: 800 });
const el = document.createElement('div');
root.appendChild(el);
const rows = Array.from({ length: 12 }, (_, i) => ({
id: 'r' + i, region: ['north', 'south', 'east'][i % 3], sales: 10 * (i + 1), lon: -1 + i * 0.1, lat: 50 + i * 0.1,
}));
// Two engines with no router between them: stand-ins for two duckdbAdapter()s.
const engine = () => ({ capabilities: { filter: 'tree' }, execute: async () => ({ rows: [], total: 0 }) });
// Stand-ins for the page's L.map and deck.gl Deck.
const map = { addLayer() {}, removeLayer() {}, on() {}, off() {},
getBounds: () => ({ getWest: () => -180, getSouth: () => -85, getEast: () => 180, getNorth: () => 85 }) };
const deck = { setProps() {} };
const spec = {
sources: {
sales: { kind: 'rows', rows, rowKey: 'id' },
east: { kind: 'pushdown', adapter: engine(), rowKey: 'id', columns: [{ field: 'id' }, { field: 'region' }] },
west: { kind: 'pushdown', adapter: engine(), rowKey: 'id', columns: [{ field: 'id' }, { field: 'region' }] },
},
panels: [
{ id: 'table', kind: 'grid', source: 'sales', options: { selection: 'multiple' } },
{ id: 'chart', kind: 'chart', source: 'sales', options: { type: 'bar', x: 'region', y: 'sales' } },
{ id: 'leaf', kind: 'map', binding: 'leaflet', source: 'sales', options: { map, layers: () => [], position: { lon: 'lon', lat: 'lat' } } },
{ id: 'gl', kind: 'map', binding: 'deckgl', source: 'sales', options: { deck, layers: () => [], position: { lon: 'lon', lat: 'lat' } } },
{ id: 'a', kind: 'grid', source: 'east' },
{ id: 'b', kind: 'grid', source: 'west' },
],
links: [
{ from: 'table', to: 'chart', on: 'region' }, // the table's selection filters the chart
{ from: 'a', to: 'b', on: 'region' }, // refused: two engines, no shared router
],
};
const seen = [validateDashboardSpec(spec).ok];
const dashboard = createDashboard(el, spec, {
createGrid, createHeadlessGrid, createPushdownSource, createLayout, createChart, bindLeaflet, bindDeck,
});
seen.push(dashboard.panels().length);
dashboard.panel('table').grid.selection.set(['r1']); // a south row
flushFrames(); // the next frame
seen.push(dashboard.panel('chart').viewer.data().categories.join('+'));
seen.push(dashboard.problems().map((p) => p.path).join(', ')); // dashboard:link:a->b, at links[1]
// The validator names every problem by path, without building anything.
seen.push(validateDashboardSpec({ panels: [{ id: 'x', kind: 'gauge' }] }).problems[0].path);
dashboard.destroy();
return seen.join(' | '); // true | 6 | south | links[1] | panels[0].kind
A proposal over a source, checked and applied; a view saved and restored; a presentation stepped
const { createTestDom, flushFrames } = await import('../packages/dom/src/renderer/testdom.js');
const { createGrid, createHeadlessGrid } = await import('../packages/dom/src/index.js');
const { createLayout } = await import('../packages/modules/layout/index.js');
const { createChart } = await import('../packages/modules/charts/index.js');
const { createDashboard } = await import('../packages/modules/dashboard/index.js');
const { document, root } = createTestDom({ width: 1200, height: 800 });
const el = document.createElement('div');
root.appendChild(el);
const rows = Array.from({ length: 12 }, (_, i) => ({
id: 'r' + i, region: ['north', 'south', 'east'][i % 3], sales: 10 * (i + 1),
}));
// The same ask() createAI takes. It sees the columns and the panel kinds, never
// the rows. A stand-in model answers with a table, a chart and a column that
// does not exist.
let asked = null;
const ask = async (payload) => {
asked = payload.schema;
return { structured: { panels: [
{ id: 'top', kind: 'grid', source: 'sales', options: { selection: 'single' } },
{ id: 'byRegion', kind: 'chart', source: 'grid:top', options: { type: 'bar', x: 'region', y: 'sales' } },
{ id: 'trend', kind: 'chart', source: 'sales', options: { type: 'line', x: 'month', y: 'sales' } },
] } };
};
const dashboard = createDashboard(el, { sources: { sales: { kind: 'rows', rows, rowKey: 'id' } }, panels: [] },
{ createGrid, createHeadlessGrid, createLayout, createChart, ai: { ask } });
const { spec, warnings } = await dashboard.propose('sales by region and a table', { source: 'sales' });
const seen = [asked.columns.map((c) => c.id).join(','), spec.panels.length, warnings.length]; // 'trend' named no column
dashboard.apply(spec); // accepted: now it is built
flushFrames();
// A view is the spec plus every grid panel's state.
dashboard.panel('top').grid.filters.set({ col: 'region', op: 'eq', value: 'east' });
const view = dashboard.saveView('East only');
dashboard.panel('top').grid.filters.clear();
dashboard.applyView(view.id);
seen.push(dashboard.views().length, dashboard.panel('top').grid.rows.matchCount());
// Presentation steps through the panels, each filling the dashboard.
dashboard.present();
dashboard.step();
seen.push(dashboard.layout.maximised());
dashboard.stopPresenting();
dashboard.destroy();
return seen.join(' | '); // id,region,sales | 2 | 1 | 1 | 4 | byRegion
Type reference
Generated from the type declarations, so it always matches the release. Each surface lists its properties, its methods and the events it raises as three tables; an option or value type lists its members once.
Dashboard composition
DashboardSpec
A dashboard described as data: a layout, the panels in it, the sources they read and the links between them. `createDashboard` builds it and `dashboard.spec()` hands it back, placements included, so a host can store and version it.
| Property | Type | Description |
|---|---|---|
| layout | DashboardLayoutSpec | The layout the panels sit in: any `createLayout` option, with `windows` placing panels by id (`{ id, xPos, yPos, xSize, ySize, … }`). A panel with no window entry is auto-placed. (optional) |
| panels | DashboardPanel[] | The panels, one viewer each, in the order their windows are mounted. |
| sources | Record<string, DashboardSource> | The named sources panels read, by id. (optional) |
| links | DashboardLink[] | Selection-to-filter links between panels. (optional) |
DashboardLayoutSpec
The layout part of a spec: the layout module's own options, windows placed by panel id.
| Property | Type | Description |
|---|---|---|
| columns | number | Cell columns across the dashboard (the layout's default otherwise). (optional) |
| rows | number | Cell rows down the dashboard (the layout's default otherwise). (optional) |
| gap | number | string | The gap between cells. (optional) |
| windows | Array<{ id: string; xPos?: number; yPos?: number; xSize?: number; ySize?: number; [option: string]: unknown }> | Each panel's window, by panel id: its placement and any window option (title, chrome, movable, …). (optional) |
| [option: string] | unknown | Every other key is handed to `createLayout` unchanged. |
DashboardPanel
One panel: a viewer in a window.
| Property | Type | Description |
|---|---|---|
| id | string | A unique id; its window has the same id. |
| kind | DashboardPanelKind 'grid' | 'chart' | 'kpi' | 'map' | 'html' | Which viewer: a grid, a chart, a KPI panel, a map, or static html. |
| title | string | The window's title, when its window entry declares none. (optional) |
| source | string | The source it reads: a key of `sources`, or `grid:<panel id>` to view that grid panel's grid (it narrows when that grid is filtered). Required for `chart` and `map`; an `html` panel reads none. (optional) |
| binding | 'chart' | 'leaflet' | 'deckgl' | For a `map` panel, what draws it: `'chart'` (a map chart such as `markermap` or `choropleth`, the default), `'leaflet'` (`bindLeaflet`) or `'deckgl'` (`bindDeck`), each needing its module on the page. (optional) |
| options | Record<string, unknown> | The viewer's own options, passed to `createGrid`, `createChart`, `createKPI`, `bindLeaflet` or `bindDeck` unchanged - the dashboard adds only the element and the grid. A Leaflet panel's `map` and a deck.gl panel's `deck` may be a function of the panel's element that makes one. An `html` panel takes `{ html }` (the host's own markup) or `{ text }`. (optional) |
DashboardRowsSource
Rows held in the page.
| Property | Type | Description |
|---|---|---|
| kind | 'rows' | Rows held in the page. |
| rows | unknown[] | The rows. |
| columns | unknown[] | The grid columns; one per key of the first rows when omitted. (optional) |
| rowKey | unknown | The row identity every grid over this source is given. (optional) |
DashboardPushdownSource
A pushdown adapter: the rows stay in the engine.
| Property | Type | Description |
|---|---|---|
| kind | 'pushdown' | A pushdown adapter: the rows stay in the engine. |
| adapter | { execute: (...args: unknown[]) => unknown; [key: string]: unknown } | The adapter, handed to `createPushdownSource` with every key but `kind`, `columns` and `rowKey`. |
| columns | unknown[] | The grid columns. (optional) |
| rowKey | unknown | The row identity every grid over this source is given. (optional) |
| [option: string] | unknown | Any other `createPushdownSource` option (`pageSize`, `aggregates`, …). |
DashboardRouterSource
A Data Router route: each grid reading it is attached with `router.attach(grid, predicate, route)`.
| Property | Type | Description |
|---|---|---|
| kind | 'router' | A Data Router route. |
| router | { attach: (...args: unknown[]) => unknown; [key: string]: unknown } | The router. Load it after the dashboard is built: a route sees what arrives after it is attached. |
| predicate | unknown | The partition value or predicate the route matches. |
| route | Record<string, unknown> | The route's options (`transform`, `rollup`, …). (optional) |
| columns | unknown[] | The grid columns. (optional) |
| rowKey | unknown | The row identity every grid over this source is given. (optional) |
DashboardGridSource
Another panel's grid, as `grid:<panel id>` does inline.
| Property | Type | Description |
|---|---|---|
| kind | 'grid' | Another panel's grid. |
| of | string | The grid panel whose grid is viewed. |
DashboardLink
A link: the rows selected in the `from` grid panel filter the `to` panel. Over one Data Router it is a `router.relate()` edge; otherwise a named `filters.where()` predicate on the target's grid. Two pushdown sources with no router between them, or two routers, are refused by name.
| Property | Type | Description |
|---|---|---|
| from | string | The grid panel whose selection drives the link. |
| to | string | The panel it filters. |
| on | string | { from: string; to: string } | ((selected: Array<Record<string, unknown>>) => (row: Record<string, unknown>) => boolean) | The relation: a column both sides share, `{ from, to }` naming each side's column, or (over one router) the router's own relation function. |
DashboardProblem
Something the dashboard refused or reported, by catalogued id.
| Property | Type | Description |
|---|---|---|
| id | string | The catalogued warning id: `dashboard:panel:<id>`, `dashboard:link:<from>-><to>`, `dashboard:spec:<path>`, … |
| path | string | Where in the spec, e.g. `panels[2].kind`. |
| message | string | What happened. |
DashboardOptions
The third argument of {@link createDashboard}: the factories the dashboard builds with, and the AI callback. A factory not given here is looked up on its script-tag global (`LatticeGrid`, `LatticeGridLayout`, `LatticeGridKPI`, `LatticeGridLeaflet`, `LatticeGridDeck`); a panel whose factory is on neither is refused by name.
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| createGrid | (el: HTMLElement, config: Record<string, unknown>) => Grid | el: HTMLElementconfig: Record<string, unknown> | => Grid | The grid's `createGrid`, for `grid` panels. (optional) |
| createHeadlessGrid | (config: Record<string, unknown>) => Grid | config: Record<string, unknown> | => Grid | The grid's `createHeadlessGrid`, which holds a chart's, KPI panel's or map's source. (optional) |
| createPushdownSource | (config: Record<string, unknown>) => unknown | config: Record<string, unknown> | => unknown | The grid's `createPushdownSource`, for `pushdown` sources. (optional) |
| createChart | (spec: Record<string, unknown>) => unknown | spec: Record<string, unknown> | => unknown | The charts module's `createChart`, for `chart` panels and map charts. (optional) |
| createLayout | (el: HTMLElement, config: Record<string, unknown>) => unknown | el: HTMLElementconfig: Record<string, unknown> | => unknown | The layout module's `createLayout`. Without it nothing is built. (optional) |
| createKPI | (el: HTMLElement, config: Record<string, unknown>) => unknown | el: HTMLElementconfig: Record<string, unknown> | => unknown | The KPI module's `createKPI`, for `kpi` panels. (optional) |
| bindLeaflet | (grid: Grid, options: Record<string, unknown>) => unknown | grid: Gridoptions: Record<string, unknown> | => unknown | The Leaflet module's `bindLeaflet`, for `map` panels with `binding: 'leaflet'`. (optional) |
| bindDeck | (grid: Grid, options: Record<string, unknown>) => unknown | grid: Gridoptions: Record<string, unknown> | => unknown | The deck.gl module's `bindDeck`, for `map` panels with `binding: 'deckgl'`. (optional) |
| ai | ((payload: Record<string, unknown>) => Promise<unknown>) | { ask: (payload: Record<string, unknown>) => Promise<unknown> } | (payload: Record<string, unknown>) => Promise<unknown> | | { ask: (payload: Record<string, unknown>) => Promise<unknown> } | The AI callback `propose()` asks: the same `ask(payload)` `createAI` takes (or `{ ask }`). Without it `propose()` declines by name. (optional) |
DashboardPanelHandle
One built panel.
| Property | Type | Description |
|---|---|---|
| id | string | The panel id. |
| kind | DashboardPanelKind 'grid' | 'chart' | 'kpi' | 'map' | 'html' | Its kind. |
| el | HTMLElement | The element its viewer was mounted in, inside its window. |
| grid | Grid | null | The grid it reads (a grid panel's own), or null. |
| viewer | unknown | What its factory returned: the grid, chart, KPI panel or binding; null for html. |
DashboardProposal
What {@link Dashboard.propose} resolves to.
| Property | Type | Description |
|---|---|---|
| spec | DashboardSpec | null | The checked spec to accept with `apply(spec)`, or null when nothing usable came back. |
| warnings | string[] | Everything dropped or refused, one line each. |
DashboardView
A saved dashboard view: the spec and each grid panel's state.
| Property | Type | Description |
|---|---|---|
| id | string | The view's id. |
| name | string | Its name. |
| spec | DashboardSpec | The dashboard spec, placements included. |
| grids | Record<string, GridState> | Each grid panel's `grid.state.get()`, by panel id. |
Dashboard
A built dashboard.
Properties
| Property | Type | Description |
|---|---|---|
| el | HTMLElement | The element it is mounted on. (read-only) |
| layout | unknown | The layout the panels sit in, or null when nothing was built. (read-only) |
| presenting | string | null | The panel a presentation shows, or null. (read-only) |
Methods
| Method | Signature | Parameters | Returns | Description |
|---|---|---|---|---|
| spec | (): DashboardSpec | - | DashboardSpec | The dashboard as built - its panels, the sources by id, the links that were made and each window's live placement - as a spec that rebuilds it. |
| apply | (spec: DashboardSpec): DashboardProblem[] | spec: DashboardSpec | DashboardProblem[] | Tear down and build this spec in its place: how a host accepts a proposal or restores a stored spec. Returns what was refused. |
| panels | (): string[] | - | string[] | The built panels' ids, in spec order. |
| panel | (id: string): DashboardPanelHandle | null | id: string | DashboardPanelHandle | null | One built panel, or null. |
| problems | (): DashboardProblem[] | - | DashboardProblem[] | Everything the last build refused or reported. |
| propose | (prompt: string, request: { source: string; rows?: boolean; maxRows?: number; signal?: AbortSignal }): Promise<DashboardProposal> | prompt: stringrequest: { source: string; rows?: boolean; maxRows?: number; signal?: AbortSignal } | Promise<DashboardProposal> | Ask the AI callback for a spec over one source's columns. The model sees the column names and types and the panel kinds this page can draw - rows only with `rows: true` (at most `maxRows`, default 50). A panel naming a column the source lacks, a kind the page cannot draw or another source is dropped with a warning. Nothing is built: accept with `apply(spec)`. Without an AI callback it declines by name. |
| saveView | (name: string): DashboardView | null | name: string | DashboardView | null | Save the spec and every grid panel's state as one named view; a name already saved is replaced. |
| views | (): DashboardView[] | - | DashboardView[] | The saved views, in the order first saved. |
| applyView | (view: string | DashboardView): boolean | view: string | DashboardView | boolean | Restore a view by id, or one the host stored: the layout and every grid's state. |
| present | (options?: { panels?: string[]; from?: number }): boolean | options?: { panels?: string[]; from?: number } | boolean | Present the panels one at a time, each filling the dashboard (the layout's maximise). |
| step | (by?: number): number | by?: number | number | Move the presentation by this many panels, clamped; the position shown, or -1. |
| stopPresenting | (): boolean | - | boolean | End the presentation and put the dashboard back. |
| destroy | (): void | - | void | Tear everything down: links, viewers, grids, router attachments and the layout. |
Events
No events.