TUI rendering for :human-input/request — the terminal half of the engine's
typed human-input pause primitive (com.blockether.vis.internal.human-input).
The engine parks an extension thread and publishes a request VIEW; this
namespace turns that view into a form the operator can fill in, and hands
the collected values back through submit-human-input! / cancel-human-input!.
Everything except paint! is PURE: init-form builds the form model
from a request view, handle-event is a reducer over one normalized
keystroke, and form-rows is the paint plan. The Lanterna surface only
shows up in key->event (decoding) and paint! (drawing), so the whole
interaction is testable without a terminal.
Navigation is a flat list of STOPS — one per text field, one per checkbox, and one per select/multiselect OPTION — so ↑/↓/Tab walks the form the way the settings dialog walks its rows.
TUI rendering for `:human-input/request` — the terminal half of the engine's typed human-input pause primitive (`com.blockether.vis.internal.human-input`). The engine parks an extension thread and publishes a request VIEW; this namespace turns that view into a form the operator can fill in, and hands the collected values back through `submit-human-input!` / `cancel-human-input!`. Everything except [[paint!]] is PURE: [[init-form]] builds the form model from a request view, [[handle-event]] is a reducer over one normalized keystroke, and [[form-rows]] is the paint plan. The Lanterna surface only shows up in [[key->event]] (decoding) and [[paint!]] (drawing), so the whole interaction is testable without a terminal. Navigation is a flat list of STOPS — one per text field, one per checkbox, and one per select/multiselect OPTION — so ↑/↓/Tab walks the form the way the settings dialog walks its rows.
(default-value {:keys [type default] :as field})The value a field starts with: its declared :default coerced to the shape
the field type submits, else that type's empty value.
The value a field starts with: its declared `:default` coerced to the shape the field type submits, else that type's empty value.
(focused-row rows)Index of the row carrying the focused stop, or 0.
Index of the row carrying the focused stop, or 0.
(focused-stop {:keys [stops focus]})The stop under the cursor, or nil for a form with no stops.
The stop under the cursor, or nil for a form with no stops.
(form-rows form)(form-rows {:keys [request focus] :as form} text-w)PURE paint plan: the ordered rows the dialog body draws for form.
text-w is the column budget prose gets — the request's description and
every field's wrap to it. Omit it (or pass nil) for the unwrapped plan.
PURE paint plan: the ordered rows the dialog body draws for `form`. `text-w` is the column budget prose gets — the request's description and every field's wrap to it. Omit it (or pass nil) for the unwrapped plan.
(handle-event form {:keys [kind char] :as _event})PURE reducer: apply ONE normalized event to form.
Returns {:form form' :action action} where action is nil (stay open),
:submit (ask the engine to accept submit-values), or :cancel.
PURE reducer: apply ONE normalized event to `form`.
Returns `{:form form' :action action}` where `action` is nil (stay open),
`:submit` (ask the engine to accept `submit-values`), or `:cancel`.(hint form)Hint-bar pairs for form — the chord list changes with the focused field.
Spelled the canonical dialog way: ↑/↓ chords and lowercase actions.
Hint-bar pairs for `form` — the chord list changes with the focused field. Spelled the canonical dialog way: `↑/↓` chords and lowercase actions.
(init-form request)Build the form model for a request VIEW. Text cursors start at end-of-text and focus starts on the first stop.
Build the form model for a request VIEW. Text cursors start at end-of-text and focus starts on the first stop.
(key->event key)Normalize one Lanterna keystroke into the event map handle-event takes,
or nil when the keystroke means nothing to a form (mouse, unknown chords).
Normalize one Lanterna keystroke into the event map [[handle-event]] takes, or nil when the keystroke means nothing to a form (mouse, unknown chords).
(paint! g cols rows form)Draw the human-input dialog for form over the whole screen. Returns the
TerminalPosition the caller should place the terminal cursor at (the
focused text field), or nil when no text field has focus.
Sized and decorated like every other dialog: shared chrome, a box wide enough for the whole hint bar, and a scrollbar in the right gutter once the form is taller than the content area.
TWO passes, because the box width is not known until the box is drawn: a draft plan sizes the frame, then the real plan wraps to the width the frame actually hands each row.
Draw the human-input dialog for `form` over the whole screen. Returns the `TerminalPosition` the caller should place the terminal cursor at (the focused text field), or nil when no text field has focus. Sized and decorated like every other dialog: shared chrome, a box wide enough for the whole hint bar, and a scrollbar in the right gutter once the form is taller than the content area. TWO passes, because the box width is not known until the box is drawn: a draft plan sizes the frame, then the real plan wraps to the width the frame actually hands each row.
(request-id form)The engine request id this form answers.
The engine request id this form answers.
(set-errors form errors)Attach the engine's per-field rejection messages and move focus to the first offending field so the operator lands on what needs fixing.
Attach the engine's per-field rejection messages and move focus to the first offending field so the operator lands on what needs fixing.
(stops request)Flat vector of focus stops for request — the linear order ↑/↓/Tab walks.
Flat vector of focus stops for `request` — the linear order ↑/↓/Tab walks.
(submit-values form)The values map handed to submit-human-input! — keyed by field id.
The values map handed to `submit-human-input!` — keyed by field id.
(window-start rows visible)First plan row to draw so the focused field stays usable: the minimal scroll that keeps the focused row on screen, pulled up to that field's LABEL row when the window is too short to hold both — a bare unlabelled input row would not say which field is being edited.
First plan row to draw so the focused field stays usable: the minimal scroll that keeps the focused row on screen, pulled up to that field's LABEL row when the window is too short to hold both — a bare unlabelled input row would not say which field is being edited.
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 |