D2 · Guestbook (the cascade)
Adds state and one control: signs the guestbook twice — each committing once to the journal — then renders the surface from the resulting fold. This is the committed cascade (run / emit / state).
Run it
cx guestbook.cx
Source — guestbook.cx
[?lib 'cx-xap' :as xap]
# D2: the `guestbook` capability -- state + one control. The slice "/guestbook"
# is a journal fold; [do :sign] is the control the panel offers; the view lists
# the signed names and renders the control. (working-panel :none -- a thin view.)
[$xap:component guestbook
{bind: "/guestbook"
emits: ([do :sign [name :string]])
view: [?fn ($gs)
[panel
[list [?for [in $g $gs] [yield [item $g/name]]]]
[control :sign [label "Sign"] [input :name]]]]
working-panel: :none}]
# One runtime; two clients (CLI + TUI) attach as peers. `run` wires a single-
# tenant runtime (its journal + bus + authz + sessions). The cascade (xap.md
# §2.1) auto-appends each committed [do :sign] to the journal, and "/guestbook"
# is the live fold of those events (§14). Both clients ride the §16 event-feed
# over the in-process bus -- no SSE, no socket -- so acting through a control on
# EITHER re-materializes BOTH; shown here as two emits (one "from" each client).
[?let [= $rt [$xap:run {tenant: "demo" components: (guestbook)}]]
[?let [= $a [$xap:emit $rt [do :sign [name "Ada"]]]] # signed from the CLI client
[?let [= $b [$xap:emit $rt [do :sign [name "Lin"]]]] # signed from the TUI client
# the surface both clients now display (Ada + Lin), as the view-tree value;
# context: $rt lets render read the live /guestbook fold (§16 shared state).
[$xap:render
[$xap:surface "guestbook" [$xap:panel guestbook {}]]
{accept: "application/cx" context: $rt}]]]]
Expected output
[surface name=guestbook [panel [list ([item 'Ada'], [item 'Lin'])] [control :sign [label 'Sign'] [input :name]]]]