Public API of the stube framework.
Most users only need this namespace. It re-exports the small set of
functions that are intended to be called from application code; the
internals live in dev.zeko.stube.kernel, dev.zeko.stube.conversation,
dev.zeko.stube.registry, dev.zeko.stube.render, dev.zeko.stube.http and
dev.zeko.stube.server.
────────────────────────────────────────────────────────────────────── At a glance ──────────────────────────────────────────────────────────────────────
(require '[dev.zeko.stube.core :as s])
(s/defcomponent :ui/prompt
:init (fn [{:keys [text]}] {:text text :answer ""})
:keep #{:answer}
:render (fn [self]
[:form (s/root-attrs self (s/on self :submit))
[:label (:text self)]
[:input (merge {:name "answer"} (s/bind :answer))]
[:button "OK"]])
:handle (fn [self _]
[(s/answer (parse-long (:answer self)))]))
(s/defcomponent :demo/guess
:init (fn [_] {:target (rand-int 100)})
:handle (fn [_ _]
[(s/call :ui/prompt {:text "Guess 1–100"} :on-guess)])
:on-guess (fn [self n]
(cond
(< n (:target self))
[(s/call :ui/prompt {:text "Too low"} :on-guess)]
:else
[(s/end {:winner true})])))
(s/mount! "/guess" :demo/guess)
(s/start! {:port 8080})
────────────────────────────────────────────────────────────────────── Stability of this surface ────────────────────────────────────────────────────────────────────── Everything in this namespace is intended to remain stable across framework versions. Names and arities outside this namespace are considered internal until the framework reaches 1.0.
Public API of the stube framework.
Most users only need this namespace. It re-exports the small set of
functions that are intended to be called from application code; the
internals live in [[dev.zeko.stube.kernel]], [[dev.zeko.stube.conversation]],
[[dev.zeko.stube.registry]], [[dev.zeko.stube.render]], [[dev.zeko.stube.http]] and
[[dev.zeko.stube.server]].
──────────────────────────────────────────────────────────────────────
At a glance
──────────────────────────────────────────────────────────────────────
(require '[dev.zeko.stube.core :as s])
(s/defcomponent :ui/prompt
:init (fn [{:keys [text]}] {:text text :answer ""})
:keep #{:answer}
:render (fn [self]
[:form (s/root-attrs self (s/on self :submit))
[:label (:text self)]
[:input (merge {:name "answer"} (s/bind :answer))]
[:button "OK"]])
:handle (fn [self _]
[(s/answer (parse-long (:answer self)))]))
(s/defcomponent :demo/guess
:init (fn [_] {:target (rand-int 100)})
:handle (fn [_ _]
[(s/call :ui/prompt {:text "Guess 1–100"} :on-guess)])
:on-guess (fn [self n]
(cond
(< n (:target self))
[(s/call :ui/prompt {:text "Too low"} :on-guess)]
:else
[(s/end {:winner true})])))
(s/mount! "/guess" :demo/guess)
(s/start! {:port 8080})
──────────────────────────────────────────────────────────────────────
Stability of this surface
──────────────────────────────────────────────────────────────────────
Everything in this namespace is intended to remain stable across
framework versions. Names and arities outside this namespace are
considered internal until the framework reaches 1.0.Effect: dispatch route-event to the current instance after
delay-ms.
[self [(s/after 1000 :tick)]]
If the conversation or instance is gone when the timer fires, the event
is dropped. route-event accepts the same keyword/vector shape as
(s/on self :click :as route-event).
Effect: dispatch `route-event` to the current instance after
`delay-ms`.
[self [(s/after 1000 :tick)]]
If the conversation or instance is gone when the timer fires, the event
is dropped. `route-event` accepts the same keyword/vector shape as
`(s/on self :click :as route-event)`.Pop this frame; deliver value to the parent under its
resume key. See dev.zeko.stube.effects/answer.
Pop this frame; deliver `value` to the parent under its resume key. See [[dev.zeko.stube.effects/answer]].
An effect that walks one step backward through the
conversation's history. Use it from a handler to wire a "Back"
button, or render (s/back-button "Back") for the stock
conversation-level button.
When emitted from a top-level handler, it pops the most recent
conversation snapshot off :conv/history and re-renders the
restored top frame. No-op if the history is empty.
An effect that walks one step backward through the conversation's history. Use it from a handler to wire a "Back" button, or render `(s/back-button "Back")` for the stock conversation-level button. When emitted from a top-level handler, it pops the most recent conversation snapshot off `:conv/history` and re-renders the restored top frame. No-op if the history is empty.
(become component-id)(become component-id args)Pop the current frame and push another in its place (Seaside
become:). The replacement inherits the original parent linkage
and resume key, so an eventual :answer still flows back to whoever
called the original frame.
(s/become :wizard/step-2)
(s/become :wizard/step-2 {:from data})
Named become instead of replace to avoid shadowing
clojure.core/replace.
Pop the current frame and push another in its place (Seaside
`become:`). The replacement inherits the original parent linkage
and resume key, so an eventual `:answer` still flows back to whoever
called the original frame.
(s/become :wizard/step-2)
(s/become :wizard/step-2 {:from data})
Named `become` instead of `replace` to avoid shadowing
`clojure.core/replace`.Pure boot effects for a flow — see dev.zeko.stube.kernel/boot.
Pure boot effects for a flow — see [[dev.zeko.stube.kernel/boot]].
(call component-id)(call component-id args-or-resume)(call component-id args resume)Push a child onto the stack. On :answer, the parent's resume key
is invoked with the answered value.
(s/call :ui/prompt) ; no args, no resume
(s/call :ui/prompt :on-pick) ; no args, resume :on-pick
(s/call :ui/prompt {:text "Hi"}) ; args, no resume
(s/call :ui/prompt {:text "Hi"} :on-pick) ; args + resume
Wraps the component id with embed internally; pass an existing
embed spec via dev.zeko.stube.effects/call if you already have one.
Push a child onto the stack. On `:answer`, the parent's resume key
is invoked with the answered value.
(s/call :ui/prompt) ; no args, no resume
(s/call :ui/prompt :on-pick) ; no args, resume :on-pick
(s/call :ui/prompt {:text "Hi"}) ; args, no resume
(s/call :ui/prompt {:text "Hi"} :on-pick) ; args + resume
Wraps the component id with [[embed]] internally; pass an existing
embed spec via [[dev.zeko.stube.effects/call]] if you already have one.(call-in-slot slot component-id)(call-in-slot slot component-id args-or-resume)(call-in-slot slot component-id args resume)Temporarily swap an embedded slot's child; the new child answers back to the parent without taking over the page.
(s/call-in-slot :slot/main :feature/edit)
(s/call-in-slot :slot/main :feature/edit {:id 7} :on-saved)
Temporarily swap an embedded slot's child; the new child answers
back to the parent without taking over the page.
(s/call-in-slot :slot/main :feature/edit)
(s/call-in-slot :slot/main :feature/edit {:id 7} :on-saved)Sentinel returned by cancellable stock UI components.
Sentinel returned by cancellable stock UI components.
(choose options)(choose options caption)Return an embed spec for the stock one-of-N choice component.
Return an embed spec for the stock one-of-N choice component.
(confirm question)Return an embed spec for the stock yes/no confirmation component.
Return an embed spec for the stock yes/no confirmation component.
(decorate base-cdef overrides)Build a new component definition by overriding keys of base-cdef.
overrides is either:
:render / :handle etc.).Returns a fresh map; the caller is responsible for giving it a fresh
:component/id and registering it.
(def site-header
(s/decorate (s/registry-lookup :booking/wizard)
(fn [base]
{:component/id :booking/wizard-with-banner
:component/render
(fn [self]
[:div {:id (:instance/id self)}
[:header.banner "Welcome"]
((:component/render base) self)])})))
No new runtime concept: this is just merge lifted into the
framework's vocabulary so the call site reads like Seaside-style
behavioural composition.
See decorate! for the register-on-the-way variant.
Build a new component definition by overriding keys of `base-cdef`.
`overrides` is either:
* a **map** that replaces the corresponding entries verbatim, or
* a **function** of the base cdef returning such a map (so the
override can call into the original `:render` / `:handle` etc.).
Returns a fresh map; the caller is responsible for giving it a fresh
`:component/id` and registering it.
(def site-header
(s/decorate (s/registry-lookup :booking/wizard)
(fn [base]
{:component/id :booking/wizard-with-banner
:component/render
(fn [self]
[:div {:id (:instance/id self)}
[:header.banner "Welcome"]
((:component/render base) self)])})))
No new runtime concept: this is just `merge` lifted into the
framework's vocabulary so the call site reads like Seaside-style
behavioural composition.
See [[decorate!]] for the register-on-the-way variant.(decorate! base-cdef overrides)Like decorate but also registers the resulting cdef and returns
the registered map. Convenient for the common case:
(s/decorate! (s/registry-lookup :booking/wizard)
{:component/id :booking/wizard-with-banner
:component/render
(fn [self]
[:div (s/root-attrs self)
[:header.banner "Welcome"]
((:component/render (s/registry-lookup :booking/wizard)) self)])})
Use decorate when you want to inspect or further modify the cdef
before deciding whether to register it.
Like [[decorate]] but also registers the resulting cdef and returns
the registered map. Convenient for the common case:
(s/decorate! (s/registry-lookup :booking/wizard)
{:component/id :booking/wizard-with-banner
:component/render
(fn [self]
[:div (s/root-attrs self)
[:header.banner "Welcome"]
((:component/render (s/registry-lookup :booking/wizard)) self)])})
Use [[decorate]] when you want to inspect or further modify the cdef
before deciding whether to register it.(defcomponent id & opts)Define a component and add it to the global registry.
(s/defcomponent :auth/login
:doc "Prompt for credentials."
:init (fn [args] state-map)
:keep #{:signal-keys} ;; optional
:render (fn [self] hiccup)
:handle (fn [self event] ;; event is {:event …, :signals …}
[self' effects])
:on-foo (fn [self answer-value] ;; resume keys, optional
[self' effects]))
The macro captures the call-site :file/:line so the halos dev
tool can jump to the definition. Use register-component! from
data-driven code that prefers a function shape.
Define a component and add it to the global registry.
(s/defcomponent :auth/login
:doc "Prompt for credentials."
:init (fn [args] state-map)
:keep #{:signal-keys} ;; optional
:render (fn [self] hiccup)
:handle (fn [self event] ;; event is {:event …, :signals …}
[self' effects])
:on-foo (fn [self answer-value] ;; resume keys, optional
[self' effects]))
The macro captures the call-site `:file`/`:line` so the halos dev
tool can jump to the definition. Use [[register-component!]] from
data-driven code that prefers a function shape.(defflow id bindings & body)Define a linear flow component. See dev.zeko.stube.flow/defflow for the
full docstring; this is a thin re-export so application code only ever
needs [dev.zeko.stube.core :as s].
Define a linear flow component. See [[dev.zeko.stube.flow/defflow]] for the full docstring; this is a thin re-export so application code only ever needs `[dev.zeko.stube.core :as s]`.
Pure event dispatch — (dispatch conv event) → [conv' fragments].
Useful from the REPL or for tests; production code goes through the http
layer.
Pure event dispatch — `(dispatch conv event) → [conv' fragments]`. Useful from the REPL or for tests; production code goes through the http layer.
Terminate the conversation with a final value. After
this the SSE channel closes and the conversation is forgotten.
See dev.zeko.stube.effects/end.
Terminate the conversation with a final value. After this the SSE channel closes and the conversation is forgotten. See [[dev.zeko.stube.effects/end]].
Run literal JS in the browser. Last-resort escape hatch;
prefer Datastar attributes for most needs. See
dev.zeko.stube.effects/execute-script.
Run literal JS in the browser. Last-resort escape hatch; prefer Datastar attributes for most needs. See [[dev.zeko.stube.effects/execute-script]].
Return a registered component's docstring, or nil.
Return a registered component's docstring, or nil.
(history cid)Summarise :conv/history for live conversation cid.
Summarise `:conv/history` for live conversation `cid`.
(info text)Return an embed spec for the stock informational OK dialog.
Return an embed spec for the stock informational OK dialog.
(instance cid iid)Return the instance map for iid in live conversation cid.
Return the instance map for `iid` in live conversation `cid`.
Fire-and-forget (thunk) off the request thread.
See dev.zeko.stube.effects/io.
Fire-and-forget `(thunk)` off the request thread. See [[dev.zeko.stube.effects/io]].
Emit an extra DOM patch without changing the stack.
See dev.zeko.stube.effects/patch.
Emit an extra DOM patch without changing the stack. See [[dev.zeko.stube.effects/patch]].
Push a Datastar signal patch (writes signal values back
to the browser). See dev.zeko.stube.effects/patch-signals.
Push a Datastar signal patch (writes signal values back to the browser). See [[dev.zeko.stube.effects/patch-signals]].
(prompt label)(prompt label default)Return an embed spec for the stock text prompt component.
Return an embed spec for the stock text prompt component.
Publish msg to every live instance subscribed to topic.
Delivery is asynchronous and cid/iid-scoped; stale subscribers are
ignored. Returns the number of subscribers targeted.
Publish `msg` to every live instance subscribed to `topic`. Delivery is asynchronous and cid/iid-scoped; stale subscribers are ignored. Returns the number of subscribers targeted.
(register-component! id opts)(register-component! id opts source)Plain function form of defcomponent. Two arities:
(register-component! id opts)
(register-component! id opts source-map)
source-map is {:file ... :line ...} to be attached under
:component/source (so the halos dev tool can jump to a definition).
The macro supplies it from call-site &form meta; hand-rolled
data-driven callers can pass nil.
Plain function form of [[defcomponent]]. Two arities:
(register-component! id opts)
(register-component! id opts source-map)
`source-map` is `{:file ... :line ...}` to be attached under
`:component/source` (so the halos dev tool can jump to a definition).
The macro supplies it from call-site `&form` meta; hand-rolled
data-driven callers can pass nil.Look up a registered component definition by id (or nil).
Look up a registered component definition by id (or nil).
(replay events)(replay baseline events)Replay events against a baseline conversation or root flow id.
Returns [conv fragments], matching dispatch / boot. When
the baseline is a flow id, replay boots a fresh conversation first.
Event maps may omit :instance-id to target the current top frame and
may omit :signals to use {}. An event may also be a function of
the current conversation returning such a map.
Replay `events` against a baseline conversation or root flow id.
Returns `[conv fragments]`, matching [[dispatch]] / [[boot]]. When
the baseline is a flow id, replay boots a fresh conversation first.
Event maps may omit `:instance-id` to target the current top frame and
may omit `:signals` to use `{}`. An event may also be a function of
the current conversation returning such a map.Effect: subscribe the current instance to topic.
Published messages arrive as route-event with the published value in
:payload. Re-emit this from :wakeup for components that should
resubscribe after crash-resume.
Effect: subscribe the current instance to `topic`. Published messages arrive as `route-event` with the published value in `:payload`. Re-emit this from `:wakeup` for components that should resubscribe after crash-resume.
(tree cid)Pretty-print the component tree for live conversation cid and
return the tree data. nil if the conversation is unknown.
Pretty-print the component tree for live conversation `cid` and return the tree data. nil if the conversation is unknown.
Effect: remove this instance's topic subscription(s).
Effect: remove this instance's topic subscription(s).
(where type-kw)Return the source location captured for component type-kw at
defcomponent time, or nil.
Return the source location captured for component `type-kw` at `defcomponent` time, or nil.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |