{"version":"v1.0","client_state":{"DocsNavigation.default":{"fields":{"query":""},"persist":"memory","send_to_server":true},"ColorScheme.default":{"fields":{"mode":"system"},"persist":"local","send_to_server":true},"PanelUI.default":{"fields":{"open":false},"persist":"memory","send_to_server":true},"NumberUI.default":{"fields":{"n":0},"persist":"memory","send_to_server":true}},"endpoints":{"action":"/_bretzel/action","sse":"/_bretzel/sse","refetch":"/_bretzel/refetch"},"csrf":"865db9f64ee33688ea78365399fe6ab6","page_id":"4a1f32b8436b40c9b47ced76b6486506","address":""}

Client reactivity

When aclient statechanges, the browser updates the display itself — with no request. One wires that by passing a binding to a reactive prop.

Reading a client state = a binding

Reading a ClientState field inside a render does not return the raw value but a `ClientBinding` — an object the component knows how to wire.
Passed to…Cable
ui.text(binding)bz-text — the text follows the value
visible=bindingbz-show — shown/hidden according to the value
value=binding (input)bz-model — two-way read/write

Displaying a value

A server value read inside a render is an ordinary Python value: one displays it directly (str, f-string). A ClientBinding is passed as is to `ui.text()` to stay reactive; interpolating it into a `str()` or an f-string raises an error.

Demo — a binding drives a bz-show

Demo — a ClientExpression drives visible=

The operators on a binding (`num.n > 3`) compose a `ClientExpression`, baked into a client-side `bz-show`. Zero round trips.
n > 3

The whole binding → JS algebra

Every `ClientBinding` operator and method, read live from the class, with the JS it really emits (captured by running a probe). Adding an operator to the algebra adds it here — with no editing. Traps: `&`/`|`/`~` (not `and`/`or`/`not`), and parenthesise compound comparisons `(x > 0) & (y < 10)`.
comparison13
In PythonEmitted JS (live)→
x == y($bz.state.State.default.x === 3)ClientExpression
x >= y($bz.state.State.default.x >= 3)ClientExpression
x > y($bz.state.State.default.x > 3)ClientExpression
x <= y($bz.state.State.default.x <= 3)ClientExpression
x < y($bz.state.State.default.x < 3)ClientExpression
x != y($bz.state.State.default.x !== 3)ClientExpression
x.between(lo, hi)($bz.state.State.default.x >= 1 && $bz.state.State.default.x <= 10)ClientExpression
x.eq(y)($bz.state.State.default.x === 3)ClientExpression
x.ge(y)($bz.state.State.default.x >= 3)ClientExpression
x.gt(y)($bz.state.State.default.x > 3)ClientExpression
x.le(y)($bz.state.State.default.x <= 3)ClientExpression
x.lt(y)($bz.state.State.default.x < 3)ClientExpression
x.ne(y)($bz.state.State.default.x !== 3)ClientExpression
arithmetic13
In PythonEmitted JS (live)→
abs(x)Math.abs($bz.state.State.default.x)ClientExpression
x + y($bz.state.State.default.x + 3)ClientExpression
x // yMath.floor($bz.state.State.default.x / 3)ClientExpression
x % y($bz.state.State.default.x % 3)ClientExpression
x * y($bz.state.State.default.x * 3)ClientExpression
-x(-$bz.state.State.default.x)ClientExpression
y + x(3 + $bz.state.State.default.x)ClientExpression
y * x(3 * $bz.state.State.default.x)ClientExpression
round(x, n)Math.round($bz.state.State.default.x)ClientExpression
y - x(3 - $bz.state.State.default.x)ClientExpression
x - y($bz.state.State.default.x - 3)ClientExpression
x / y($bz.state.State.default.x / 3)ClientExpression
x.to_fixed(n)Number($bz.state.State.default.x).toFixed(2)ClientExpression
logic5
In PythonEmitted JS (live)→
x & y($bz.state.State.default.x && $bz.state.State.default.y)ClientExpression
~x(!$bz.state.State.default.x)ClientExpression
x | y($bz.state.State.default.x || $bz.state.State.default.y)ClientExpression
x.not_()(!$bz.state.State.default.x)ClientExpression
cond.then_else(a, b)($bz.state.State.default.x ? 1 : 10)ClientExpression
list3
In PythonEmitted JS (live)→
x.contains(v)$bz.state.State.default.x.includes(3)ClientExpression
x.join(sep)((($bz.state.State.default.x || []).join(", ")) || "")ClientExpression
x.length()$bz.state.State.default.x.lengthClientExpression
mutation6
In PythonEmitted JS (live)→
x.clear()$bz.state.State.default.x = []str
x.decrement(n)$bz.state.State.default.x -= 1str
x.increment(n)$bz.state.State.default.x += 1str
x.push(v)$bz.state.State.default.x = [...($bz.state.State.default.x || []), 3]str
x.set(v)$bz.state.State.default.x = 3str
x.toggle()$bz.state.State.default.x = !$bz.state.State.default.xstr