datagrout-panels
The DataGrout Smart Panel model: declarative UI
stored as logic-cell facts, parsed into a renderer-agnostic Panel tree.
A Smart Panel is a set of Prolog facts in a cell's _panels namespace:
panel(revenue_chart, bar_chart, my_app).
panel_prop(revenue_chart, title, 'Revenue by Month').
panel_source(revenue_chart, my_app, 'monthly_revenue(Month, Amt)').
That makes a panel queryable, composable and versioned like any other
knowledge in the cell, and derived rather than stored: its panel_source
goal re-runs against the rulebase every time the panel is read.
This crate parses those facts and stops. Rendering lives in separate crates —
datagrout-panels-egui
(native GUI) and
datagrout-panels-mcp
(MCP Apps) — so a consumer that only transpiles never links a GUI toolkit.
Two ways in
From smart_panel.list — the intended path. One gateway call returns every
panel with its props, a row preview and its parts already resolved:
use Panel;
// `response` is the JSON the `smart_panel.list` tool returned.
let panels: = all_from_list;
for p in &panels
From raw facts — when you need full row sets or field metadata. Run each
goal in goals through logic.query and hand the solution rows over:
use ;
let facts = PanelFacts ;
let panels = all_from_facts;
The model
Panel { id, kind, namespace, props, rows, source, fields, children, published }.
- Kinds mirror the gateway's publish tool: charts,
table,metric,gauge,markdown,dashboard, and the form kinds (form,text_input,dropdown,button, …). A kind this crate predates parses asPanelKind::Unknown(String)so a renderer can draw a placeholder instead of failing. - Composition is by the child's
parentprop. A dashboard's children and a form's fields are their ownpanel/3facts naming the container; the parser inverts that edge intochildrenandfields, and parts do not appear at the top level. - Props are typed.
columnsis a list,publisheda boolean.propsis aBTreeMap<String, serde_json::Value>with coercing accessors:prop_str,prop_list,prop_bool,prop_f64, plustitle(),description(),columns(),parent(),slot(). - Rows are
Vec<Vec<Value>>afternormalize_rows, which accepts the list-of-lists snapshot form and the object-per-solution formlogic.queryreturns. - Identity is
(namespace, id); duplicate registrations from a republish collapse to one.
What a fetch layer has to know
This crate does not fetch. Whatever does should expect:
- An undefined predicate is an error from
logic.query, not an empty result. Treat it as empty. - Results over roughly 48 KB come back as a
cache_refrather than inline;prism.paginatepages them, as solution rows. panel_data(Id, Rows)is an ordered snapshot;panel_sourcerows come back as objects whose key order is not the query's variable order. Prefer the snapshot when both exist.
The full contract, including renderer conformance, is in
SPEC.md.
Security
Panel facts may have been asserted by an agent. Treat every prop and row as untrusted text; the renderers escape accordingly.
License
MIT OR Apache-2.0, at your option.