# tml

URL: https://tuios.dev/docs/libraries/tml

> The TML language as a Go library. Parse, print and validate documents for terminal surfaces, evaluate their bindings, run local actions and apply JSON Patch to their state.

`tuios.dev/tml` is the TML language. A TML document is a tree of components from a fixed catalog, with a JSON state store. A program, a plugin or an agent sends one to a host, and the host draws it in a pane, a popup or a dock cell. This library is the language. It draws nothing.

```bash
go get tuios.dev/tml
```

The library uses only the standard library. The API reference is on [pkg.go.dev](https://pkg.go.dev/tuios.dev/tml).

> tuios does not show TML surfaces yet. The renderer, the tuios commands and the standalone `tml` command come in later releases.

## What is in it

- **Two encodings.** TML text is a strict XML subset for files and people. TML JSON is for the wire and for agents. Each has a parser and a printer. Both parsers give the same canonical tree, and a canonical tree prints and reads back unchanged in either encoding. The first parse normalizes some input: it types attributes by the catalog, joins adjacent text and moves the state to the end of the root.
- **The validator.** It checks the full catalog: attributes and their types, required attributes, allowed children, theme roles, bindings, actions and limits. Each problem names the fix.
- **The JSON Schema** of TML JSON, generated from the catalog. An agent can use it for constrained output.
- **State and bindings.** A JSON store, `{state.path}` bindings, and a small expression language with a step budget. Expressions cannot loop, assign or call out.
- **Actions.** The parser checks the action attributes, such as `on-press="emit('save', form)"`. The renderer runs them.
- **JSON Patch.** RFC 6902, atomic, applied to the state. The size limit is checked after every operation.
- **Dependencies.** Each expression records the state paths it reads, so a renderer can redraw only what a patch touches.

## A document

```xml
<tml version="1" title="Build">
  <state>{"pct": 40, "step": "Compiling"}</state>
  <card title="Build">
    <text>Step: <b>{state.step}</b></text>
    <progress value="{state.pct / 100}"/>
  </card>
</tml>
```

The same document in TML JSON:

```json
{"type": "tml", "version": 1, "title": "Build", "state": {"pct": 40, "step": "Compiling"},
 "children": [{"type": "card", "title": "Build", "children": [
   {"type": "text", "children": ["Step: ", {"type": "b", "children": ["{state.step}"]}]},
   {"type": "progress", "value": "{state.pct / 100}"}]}]}
```

A colour is always a theme role, such as `success` or `dim`. A hex colour is an error, so every surface follows the theme.

## Check a document

```go
doc, problems, err := tml.Check(src, nil)
if err != nil {
	for _, p := range problems {
		// 3:3: <chart-bar> is not a TML component. (unknown_component at /children/0) Did you mean "bar"?
		fmt.Println(p)
	}
	return
}
```

Each problem has a JSON Pointer to the node, a line and a column, a code, and the closest legal value. `*tml.Error` marshals to the error object a host sends back:

```json
{"error": {"code": "invalid_tml", "message": "1 problem in the document", "problems": [
  {"at": "/children/0", "line": 3, "col": 3, "code": "unknown_component",
   "message": "<chart-bar> is not a TML component.", "severity": "error", "did_you_mean": "bar"}]}}
```

## Apply a patch

```go
store, err := tml.StoreFor(doc, nil) // nil means the default limits
if err != nil {
	panic(err)
}
ops, err := tml.DecodePatch([]byte(`[{"op":"replace","path":"/pct","value":80}]`), nil)
if err != nil {
	panic(err)
}
changed, err := store.Apply(ops)
fmt.Println(changed, err) // [/pct] <nil>
```

## Notes for a host

- **Set a budget.** Give `Env.Budget` one `tml.NewBudget()` for all the bindings of a frame. Every node, value and 32 bytes of text an expression reads costs steps. Past the budget a binding renders `!`. A nil budget gets a fresh one for each call, which bounds one binding but not a frame.
- **The store sanitizes state.** The store removes control, bidi and invisible characters from every string and key it takes in: the document's state, patch values and `Set`. It refuses keys that become one after this, and Go values outside the JSON model. Literal data arrays are sanitized when the document is parsed. Evaluated bindings are sanitized too. A renderer that reads the store directly, such as for table rows, gets clean strings.
- **Make the store with StoreFor.** It keeps the tree, and refuses a patch that grows `each`, `list`, the images or the rows of tables and charts past their limits.

## Validate, format and describe

`tml.Check` validates a document in either encoding. `tml.Format` prints it in canonical TML text or JSON. `tml.Schema` returns the JSON Schema of TML JSON. The standalone `tml` command will come with the renderer.

The [README](https://github.com/Gaurav-Gosain/tuios/tree/main/tml#readme) lists every component, attribute, function, action and limit.

## Versions

The module is at v0. The API can change between minor versions. Its tags have the folder name as a prefix, such as `tml/v0.1.0`. The source is in the [tml folder](https://github.com/Gaurav-Gosain/tuios/tree/main/tml) of the tuios repository, under the MIT licence.
