Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ windows-sys = { version = "0.61.2", features = [

# Test dependencies
criterion = "0.8.2"
insta = { version = "1.48.0", features = ["json"] }
tempfile = "3.27.0"

# Build dependencies
Expand Down
68 changes: 68 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -2989,6 +2989,74 @@ function returns `NULL`, call `webui_last_error()` for a human-readable diagnost

The CLI specification and usage details are maintained in [crates/webui-cli/README.md](crates/webui-cli/README.md).

### Render State JSON Schema

`webui schema <protocol.bin>` analyzes the compiled entry and writes a
JSON Schema draft 2020-12 document to stdout. `--entry` selects the entry
fragment and `--title` sets the schema title.

`webui build --emit-schema` runs the same analysis directly against the
in-memory protocol and writes `<protocol-stem>.state.schema.json` beside the
protocol. Schema generation and serialization complete before build outputs are
written. Emission is opt-in because schemas are build-time tooling artifacts,
not render-time requirements.

For entries without routes, the output is a normal object schema. Inference
uses the following rules:

- Plain text and normal attribute values accept JSON scalars: `string`,
`number`, or `boolean`. Complex `:property` bindings remain unconstrained
until child-template usage provides stronger structural evidence.
- Broad scalar and truthiness schemas can carry non-validating generation
metadata under `x-webui.preferredType`. Plain rendered values prefer
`string`; condition identifiers prefer `boolean`. Validators continue to use
the standard `type`/`anyOf` contract.
- Raw signals are strings.
- Dotted paths create nested objects.
- A terminal `.length` accepts the runtime's three valid forms: a string, an
array, or an object with a `length` property.
- `<for>` collections create arrays; loop-item paths define the item schema.
- Ordered predicates infer numbers. Equality predicates infer the type of a
literal operand; integer literals produce `integer`, and known types propagate
across path-to-path equality.
- Condition-only paths are optional because a missing condition value evaluates
false. A path referenced by rendered output or a collection remains required.
- Normal component-attribute source paths are recorded as scalars, but
child-local comparisons do not re-type the parent path. Complex bindings
preserve structural origin through the child template. Static and computed
component-local values do not become root state properties.
- Parser-synthesized `head_end`, `body_start`, and `body_end` signals are not
state.

For routed entries, the output is a schema bundle. Each complete matched route
chain receives a distinct schema under `$defs`. Top-level `anyOf` references
those definitions, while `x-webui-routes` maps each route pattern to its
definition:

```json
{
"$defs": {
"route": {},
"route.contacts.:id": {}
},
"anyOf": [
{ "$ref": "#/$defs/route" },
{ "$ref": "#/$defs/route.contacts.:id" }
],
"x-webui-routes": {
"/": "#/$defs/route",
"/contacts/:id": "#/$defs/route.contacts.:id"
}
}
```

Route definitions are not structurally deduplicated. Even routes with identical
current shapes retain independent contracts so future changes do not
unexpectedly couple them. Definition keys are readable route encodings, but
consumers should still use `x-webui-routes` rather than constructing `$defs`
keys. Host-language DTO generation and runtime schema validation are separate
concerns and are not part of `schema`.

## Example Workflow

Examples and end-to-end walkthroughs are maintained in [examples/README.md](examples/README.md)
2 changes: 2 additions & 0 deletions crates/webui-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ microsoft-webui-dev-server = { path = "../webui-dev-server", version = "0.0.18"
clap = { workspace = true }
anyhow = { workspace = true }
console = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
actix-web = { workspace = true }
awc = { workspace = true }
Expand All @@ -36,6 +37,7 @@ tokio-stream = { workspace = true }
log = { workspace = true }

[dev-dependencies]
insta = { workspace = true }
tempfile = { workspace = true }
microsoft-webui-protocol = { path = "../webui-protocol", version = "0.0.18" }

Expand Down
22 changes: 20 additions & 2 deletions crates/webui-cli/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# microsoft-webui-cli

Command-line tool for the [WebUI](https://github.com/microsoft/webui) framework build, serve, and inspect WebUI applications.
Command-line tool for the [WebUI](https://github.com/microsoft/webui) framework - build, serve, inspect, and generate render-state schemas for WebUI applications.

## Install

Expand All @@ -17,7 +17,7 @@ This installs the `webui` binary.
Build a WebUI application into a compiled protocol and CSS files.

```bash
webui build [APP] --out <DIR> [--entry <FILE>] [--css <MODE>] [--plugin <NAME>] [--asset-file-name-template <TEMPLATE>] [--css-public-base <BASE>]
webui build [APP] --out <DIR> [--entry <FILE>] [--css <MODE>] [--plugin <NAME>] [--emit-schema] [--asset-file-name-template <TEMPLATE>] [--css-public-base <BASE>]
```

| Option | Default | Description |
Expand All @@ -27,13 +27,15 @@ webui build [APP] --out <DIR> [--entry <FILE>] [--css <MODE>] [--plugin <NAME>]
| `--entry` | `index.html` | Entry HTML file |
| `--css` | `link` | CSS mode: `link` (external files) or `style` (inline) |
| `--plugin` | *(none)* | Plugin identifier (see [Plugins](https://microsoft.github.io/webui/guide/concepts/plugins/) for available identifiers) |
| `--emit-schema` | off | Emit `<protocol-stem>.state.schema.json` beside the protocol |
| `--asset-file-name-template` | `[name].[ext]` | Emitted asset filename template. Tokens: `[name]`, `[hash]`, `[ext]` |
| `--css-public-base` | *(none)* | Optional base URL/path prepended to Link-mode stylesheet hrefs |

```bash
webui build ./src --out ./dist
webui build ./src --out ./dist --plugin webui --css style
webui build ./src --out ./dist/app1.bin
webui build ./src --out ./dist/app1.bin --emit-schema
webui build ./src --out ./dist --asset-file-name-template "[name]-[hash].[ext]"
webui build ./src --out ./dist --asset-file-name-template "[name]-[hash].[ext]" --css-public-base "https://cdn.example.com/assets"
```
Expand Down Expand Up @@ -82,6 +84,22 @@ webui inspect <FILE>
webui inspect ./dist/protocol.bin
```

### `webui schema`

Generate a JSON Schema for the render state referenced by a compiled protocol.

```bash
webui schema ./dist/protocol.bin \
--entry index.html \
--title MyAppState > ./dist/state.schema.json
```

Plain bindings accept string, number, or boolean values. Loops and dotted paths
produce array and object schemas. Routed entries produce separate definitions
for each route chain plus an `x-webui-routes` path-to-schema mapping.
Broad values may include `x-webui.preferredType` as a non-validating hint for
future host-language type generation.

## App Layout

```
Expand Down
Loading
Loading