Reference

Streaming UI Script language.

A compact, line-oriented language designed for LLMs. Each statement is a single assignment of an identifier to an expression. The parser is forgiving — invalid lines are dropped and valid lines render immediately.

Statements

KindSyntaxExample
Component assignmentname = Component(args...)card = Card([header])
State variable$name = defaultValue$days = "7"
Query / Mutationname = Query(...) | Mutation(...)data = Query("get_users", {}, {rows: []})

Expressions

TypeSyntaxExample
String (single-line)"text" or 'text'"Hello"
String (multi-line)`text` (backticks)`line 1
line 2`
— great for Script(...) bodies
Number123, -5, 12.542
Booleantrue / falsetrue
Nullnull
Array[a, b, c]["Mon","Tue"]
Object{key: value, ...}{limit: 10}
Referenceidentifiercard
State reference$name$days
Member accessobj.fielddata.rows.title (array pluck)
Component callComponent(args)Header("Hi")
Builtin call@Builtin(args)@Filter(rows, "name", "contains", $q)
Ternarycond ? a : b$open ? form : null
Binarya + b, a == b, a && b"" + $days + " days"
Unary!a, -a!$open

Comments

The parser strips three comment styles silently. They never appear in the rendered output and are safe to mix with real statements:

StyleSyntaxNotes
Line// rest of lineSkipped to end of line. Canonical form — used by editor comment-toggle shortcuts.
Line (alt)# rest of lineShell / Python-style alternative. Identical semantics to //; convenient for marking sections in chat-style examples.
Block/* ... */Spans multiple lines. Useful for temporarily disabling a chunk of script.

Comments inside string literals ("...", '...', and multi-line `...`) are part of the string and are not stripped. LLM-generated responses should still leave comments out to save tokens — identifiers double as documentation.

Reactive state

Variables prefixed with $ are reactive. Declare them with a literal default. When a state value changes, every expression that read it re-evaluates and the affected components re-render.

$days = "7"
$showEdit = false
filter = Select("days", [SelectItem("7","7d"), SelectItem("30","30d")], null, null, $days)
data = Query("usage", {days: $days}, {rows: []})

Passing a $variable as the value argument of an Input, Select, or Checkbox enables two-way binding: user input updates the variable, which in turn re-runs anything that depends on it.

Built-in functions

GroupFunctions
Aggregation@Count, @Sum, @Avg, @Min, @Max, @First, @Last
Numeric@Round(n, decimals?), @Abs, @Floor, @Ceil
Filter / sort@Filter(arr, "field", "op", value), @Sort(arr, "field", "asc"|"desc")
Array growth@Push(arr, value) (returns new array with value appended), @Concat(a, b)
Iteration@Each(arr, "varName", template)varName is local to template
Array shortcuts$rows.length, $rows.first, $rows.last, $rows.field (pluck)
Action steps@Run(ref), @Set($var, value), @Reset($a, $b), @ToAssistant("msg"), @OpenUrl("url"), @Navigate("/path")

Queries and mutations

Query(toolName, args, defaults, refreshSeconds?) reads data and runs immediately. The defaults argument is rendered while the response is in flight, and the optional refreshSeconds sets up auto-polling. Mutation(toolName, args) only runs when a button or action invokes @Run(ref).

$title = ""
create = Mutation("create_ticket", {title: $title})
list   = Query("list_tickets", {}, {rows: []})
btn    = Button("Create", Action([@Run(create), @Run(list), @Reset($title)]))

Hoisting and streaming

Forward references are allowed. The renderer puts skeletons in for missing identifiers, then swaps them in as the LLM streams more lines. Always lay out the structure first (root = Stack([a, b, c])) and define the children below — the user sees the shell instantly.

JavaScript interactions

Two additional surfaces are always available: Script("id", "body", deps?) for effect-style behaviour, and @Js("code") as an action step. Both share a ctx bridge that exposes reactive state, registered tools, DOM refs, and lifecycle hooks. They are part of the default ("full") system prompt; use getSystemPrompt({ mode: "chat" }) when you want a compact, chat-focused prompt without these surfaces. See the dedicated JavaScript interactions page for the full API.

Routing

Four routing primitives ship with every renderer: Routes(items, default?), Route(path, content), NavLink(label, to, …), and the @Navigate("/path") action step. The current path is exposed reactively as $route; matched URL parameters land in the params loop variable inside each Route's content. The default ("full") prompt documents the routing surface; switch to getSystemPrompt({ mode: "chat" }) for the compact chat-focused prompt that omits it. See the dedicated routing reference (and live demo) for the full surface.

Error handling

The parser collects errors per line and continues past invalid input. The renderer logs unknown components and shows an inline placeholder. The element fires an error event with the parse errors so you can surface them in your dev tools.