-
-
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.
crates/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. -
view/edit(role/claim visibility) andpattern/format/min/max(format validation) are client-side hints only right now, not an enforced security boundary. Older versions of this page credited aserve.rsmodule with enforcing these server-side (redact_gated_fields/check_edit_gates/check_field_validations) — that file, and the live server that ran it, no longer exist (removed with the interpreter).emit-uistill emits the hide/disable hints into the generated page, but there's no server left behind them to enforce anything a curious user couldn't just bypass by reading the page source. This is genuinely different from the README's headline field-masking example (requires(role: ...)zeroing a struct field on function return, inside the compiled binary itself) — that mechanism is real and compiled, independent ofemit-ui/screenentirely; thescreenDSL's ownview/editgates are the part that lost server enforcement. See Honest Scope & Roadmap. - Tracked-but-not-wired (see
crates/compiler/UI_DSL_TODO.md):paginate,searchable/sortableas DSL keys — real sorting/ search/pagination existed unconditionally per struct via a live server's own table route before the interpreter's removal; that route doesn't exist right now either. - 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 --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. This is a static, build-time layering —
there's no live server to re-read the file on a TTL right now; a
redeployed theme means re-running emit-ui.
There is no nirdosha serve subcommand in this binary right now (removed
along with the interpreter). A previous version of this page described
one running a tiny_http server exposing the inferred functions as a
JSON API (POST /api/<fn>) with OIDC JWKS/issuer/audience gating — that
was real when the interpreter existed, and is documented as a real design
target for a compiled replacement, but nothing in this repository
implements it today. See Honest Scope & Roadmap
and PUBLIC_ROADMAP.md
for the current, exact status.
The zero-syntax path means an LLM asked for "a CRUD app over Product"
doesn't need to also generate any UI code or HTML — it writes struct Product and five naming-convention functions, and a working, themeable
static page falls out. What that page can honestly claim today is
narrower than an older version of this section implied: it's real,
generated, and — for whatever role-gated function-level logic the
program itself compiles (requires(role: ...), field masking on
return) — genuinely safe underneath, independent of any UI. What it
isn't, right now, is a page whose own view/edit/validation hints
are enforced by anything other than the client-side JS itself — a
curious user reading the generated page's own source can see exactly
what those hints do and don't stop them from doing, and that's an
accurate read of the current state, not a bug in the generated code.
See "Extending the catalog" above and
Honest Scope & Roadmap for the fuller
picture of what's compiled vs. not right now.
Why
How
For LLM agents
Using it