{"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}},"endpoints":{"action":"/_bretzel/action","sse":"/_bretzel/sse","refetch":"/_bretzel/refetch"},"csrf":"b8e50f6eed3b4c5838c6bc80b6849b02","page_id":"c0dfc343ae8b4374a70a626390573a89","address":""}

The browser

The server stays the source of truth, but some things exist only on the user's machine: their clipboard, their printer, their screen, their share sheet. Bretzel reaches them in three ways, and each answers a different constraint.

1. The verbs — an action, a string

A verb produces JavaScript and plugs into an `on_*=`. It costs almost nothing because the slot already existed: `on_<event>=` is polymorphic — a callable leaves as a signed POST to the server, a STRING is evaluated in place, with no round trip. It is `dialog.open()`'s contract; the verbs plug into it without adding a directive, a scope or a request.copy(…)
ParameterTypeDefaultPassed as
valueAny— (requis)positionnel
print_page(…)No parameter.fullscreen(…)
ParameterTypeDefaultPassed as
targetAnyNonepositionnel
share(…)
ParameterTypeDefaultPassed as
urlAnyNonepositionnel
titleAnyNonekeyword-only
textAnyNonekeyword-only
vibrate(…)
ParameterTypeDefaultPassed as
patternint | Sequence[int]50positionnel

What they do, one line each

def exemple_verbes() -> None:
    ui.button('Copy the key', on_click=bretzel.copy(state.api_key))
    ui.button("Print", on_click=bretzel.print_page())
    ui.button('Full screen', on_click=bretzel.fullscreen(carte))
    ui.button('Share this view', on_click=bretzel.share())
    ui.button("Vibrate", on_click=bretzel.vibrate([50, 30, 50]))
The clipboard demands a SECURE context
`navigator.clipboard` only exists over https:// or localhost. Over http:// with a local-network IP — the way an internal tool gets used — it is `undefined`, and a bare call would do NOTHING, without a word. Bretzel falls back to `document.execCommand`, deprecated and the only route that exists there.
`share` falls back to copying, and that is the contract
`navigator.share` is absent from desktop Chromium (measured). Its absence is therefore not an edge case, it is the NORMAL case where one develops. Rather than an inert “Share” button, the verb copies the URL. A share CANCELLED by the user, on the other hand, does not fall back on it: closing the sheet would copy behind their back.
No visual feedback on `copy`
That is the contract, not an oversight: the verb copies, the app wires whatever feedback it wants. Nothing tells the user the click took.

2. @download — a routable that returns a file

This is NOT an action, and the constraint is structural: an action's response is swallowed by the bridge and applied as a `<bz-patch>`, whereas a download must BE the file. So it is an ordinary `ui.link(href=…)`, not an `on_click=`.download(…)
ParameterTypeDefaultPassed as
pathstr— (requis)positionnel
filenamestr | NoneNonekeyword-only
media_typestr | NoneNonekeyword-only
def exemple_download() -> None:
    @download("/clients.csv")
    async def clients_csv() -> list[dict]:
        return await db.clients()

    # ⚠️ `download=True` is MANDATORY: without it, `hx-boost` swallows
    # the link and the CSV arrives as HTML in the outlet, with no sign at
    # all on the server side. The example omitted it although the
    # /capabilities page said so — two pages contradicting each other.
    ui.link('Export the customers', href="/clients.csv", download=True)

Four rendered forms

what the function returnswhat the browser receives
list[dict]a CSV — headers derived from the first record's keys, UTF-8 BOM, RFC 4180 quoting
strthe text as is
bytesthe bytes as they are — a PDF, an image, a zip
Responsethe escape hatch, tested FIRST: everything the rest does not cover
It is NOT signed, unlike the datatable's export
And that is the difference that justifies the routable. The datatable's link carries in its URL the NAME of the function to invoke, so it MUST be signed — and it inherits being a bearer capability, with no expiry. A `@download` fixes its function at DECORATION time, like `@page`: the URL decides nothing, and the route goes through the same middleware. An app that protects its pages protects its downloads.
`ui.datatable(exportable=True)` is NOT wired to it
The two mechanisms coexist today, which principle 4 refuses — and that is written down rather than hidden. The datatable's export needs the READER's query (sort, filters at click time), which does not exist in a static route. Rewiring them means deciding how a VIEW travels as far as a `@download`.

3. PWA — the app becomes installable

The manifest describes the app to the system: its name, its icon, its colour, and the fact that it opens in its OWN window rather than in a tab. It is what gives an internal tool an icon in the Start menu.PWA(…)
ParameterTypeDefaultPassed as
namestr— (requis)positionnel
short_namestr | NoneNonepositionnel
descriptionstr | NoneNonepositionnel
iconstr | NoneNonepositionnel
maskableboolFalsepositionnel
displaystr'standalone'positionnel
start_urlstr'/'positionnel
theme_colorstr | NoneNonepositionnel
background_colorstr | NoneNonepositionnel
iconstuple[PWAIcon, ...]<factory>positionnel
def exemple_pwa() -> None:
    app = Bretzel(
        title="Tracker",
        secret_key=...,
        pwa=PWA(name="Tracker", icon="/static/logo.png",
                theme_color="#0f172a"),
    )
It makes NOTHING available offline
Offline needs a service worker — a script intercepting every request. It is not shipped, deliberately: it is a whole lifecycle (versions, invalidation, updating an app already installed on somebody's machine), and a place where one breaks an app in production while believing one is improving it.
If the “Install” prompt does not appear, it is not the manifest
Chrome long ALSO demanded a service worker before offering it. What Bretzel guarantees and what is measured: the manifest is served, valid, correctly typed and linked — and it is Chromium itself that says so (`Page.getAppManifest`, zero errors).

What Bretzel can NOT do yet

Written here so the list above does not read as complete. None of these capabilities has the slightest start in the code: geolocation, camera, network state, system notification, screen lock, IndexedDB. They all need a new pattern — a PERMISSION and a WAIT — that a synchronous verb cannot carry.