Skip to content

The format

A readable tour of GistUI Format 1.0 - text and lines, grammar, recovery rules, catalogs, the abstract tree, runtime, limits, security and versioning.

Updated View as Markdown

This page summarises the specification. The normative text is spec/gistui-format.md in the repository; section numbers below refer to it.

Text and lines

Sections 4 and 5.

  • A program is UTF-8 text. A byte-order mark is ignored. Lines end with LF, CRLF or CR.
  • Each line is classified: a statement, a table row, a comment (#), a fence, or prose.
  • A prose line (anything else) is ignored and reported as prose-ignored. It never breaks a program.
  • There are two modes. In document mode the whole text is a program. In inline mode the text is chat with programs in gistui code fences.
  • #gistui 1 on the first line is the version pragma. It is optional.
  • A statement normally ends at its line end. An open bracket continues it onto the next line.

Grammar

Section 6.

program   = { line NL }
stmt      = ID "=" ( expr | table )
stateDecl = "$" ID "=" expr
patch     = ID "." ID "=" expr
append    = ID "+=" expr
table     = row { NL row }          row = "|" cell { "|" cell }
expr      = lambda | cond
lambda    = ( ID | "(" [ID {"," ID}] ")" ) "=>" expr
cond      = or [ "?" expr ":" expr ]
primary   = call | builtin | STRING | NUMBER | "true" | "false" | "null"
          | "$" ID | ID | "[" [args] "]" | "{" [pairs] "}" | "(" expr ")"
call      = COMP "(" [args] ")"     builtin = "@" ID "(" [args] ")"
arg       = ID (":"|"=") expr | expr
STRING    = JSON string    COMP = [A-Z]\w*    ID = [a-z_]\w*

Recovery

Sections 7 and 8.

Text that is not strict is still read, by numbered rules. A processor must apply them and must report each application. This is what makes two implementations agree on imperfect input.

Example Rule (in short) Diagnostic
A call still open at the line end Closed there lenient-syntax
A string still open at the line end Ended there unterminated-string
'text' A string lenient-syntax
key=value, "key": value A named argument lenient-syntax
Card(a b) The missing comma is supplied lenient-syntax
card(…) The component Card lenient-syntax
// note A comment lenient-syntax
.5, +3, 05 The numbers 0.5, 3, 5 lenient-syntax
An unexpected character Skipped; the statement is kept parse-failed, fixed
Nothing recoverable The statement is dropped parse-failed

A generator must write strict text and must not rely on recovery. Recovery is for the processor.

Catalogs

Section 9.

A catalog lists components. For each: its name, its positional arguments in order, whether it takes children (and of which types), its props with their types, its aliases, and an optional table mapping.

Prop types: string (optionally format: "url"), number, boolean, enum, node, nodes, data, array, object, action, state, any.

Bare words are resolved by the catalog, in this order:

  1. key:WORD where the prop is an enum: an enum value.
  2. A word that is a statement id: a reference. A defined statement always wins.
  3. A word that is a boolean prop of the enclosing component: a flag.
  4. A word that is a value of exactly one enum prop of the enclosing component: that prop.
  5. Anything else: a reference that may be defined later.

Misspelled names use the closest-name rule: a name within a small edit distance of exactly one catalog name is taken as that name, and reported.

A catalog has a JSON manifest form (schemas/catalog.schema.json), so tools in any language can read it.

The tree

Section 13.

Processing gives an abstract tree: a set of nodes, each with an id, a type, props, children and dynamic entries for runtime expressions.

  • Node ids are stable. A statement’s node has the statement’s id; an inline component gets a derived id such as kpis/1. A renderer can key on them.
  • Special types: #text for a string child, #pending for a reference not defined yet, #unknown for a component nothing matches, #expr for a runtime expression in child position.
  • The tree has a JSON form (schemas/tree.schema.json), which the conformance suite compares.

Edits

Section 14. Replace (id = …), patch (id.prop = …), append (id += …) and delete (id = null). Described for users in Edits and multi-turn.

End of stream and validity

Sections 15 to 17.

At the end, open brackets are closed, references to nothing are dropped, cycles are broken, and unused statements are reported.

Chunking invariance: a processor must give the same final tree and diagnostics however the input is divided, from one piece to one character at a time.

A program has two validity flags:

  • strict: no diagnostic at all. This is what a generator should produce.
  • lenient: every problem was repaired by a rule of the format.

Runtime

Sections 18 to 20.

  • Values: null, booleans, numbers, text, lists, objects, component references.
  • State: $name variables with declared defaults; a host may supply initial values.
  • Operators and built-ins: a fixed set (see State and expressions). No user-defined functions.
  • Queries (@query) call a tool from the query set, re-run when their arguments change, and have a default value.
  • Mutations (@mutation) call a tool from the mutation set, and only through @run in an action list.
  • Action lists run steps in order and stop at the first failure.

Envelopes

Sections 20.3 and 26.4.

Two JSON objects cross the boundary between a renderer and its host.

The action envelope is what a renderer reports when a person acts (schemas/action.schema.json). Its type is one of send, submit, open, emit, select, change, error. The form submit is described in Actions and forms.

The capability object is what a host tells a generator it may write (schemas/capabilities.schema.json):

{
  "gistui": "1.0",
  "profiles": ["core", "interactive", "tools"],
  "mode": "document",
  "edit": false,
  "catalog": { "id": "https://gistui.com/catalogs/default", "version": "1.0.0" },
  "tools": [
    { "name": "get_sales", "kind": "query", "args": "{range:str}", "description": "Sales by month" },
    { "name": "update_goal", "kind": "mutation", "args": "{goal:num}" }
  ]
}

A generator must not write state or actions for a host without the interactive profile, nor queries or mutations without tools.

Limits

Section 21. The values are part of 1.0, because they decide which diagnostics a program gets.

Processor limit Value
Nesting depth in one statement 100
Binary operators in one chain 500
Nodes owned by one statement 5,000
Nodes in one program 50,000
Values in one data statement 200,000
Component instances in the tree 50,000
Depth of the tree 400
Runtime limit Value
Evaluation steps in one pass 500,000
Items of one @each 5,000
Generated nodes 20,000
Length of text made by + or @join 1,000,000
Queries with a refresh timer 20
Refresh interval at least 5 s
Tool calls 60 in any 10 s

No input may make a processor or runtime fail with an unhandled error, exhaust the stack, or hang.

Security

Section 23.

  • Threat model: the program is written by a model that may have been steered by hostile content. The host, the catalog and the renderer are trusted; the program is not.
  • No code execution. Expressions are interpreted. There is no way to reach the host environment.
  • URL schemes. Links and @open allow http, https, mailto and tel.
  • URLs that load by themselves (images, video, backgrounds) follow a host policy: an allow-list of hosts, and a rule that a URL assembled from data is not loaded unless its host is listed. This closes the path where an image request carries private data out.
  • Markdown is rendered without raw HTML.
  • Tools. Queries must be read-only. Mutations run only from a person’s action.
  • Messages from the UI must be distinguishable from typed text when they go back to a model.
  • Deception. A renderer should keep generated UI visually inside its container.

The user-facing guide is Security.

Accessibility and internationalisation

Sections 24 and 25. A renderer must expose roles, names and states for interactive components, support keyboard use, manage focus for dialogs, and honour reduced motion. Text direction follows the content; number and date formatting follows the host locale.

Versioning

Section 26.

  • The version is 1.0. A processor that sees an unknown major version in the pragma reports unsupported-version and continues.
  • A host must accept diagnostic codes it does not know and treat them by severity.
  • Media type: text/vnd.gistui (not registered yet). File extension: .gistui. Inline mode is text/markdown with gistui fences.
  • Catalogs have ids and versions of their own.
Navigation

Type to search…

↑↓ navigate↵ selectEsc close