Liking cljdoc? Tell your friends :D

com.blockether.vis.ext.channel-tui.components

Reusable header/tab UI components.

Each drawable here is a self-contained fn that paints into a Lanterna TextGraphics and — when asked — registers its OWN click region, so the same widget can't drift between call sites (the bug that left the tab close painted in one place and made clickable in another, or not at all). Two kinds live here:

  • text layout helpers — truncate-with-ellipsis, center-padded
  • drawable components — close-button!, tab-cell!

Components own their visual contract AND their interaction contract (the click region's :kind), keeping header.clj a thin layout caller.

Reusable header/tab UI components.

Each drawable here is a self-contained fn that paints into a Lanterna
`TextGraphics` and — when asked — registers its OWN click region, so the
same widget can't drift between call sites (the bug that left the tab
close `✕` painted in one place and made clickable in another, or not at
all). Two kinds live here:

  - text layout helpers — `truncate-with-ellipsis`, `center-padded`
  - drawable components — `close-button!`, `tab-cell!`

Components own their visual contract AND their interaction contract
(the click region's `:kind`), keeping `header.clj` a thin layout caller.
raw docstring

band-rule!clj

(band-rule! g row cols)
(band-rule! g row cols fg)

Paint a full-width single-line horizontal rule across cols on row.

Paint a full-width single-line horizontal rule across `cols` on `row`.
sourceraw docstring

button!clj

(button! g col row label kind)
(button! g
         col
         row
         label
         kind
         {:keys [extra danger? accent? register?] :or {register? true}})

Generic clickable button: paint label (already space-padded, e.g. " < ") as a filled accent cap at (col,row) — brighter on hover — AND register its click region under :kind, plus any identity keys in extra (merged into both the region and the hover match). One widget owns its look, its hover, and its click, so they can't drift apart. Returns the consumed width.

Hover is detected by matching the registered :kind and every extra key against cr/hovered, so two buttons of the same kind but different ids stay independent.

Generic clickable button: paint `label` (already space-padded, e.g. " < ") as
a filled accent cap at (col,row) — brighter on hover — AND register its click
region under `:kind`, plus any identity keys in `extra` (merged into both the
region and the hover match). One widget owns its look, its hover, and its
click, so they can't drift apart. Returns the consumed width.

Hover is detected by matching the registered `:kind` and every `extra` key
against `cr/hovered`, so two buttons of the same kind but different ids stay
independent.
sourceraw docstring

center-paddedclj

(center-padded s cell-w)

Centre s inside a cell-w-wide cell with vh/tab-entry-padding reserved on each side; ellipsises overflow.

Centre `s` inside a `cell-w`-wide cell with `vh/tab-entry-padding`
reserved on each side; ellipsises overflow.
sourceraw docstring

close-button!clj

(close-button! g col row _tab-fg _tab-bg workspace-id register?)

Draw a close affordance (space-padded ✕, no divider) at (col,row). At rest the ✕ sits on its OWN chip — the shared button-fg/button-bg pair every other TUI button uses at rest — so it reads as a SEPARATE button pinned to the tab's right edge instead of blending into the tab surface (a gray ✕ vanished on the inactive near-white tab; a white ✕ melted into the active blue tab). The dark button-fg glyph stays legible on the light chip, and the chip's contrast against BOTH tab backgrounds is the affordance. On hover the cap escalates to a red pill (close-button-hover-fg bg behind a white header-active-tab-fg ✕) to signal the destructive click. Registers its :close-tab click region for workspace-id. Returns the consumed width (close-button-width).

Draw a ` ✕ ` close affordance (space-padded ✕, no divider) at (col,row).
At rest the ✕ sits on its OWN chip — the shared `button-fg`/`button-bg`
pair every other TUI button uses at rest — so it reads as a SEPARATE
button pinned to the tab's right edge instead of blending into the tab
surface (a gray ✕ vanished on the inactive near-white tab; a white ✕
melted into the active blue tab). The dark `button-fg` glyph stays legible
on the light chip, and the chip's contrast against BOTH tab backgrounds is
the affordance.
On hover the cap escalates to a red pill (`close-button-hover-fg` bg behind
a white `header-active-tab-fg` ✕) to signal the destructive click.
Registers its `:close-tab` click region for `workspace-id`. Returns the
consumed width (`close-button-width`).
sourceraw docstring

close-button-widthclj

Cells a close-button! occupies: a leading space + 1-col + a trailing space. No divider — the bare ✕ reads as a button, not |x.

Cells a `close-button!` occupies: a leading space + 1-col `✕` + a
trailing space. No divider — the bare ✕ reads as a button, not `|x`.
sourceraw docstring

context-overlay!clj

(context-overlay! g cols rows _ctx scroll _expanded)

Dialog showing the session's working memory - :session/tasks AND :session/facts - the W3 user-visible panel (F2). Uses the shared dialogs/draw-dialog-chrome! + dialog-layout so it looks like every other modal (shadow, border, accent title bar, hint row). Title carries a tasks-done + facts count summary; a TASKS section (status-sorted, colored glyphs, acceptance sub-lines, verify badges) then a FACTS section (active first, > N for file-bearing facts). expanded is the set of fact keys (as strings) whose file list is unfolded; each file-bearing fact's meta row gets a :toggle-fact-files click region so a click folds/unfolds its paths. The dialog SIZES to its content (grows to fit, clamped to the terminal); overflow scrolls through the shared scrollable-dialog-body! (same plumbing as F1 help). ctx is {:tasks ... :facts ...}. Returns {:scroll :max-scroll :selectable-ranges}.

Dialog showing the session's working memory - `:session/tasks` AND
`:session/facts` - the W3 user-visible panel (F2). Uses the shared
`dialogs/draw-dialog-chrome!` + `dialog-layout` so it looks like every other
modal (shadow, border, accent title bar, hint row). Title carries a
tasks-done + facts count summary; a TASKS section (status-sorted, colored
glyphs, acceptance sub-lines, verify badges) then a FACTS section (active
first, `> N` for file-bearing facts). `expanded` is the set of fact keys (as
strings) whose file list is unfolded; each file-bearing fact's meta row gets
a `:toggle-fact-files` click region so a click folds/unfolds its paths. The
dialog SIZES to its content (grows to fit, clamped to the terminal); overflow
scrolls through the shared `scrollable-dialog-body!` (same plumbing as F1
help). `ctx` is `{:tasks ... :facts ...}`. Returns
`{:scroll :max-scroll :selectable-ranges}`.
sourceraw docstring

dialog-close-button!clj

(dialog-close-button! g bounds kind)

Draw a clickable close chip at a dialog's top-right title corner — the SAME button every modal dialog shows — and register its kind click region (:toggle-help / :toggle-tasks) so a mouse click dismisses the overlay. Inverted title chip at rest; red pill on hover. Returns nil.

Draw a clickable `✕` close chip at a dialog's top-right title corner —
the SAME button every modal dialog shows — and register its `kind`
click region (`:toggle-help` / `:toggle-tasks`) so a mouse click
dismisses the overlay. Inverted title chip at rest; red pill on hover.
Returns nil.
sourceraw docstring

find-bar!clj

(find-bar! g cols text-top search)

Browser-style in-session find WIDGET: a single-line BORDERED box, right-aligned at the top of the messages area, holding a WHITE input field (the live query), the i/N match count, and spaced Aa/◀/▶/✕ glyph buttons (each its own click region via button!, so the mouse drives the same :search-* events as C-p / C-n). search is app-db's :search map; no-op when inactive.

Returns the cursor cell [col row] from find-bar-cursor (nil when inactive) so callers can park the terminal cursor in the query field.

Browser-style in-session find WIDGET: a single-line BORDERED box, right-aligned at the top of the messages area, holding a WHITE input field (the live query), the i/N match count, and spaced Aa/◀/▶/✕ glyph buttons (each its own click region via `button!`, so the mouse drives the same `:search-*` events as C-p / C-n). `search` is app-db's `:search` map; no-op when inactive.

Returns the cursor cell [col row] from `find-bar-cursor` (nil when
inactive) so callers can park the terminal cursor in the query field.
sourceraw docstring

find-bar-cursorclj

(find-bar-cursor cols text-top search)

Screen cell [col row] where the terminal cursor belongs while the find bar is ACTIVE — the end of the typed query inside the white field — or nil when inactive. Both render paths (full frame + live/partial) consult this AFTER placing the prompt-input cursor: while the bar owns the keyboard the cursor visibly moves INTO it, and because the prompt's logical {:lines :crow :ccol} state is never touched by the search key-loop, closing the bar lands the cursor back exactly where it was.

Screen cell [col row] where the terminal cursor belongs while the find bar
is ACTIVE — the end of the typed query inside the white field — or nil
when inactive. Both render paths (full frame + live/partial) consult this
AFTER placing the prompt-input cursor: while the bar owns the keyboard the
cursor visibly moves INTO it, and because the prompt's logical
`{:lines :crow :ccol}` state is never touched by the search key-loop,
closing the bar lands the cursor back exactly where it was.
sourceraw docstring

header-badge!clj

(header-badge! g col row glyph kind register?)

Paint a clickable header chip glyph at (col,row) and register a click region of kind (e.g. :toggle-tasks / :toggle-help). Brightens on hover. Returns the consumed width. A terminal-safe, always-visible stand-in for the F1/F2 accelerators.

Paint a clickable header chip `glyph` at (col,row) and register a click
region of `kind` (e.g. :toggle-tasks / :toggle-help). Brightens on hover.
Returns the consumed width. A terminal-safe, always-visible stand-in for
the F1/F2 accelerators.
sourceraw docstring

help-overlay!clj

(help-overlay! g cols rows scroll)

Draw the keyboard-shortcut help as a dialog, using the shared dialogs/draw-dialog-chrome! + dialog-layout so it matches the F2 context panel (shadow, border, accent title bar, centered hint row). The body (built by box-grid-lines) is a sectioned 2-column grid: each group rides under a bold full-width banner, so the card reads as labelled SECTIONS instead of bare rows with blank gaps.

The chrome is drawn FIRST (the shared 5-arg arity picks a fixed modal footprint and IGNORES the passed line count), so we learn the real inner width, then size the desc column to what actually FITS — reserving one scrollbar-lane column — and let box-grid-lines WRAP long descriptions instead of clipping them. The scroll extent is the wrapped line count, so scrolling always covers every visual line. Registers only its close-button click region; the caller dismisses it (Ctrl+H / F1 / any key). Returns {:scroll :max-scroll} so the caller can feed the clamp back, exactly like context-overlay!.

Draw the keyboard-shortcut help as a dialog, using the shared
`dialogs/draw-dialog-chrome!` + `dialog-layout` so it matches the F2
context panel (shadow, border, accent title bar, centered hint row).
The body (built by `box-grid-lines`) is a sectioned 2-column grid: each
group rides under a bold full-width banner, so the card reads as labelled
SECTIONS instead of bare rows with blank gaps.

The chrome is drawn FIRST (the shared 5-arg arity picks a fixed modal
footprint and IGNORES the passed line count), so we learn the real inner
width, then size the desc column to what actually FITS — reserving one
scrollbar-lane column — and let `box-grid-lines` WRAP long descriptions
instead of clipping them. The scroll extent is the wrapped line count, so
scrolling always covers every visual line. Registers only its close-button
click region; the caller dismisses it (Ctrl+H / F1 / any key). Returns
`{:scroll :max-scroll}` so the caller can feed the clamp back, exactly like
`context-overlay!`.
sourceraw docstring

id-badge!clj

(id-badge! g col row text full-uuid register?)

Paint the session-id COPY BUTTON text at (col,row) as a filled chip — via the shared button!, so it reads as the SAME control as the F1/F2 buttons instead of bare text — and register its :copy-id click region carrying the FULL uuid (the click handler drops it on the clipboard). Brightens to the accent on hover. No-op for blank text.

Paint the session-id COPY BUTTON `text` at (col,row) as a filled chip — via
the shared `button!`, so it reads as the SAME control as the F1/F2 buttons
instead of bare text — and register its `:copy-id` click region carrying the
FULL uuid (the click handler drops it on the clipboard). Brightens
to the accent on hover. No-op for blank text.
sourceraw docstring

(nav-arrow! g row col glyph direction register?)

Paint a workspace-overflow navigation arrow glyph at (col,row) as a FILLED BUTTON CHIP - routed through the shared button!, so the < > read as the SAME control as the F1/F2 and id-copy buttons (inverted cap at rest, lifts to the accent + bold on hover) instead of a bare character. Registers its :workspace-entry click region keyed by direction (:prev / :next), so a click still cycles the visible tab window.

Paint a workspace-overflow navigation arrow `glyph` at (col,row) as a FILLED
BUTTON CHIP - routed through the shared `button!`, so the < > read as the
SAME control as the F1/F2 and id-copy buttons (inverted cap at rest, lifts to
the accent + bold on hover) instead of a bare character. Registers its
`:workspace-entry` click region keyed by `direction` (`:prev` / `:next`), so a
click still cycles the visible tab window.
sourceraw docstring

notification-slot!clj

(notification-slot! g col row text level)

Paint the header's left-slot notification/status text at (col,row), colored by level (bold). No-op for blank text.

Paint the header's left-slot notification/status `text` at (col,row),
colored by `level` (bold). No-op for blank text.
sourceraw docstring

scrollable-dialog-body!clj

(scrollable-dialog-body! g
                         lines
                         {:keys [content-top content-h hint-row sb-col
                                 body-right]}
                         scroll
                         paint-line)

Paint the scroll plumbing both modal overlays (F1 help, F2 context) share: clamp scroll to [0, (- (count lines) content-h)], window lines by that effective offset and paint each visible row via paint-line (a (fn [screen-row-i line])), draw the shared scrollbar/draw! in sb-col when the content overflows, and a right-aligned N-M / total position hint on hint-row (anchored to body-right). geom is {:content-top :content-h :hint-row :sb-col :body-right}. Returns {:scroll :max-scroll :sb? :shown-n} so callers feed the clamp back and derive their own geometry (e.g. F2's selectable ranges).

Paint the scroll plumbing both modal overlays (F1 help, F2 context) share:
clamp `scroll` to `[0, (- (count lines) content-h)]`, window `lines` by that
effective offset and paint each visible row via `paint-line` (a
`(fn [screen-row-i line])`), draw the shared `scrollbar/draw!` in `sb-col`
when the content overflows, and a right-aligned `N-M / total` position hint
on `hint-row` (anchored to `body-right`). `geom` is
`{:content-top :content-h :hint-row :sb-col :body-right}`. Returns
`{:scroll :max-scroll :sb? :shown-n}` so callers feed the clamp back and
derive their own geometry (e.g. F2's selectable ranges).
sourceraw docstring

tab-cell!clj

(tab-cell! g
           {:keys [left row width label tab-no status active? workspace-id index
                   register? closable?]
            :or {closable? true}})

Draw one workspace tab into the band at [left,row] spanning width cells:

  • the bg slab (active = inverted bold; inactive = dim italic);
  • a STATUS BORDER painted as a per-column UNDERLINE along the bottom of the cell (this replaced the old 2-col status prefix / edge rails, so the number now sits FLUSH — no leading gap). The border is all theme colour (no block glyphs, nothing to clash with the surface) and IS the status cue: :running -> a CONTINUOUS amber underline with a bright band sweeping left→right along the bottom edge (the "working" crawl, smooth — no gaps, no flicker); :ready -> the whole cell glows GREEN (status-ok) under a steady, solid underline — the "ready to check" outline; nil -> NO border — an idle tab keeps the plain surface background and blends in;
  • the centered <num> | <label>, given close-button-width fewer cells so it never collides with the ✕;
  • an ALWAYS-VISIBLE close-button! pinned to the right edge (when the cell is wide enough to host both a title and the button).

Registers the cell's :workspace-entry region FIRST, then the ✕ :close-tab region ON TOP, so a click on the glyph wins the topmost (last-registered) lookup and closes the tab instead of selecting it.

opts keys: :left :row :width :label :tab-no :status :active? :workspace-id :index :register? :closable?

Draw one workspace tab into the band at [left,row] spanning `width` cells:

  - the bg slab (active = inverted bold; inactive = dim italic);
  - a STATUS BORDER painted as a per-column UNDERLINE along the bottom of
    the cell (this replaced the old 2-col status prefix / edge rails, so the
    number now sits FLUSH — no leading gap). The border is all theme
    colour (no block glyphs, nothing to clash with the surface) and IS the
    status cue:
      :running -> a CONTINUOUS amber underline with a bright band sweeping
                  left→right along the bottom edge (the "working" crawl,
                  smooth — no gaps, no flicker);
      :ready   -> the whole cell glows GREEN (`status-ok`) under a steady,
                  solid underline — the "ready to check" outline;
      nil      -> NO border — an idle tab keeps the plain surface
                  background and blends in;
  - the centered `<num> | <label>`, given `close-button-width` fewer cells
    so it never collides with the ✕;
  - an ALWAYS-VISIBLE `close-button!` pinned to the right edge (when the
    cell is wide enough to host both a title and the button).

Registers the cell's `:workspace-entry` region FIRST, then the ✕
`:close-tab` region ON TOP, so a click on the glyph wins the topmost
(last-registered) lookup and closes the tab instead of selecting it.

`opts` keys: :left :row :width :label :tab-no :status :active? :workspace-id
:index :register? :closable?
sourceraw docstring

tab-divider!clj

(tab-divider! g col row)

Paint a 1-col dotted divider (between tabs, or between header-cluster chips) at (col,row). Painted in the high-contrast accent (dark on a light terminal, light on a dark one) so the sparse dotted glyph is actually visible — footer-fg washed it out.

Paint a 1-col dotted `┊` divider (between tabs, or between header-cluster
chips) at (col,row). Painted in the high-contrast accent (dark on a light
terminal, light on a dark one) so the sparse dotted glyph is actually
visible — `footer-fg` washed it out.
sourceraw docstring

title!clj

(title! g row left width text)

Paint an INERT, centered header title in [left,row] over width cells — bold on the header surface, ellipsised on overflow. Registers no click region: a single-workspace title is not a switcher and must not look or behave like one.

Paint an INERT, centered header title in [left,row] over `width` cells —
bold on the header surface, ellipsised on overflow. Registers no click
region: a single-workspace title is not a switcher and must not look or
behave like one.
sourceraw docstring

truncate-with-ellipsisclj

(truncate-with-ellipsis s max-cols)

Truncate s so its display width fits in max-cols. When truncation actually happens, append vh/workspace-ellipsis so overflow is visible.

Truncate `s` so its display width fits in `max-cols`. When truncation
actually happens, append `vh/workspace-ellipsis` so overflow is visible.
sourceraw docstring

cljdoc builds & hosts documentation for Clojure/Script libraries

Keyboard shortcuts
Ctrl+kJump to recent docs
Move to previous article
Move to next article
Ctrl+/Jump to the search field
× close