-
-
Notifications
You must be signed in to change notification settings - Fork 0
UI Engine
Nirdosha ships a declarative UI DSL plus a UI generator that derives a
full CRUD + dashboard web application from a program's struct
declarations and its function-naming conventions — no UI syntax needed
for the common case.
compiler/src/ui_gen.rs looks for list_<struct>, create_<struct>,
update_<struct>, delete_<struct>, get_<struct>, and
stat_<name>/chart_<name> functions and generates a complete HTML/JS
CRUD app + dashboard from them alone.
For what a naming convention can't express (a friendlier title, a relabeled field, a custom action), there is an additive DSL:
struct Product {
id: i64,
name: str,
price_cents: i64,
stock: i64,
}
fn list_product() -> Result(json, str) { ... }
fn create_product(p: Product) -> Result(i64, str) requires(role: "admin") { ... }
fn restock_product(id: i64) -> Result(i64, str) requires(role: "admin") { ... }
screen Product {
title: "Catalog"
field name { label: "Product Name" pattern: "^[A-Za-z0-9 ]+$" }
field stock { min: 0 }
action "Restock +10" -> restock_product {
style: "outlined"
confirm: "Restock this product by 10 units?"
}
}
dashboard {
tile "Products" -> stat_product_count
chart "By Price" -> chart_products_by_price
}
-
screen/dashboardare real reserved keywords (top-level items likestruct/fn);field/action/tile/chartare contextual keywords. - Typechecked:
screen <Name>must name a realstruct;field/actiontargets must resolve;view/editmust berole(...)/claim(...);patternmust compile as a regex and only apply to astrfield;formatmust be one of"email"/"phone"/"date"/"url"/"uuid";min/maxmust only apply to a numeric field. -
Inert to native codegen —
nirdosha buildcompiles a program containingscreen/dashboardcleanly (codegen never inspects them). They're consumed only bynirdosha emit-ui/nirdosha serve. -
view/edit(role/claim visibility) andpattern/format/min/max(format validation) are enforced for real, both client- and server-side —serve.rsis the actual security/validation boundary (redact_gated_fields,check_edit_gates,check_field_validations); the client-side version is cosmetic convenience only. - Tracked-but-not-wired (see
compiler/UI_DSL_TODO.md):paginate,searchable/sortableas DSL keys —nirdosha serve --dbalready provides real sorting/search/pagination unconditionally per struct via its own table route, independent of these two keys. - Deliberately closed, not a fixed ceiling: four built-in chart shapes
(inline-SVG
bar_chart/graph/heatmap/timeline, no Recharts/D3/ Victory dependency) are joined byrender: "chart"— a bounded grammar-of-graphics config, not a fifth hardcoded shape — and, if that still isn't enough, a Rust crate can contribute an entirely newlayoutwidget kind. See "Extending the catalog" below. Four fixed built-in animations (fade-in/slide-up/scale-in/pop, no custom transitions or Framer-Motion-style gesture/physics motion) and a fixed seven-kind form-control set (text/number/checkbox/select/struct/ readonly/date — no rich text editor, color picker, drag-drop upload preview, autocomplete, calendar/scheduler, or signature pad) remain genuinely closed, not yet extensible the same way. Seecrates/compiler/UI_DSL_TODO.md's "Deliberate non-goals" section for the full rationale.
Two ways to grow the chart/component vocabulary above, both still fully closed and typechecked — no arbitrary markup, no runtime registration, nothing an agent or a served request can add on its own.
render: "chart" — a grammar-of-graphics config (mark ×
encode <channel>) for chart shapes the four fixed ones don't cover,
additive alongside them (also wired into workspace panel { ... },
same grammar):
dashboard {
visual "Revenue by month" -> chart_revenue_by_month {
render: "chart"
mark: "bar"
encode x { field: "month" type: "temporal" }
encode y { field: "amount" type: "quantitative" aggregate: "sum" }
}
}
A Rust crate contributing a new layout widget kind — for
something no chart shape covers at all. nirdosha emit-ui --manifest-path <Cargo.toml> (or an auto-detected Cargo.toml sitting
next to the .nir file) discovers any dependency tagged
[package.metadata.nirdosha] kind = "nir-ui-component" in the app's own
Cargo.toml and links its JS automatically — no hand-written Rust
glue needed for the common case:
layout {
sparkline { source: recent_sales_totals field: "amount" }
}
Both resolve entirely at nirdosha build/emit-ui time — before the
compiled artifact exists, let alone before an agent ever talks to it.
There's no admin API, no hot-reload, no runtime negotiation that adds
or changes a component after that point: growing the catalog is a human
adding a reviewed Cargo dependency once, never something reachable from
a served request or a chat turn. See
rfcs/0009
for the full design, the real reference plugin crate
(crates/ui-plugin-example-sparkline), and exactly what's shipped vs.
still open — widening a component past a single layout widget kind
to a full typed catalog entry, and Cargo-driven discovery for native
builtins (a different, harder problem), both remain future work.
nirdosha emit-ui/serve --theme theme.json layers a full design system
on the baked-in Material Design 3 defaults — brand/neutral color ramps,
fonts, radius, shadow, density, real entrance/hover/press animations,
three dark-mode strategies, and CSS-only layout shell variants
(LANGUAGE.md §11b). Every section is optional; a program with no
--theme renders exactly as before this existed. nirdosha serve
re-reads the file on a TTL, so a redeployed theme takes effect without
restarting the server.
nirdosha serve <file.nir> runs a tiny_http server exposing the
inferred functions as a JSON API (POST /api/<fn>), with optional OIDC
JWKS/issuer/audience gating — the same identity primitives as
Language Features, applied to HTTP.
The zero-syntax path means an LLM asked for "a CRUD app over Product"
doesn't need to also generate any UI code, HTML, or client-side validation
logic — it writes struct Product and five naming-convention functions,
and a working, role-gated, themeable web app falls out. The demo referenced
in the README (examples/vendor_ops.nir, 334 lines, zero UI code) is the
concrete proof: a themed dashboard, a sortable/searchable table, and a
role-gated approval action, all derived, all enforced server-side — not
generated client JS that a curious user could bypass by reading the page
source.
Why
How
For LLM agents
Using it