Low-level drawing primitives wrapping Lanterna TextGraphics. All rendering code should use these instead of raw Java interop.
String width here means display columns the terminal will paint,
NOT Java .length() and NOT (count s). Those count UTF-16 code units;
a single emoji like 📁 is two code units but two columns,
a CJK glyph like 日 is one code unit but two columns, a flag
sequence 🇵🇱 is four code units, one grapheme,
two columns. Mix the two and your alignment drifts.
Use display-width and truncate-cols for any string that could
contain non-ASCII. Plain count/subs are still allowed for ASCII
internals (box-drawing strings we authored, single-glyph keystroke
labels, etc.) where the answer is identical and the call is a hot path.
Low-level drawing primitives wrapping Lanterna TextGraphics. All rendering code should use these instead of raw Java interop. Width-math contract =================== String width here means **display columns the terminal will paint**, NOT Java `.length()` and NOT (count s). Those count UTF-16 code units; a single emoji like 📁 is two code units but two columns, a CJK glyph like 日 is one code unit but two columns, a flag sequence 🇵🇱 is four code units, one grapheme, two columns. Mix the two and your alignment drifts. Use `display-width` and `truncate-cols` for any string that could contain non-ASCII. Plain `count`/`subs` are still allowed for ASCII internals (box-drawing strings we authored, single-glyph keystroke labels, etc.) where the answer is identical and the call is a hot path.
(align s width mode)Align s to width display columns by mode:
:left pad on the right (default for any unknown mode)
:right pad on the left
:center pad both sides
:justify full-justify the words (flush both margins).
Align `s` to `width` display columns by `mode`: :left pad on the right (default for any unknown mode) :right pad on the left :center pad both sides :justify full-justify the words (flush both margins).
(ansi-fold-cols s max-cols)Like fold-cols, but ANSI-SGR aware: char-fold s into a vector of
segments each at most max-cols display columns wide, never splitting a
grapheme cluster and never counting an \u001b[..m escape toward the
width. The SGR sequence active at a cut is RE-OPENED at the head of the
next segment (the cut segment closed with \u001b[0m), so a
syntax-highlighted line that folds keeps each token's color across the
break instead of being clipped at the bubble edge.
This is lanterna's TerminalTextUtils/ansiFoldColumns (>= 3.1.5-vis.26) —
the same grapheme/EAW-aware column engine as fold-cols/display-width;
ESC-free input takes the plain fold fast path. nil/empty returns [""].
Like `fold-cols`, but ANSI-SGR aware: char-fold `s` into a vector of segments each at most `max-cols` display columns wide, never splitting a grapheme cluster and never counting an `\u001b[..m` escape toward the width. The SGR sequence active at a cut is RE-OPENED at the head of the next segment (the cut segment closed with `\u001b[0m`), so a syntax-highlighted line that folds keeps each token's color across the break instead of being clipped at the bubble edge. This is lanterna's `TerminalTextUtils/ansiFoldColumns` (>= 3.1.5-vis.26) — the same grapheme/EAW-aware column engine as `fold-cols`/`display-width`; ESC-free input takes the plain fold fast path. nil/empty returns `[""]`.
(ansi-slice-cols s start width)Return the display-column WINDOW [start, start+width) of s as a string —
the horizontal less -S clip the code pager paints each row with (CHOP, not
fold). ANSI-SGR aware: \u001b[..m escapes never count toward a column, the
SGR active at the window's LEFT edge is RE-OPENED at the head of the result,
escapes that fall INSIDE the window are kept inline, and the result is closed
with \u001b[0m whenever any SGR was emitted — so a syntax-highlighted row
keeps its colors when scrolled sideways.
This is lanterna's TerminalTextUtils/ansiSliceColumns (>= 3.1.5-vis.26).
ESC-free input is a plain grapheme-safe column slice (never splits a
cluster). Negative start clamps to 0; non-positive width yields "".
Return the display-column WINDOW `[start, start+width)` of `s` as a string — the horizontal `less -S` clip the code pager paints each row with (CHOP, not fold). ANSI-SGR aware: `\u001b[..m` escapes never count toward a column, the SGR active at the window's LEFT edge is RE-OPENED at the head of the result, escapes that fall INSIDE the window are kept inline, and the result is closed with `\u001b[0m` whenever any SGR was emitted — so a syntax-highlighted row keeps its colors when scrolled sideways. This is lanterna's `TerminalTextUtils/ansiSliceColumns` (>= 3.1.5-vis.26). ESC-free input is a plain grapheme-safe column slice (never splits a cluster). Negative `start` clamps to 0; non-positive `width` yields "".
(boxed-horiz-line widths left junction right)Return a bordered horizontal rule, e.g. ┌──┬───┐.
Return a bordered horizontal rule, e.g. `┌──┬───┐`.
(center-text s w)Center string within w terminal columns, padding both sides.
Truncates (column-aware) if too wide. Backed by the lanterna fork's
TerminalTextUtils/center (>= 3.1.5-vis.25).
Center string within `w` terminal columns, padding both sides. Truncates (column-aware) if too wide. Backed by the lanterna fork's `TerminalTextUtils/center` (>= 3.1.5-vis.25).
(clamp x lo hi)Clamp x into the inclusive range [lo, hi]. Primitive-long in and out —
the one canonical range clamp every dialog / header / provider view shares.
Delegates to the lanterna fork's TerminalTextUtils/clamp (>= 3.1.5-vis.27)
so the whole channel reuses ONE primitive range clamp.
Clamp `x` into the inclusive range [lo, hi]. Primitive-long in and out — the one canonical range clamp every dialog / header / provider view shares. Delegates to the lanterna fork's `TerminalTextUtils/clamp` (>= 3.1.5-vis.27) so the whole channel reuses ONE primitive range clamp.
(clear-styles! g)Remove all active text styles.
Remove all active text styles.
(col-prefix-end s max-cols)Return the char-index i such that (subs s 0 i) is the longest
prefix of s whose display-width is <= max-cols AND that does NOT
split a grapheme cluster.
Use this when you need both the prefix and the position of the
un-consumed remainder (e.g. in word-wrapping). For just the prefix,
truncate-cols is friendlier.
Delegates to the lanterna fork's TerminalTextUtils/columnPrefixLength
(>= 3.1.5-vis.24). Returns 0 for nil/empty or non-positive max-cols.
Return the char-index `i` such that `(subs s 0 i)` is the longest prefix of `s` whose `display-width` is <= `max-cols` AND that does NOT split a grapheme cluster. Use this when you need both the prefix and the position of the un-consumed remainder (e.g. in word-wrapping). For just the prefix, `truncate-cols` is friendlier. Delegates to the lanterna fork's `TerminalTextUtils/columnPrefixLength` (>= 3.1.5-vis.24). Returns 0 for nil/empty or non-positive `max-cols`.
(cursor-pos col row)Create a TerminalPosition for cursor placement.
Create a TerminalPosition for cursor placement.
(disable! g & modifiers)Disable one or more text styles.
Disable one or more text styles.
(display-width s)Number of terminal columns s will occupy when painted by lanterna.
Delegates to the lanterna fork's TerminalTextUtils/displayColumns
(>= 3.1.5-vis.24): grapheme clusters honoured, CJK + emoji counted as
two columns, ASCII as one, inline-span sentinels (\uE110..\uE119) as
zero, C0 control bytes sanitized to / first. This measures by the
exact same rule AbstractTextGraphics.putString paints by, so what we
measure always matches what the renderer emits.
s is coerced via (str s) first: a caller that maps display-width
over a string (handing us a Character / number) must never crash the
render thread with a ClassCastException. nil → 0.
Returns 0 for nil/empty input.
Number of terminal columns `s` will occupy when painted by lanterna. Delegates to the lanterna fork's `TerminalTextUtils/displayColumns` (>= 3.1.5-vis.24): grapheme clusters honoured, CJK + emoji counted as two columns, ASCII as one, inline-span sentinels (\uE110..\uE119) as zero, C0 control bytes sanitized to `/` first. This measures by the exact same rule `AbstractTextGraphics.putString` paints by, so what we measure always matches what the renderer emits. `s` is coerced via `(str s)` first: a caller that maps display-width over a string (handing us a Character / number) must never crash the render thread with a ClassCastException. nil → 0. Returns 0 for nil/empty input.
(dot-cell! g col row fg)Overlay a bottom-flush ▁ mark in fg colour onto the SINGLE cell at
(col,row), keeping its existing background, but ONLY when that cell is
currently a blank space OR an existing ▁ mark. ▁ (LOWER ONE EIGHTH
BLOCK) sits flush on the cell's bottom edge — the SAME row as an
underline-cell! border — so the
running tab's marching marks line up with the ready tab's underline instead
of floating one line high like a baseline . did (which read as flicker).
Only blank padding cells are touched, so the number, | separator,
label, and close ✕ (non-blank) are never clobbered — but a prior ▁ may be
RE-COLOURED, which lets a running tab paint a dim base line then overlay a
bright marching band on top. No-op off-screen or over
any non-blank glyph.
Overlay a bottom-flush `▁` mark in `fg` colour onto the SINGLE cell at (col,row), keeping its existing background, but ONLY when that cell is currently a blank space OR an existing `▁` mark. `▁` (LOWER ONE EIGHTH BLOCK) sits flush on the cell's bottom edge — the SAME row as an `underline-cell!` border — so the running tab's marching marks line up with the ready tab's underline instead of floating one line high like a baseline `.` did (which read as flicker). Only blank padding cells are touched, so the number, ` | ` separator, label, and close ✕ (non-blank) are never clobbered — but a prior `▁` may be RE-COLOURED, which lets a running tab paint a dim base line then overlay a bright marching band on top. No-op off-screen or over any non-blank glyph.
(draw-box! g left top w h)Draw a single-line bordered box at (left, top) of size wxh. Draws corners, edges. Does NOT fill interior.
Draw a single-line bordered box at (left, top) of size wxh. Draws corners, edges. Does NOT fill interior.
(draw-centered! g left row width text)Draw text centered at row within [left, left+width).
Draw text centered at row within [left, left+width).
(draw-selection-marker! g col row selected?)(draw-selection-marker! g col row selected? marker-fg)Paint the selection marker at (col, row) when selected? is
truthy. Unselected rows get nothing — the surrounding row fill is
expected to already cover those cells.
The glyph is rendered BOLD. Provide marker-fg to give it its
own color (e.g. dialog-hint-key); when omitted, whatever fg is
currently set on g is used.
See the Selection marker block above for the project-wide
rationale. Callers that need the prefix as a STRING (to inline
into a row label) should use selection-prefix instead.
Paint the selection marker at (col, row) when `selected?` is truthy. Unselected rows get nothing — the surrounding row fill is expected to already cover those cells. The glyph is rendered BOLD. Provide `marker-fg` to give it its own color (e.g. `dialog-hint-key`); when omitted, whatever fg is currently set on `g` is used. See the `Selection marker` block above for the project-wide rationale. Callers that need the prefix as a STRING (to inline into a row label) should use `selection-prefix` instead.
(draw-separator! g left right row)Draw a horizontal separator with T-junctions at left/right edges.
Draw a horizontal separator with T-junctions at left/right edges.
(draw-space-around! g left row width items)Draw items spread across row within [left, left+width) with space-around.
Draw items spread across row within [left, left+width) with space-around.
(draw-space-between! g left row width items)Draw items spread across row within [left, left+width) with space-between.
Draw items spread across row within [left, left+width) with space-between.
(draw-tabs! g
tabs
{:keys [left row width active-id gap fg bg active-fg active-bg
inactive-fg inactive-bg bordered?]})Draw a tab strip and return its geometry.
Required opts: :left, :row, :width, :active-id, :fg, :bg,
:active-fg, :active-bg, :inactive-fg, :inactive-bg.
Optional: :gap, :bordered?.
This primitive knows layout and drawing only. Callers own domain actions and click-region registration.
Draw a tab strip and return its geometry. Required opts: `:left`, `:row`, `:width`, `:active-id`, `:fg`, `:bg`, `:active-fg`, `:active-bg`, `:inactive-fg`, `:inactive-bg`. Optional: `:gap`, `:bordered?`. This primitive knows layout and drawing only. Callers own domain actions and click-region registration.
(ellipsize s max-cols)(ellipsize s max-cols marker)Shorten s to at most max-cols terminal columns, appending an ellipsis
marker (default …) when truncation happens. Grapheme-cluster + EAW safe
and WIDTH-CORRECT — the marker's own display width is charged against the
budget, so the result never exceeds max-cols (even for tiny budgets, where
the marker itself is truncated to fit). nil s → treated as empty;
max-cols <= 0 → ""; a string already within budget is returned as-is.
The single canonical column-ellipsis for the TUI — dialogs/table/header
delegate here. Backed by the lanterna fork's TerminalTextUtils/ellipsize
(>= 3.1.5-vis.24).
Shorten `s` to at most `max-cols` terminal columns, appending an ellipsis `marker` (default `…`) when truncation happens. Grapheme-cluster + EAW safe and WIDTH-CORRECT — the marker's own display width is charged against the budget, so the result never exceeds `max-cols` (even for tiny budgets, where the marker itself is truncated to fit). nil `s` → treated as empty; `max-cols <= 0` → `""`; a string already within budget is returned as-is. The single canonical column-ellipsis for the TUI — `dialogs`/`table`/`header` delegate here. Backed by the lanterna fork's `TerminalTextUtils/ellipsize` (>= 3.1.5-vis.24).
(enable! g & modifiers)Enable one or more text styles. modifiers are SGR constants (BOLD, ITALIC, etc.).
Enable one or more text styles. `modifiers` are SGR constants (BOLD, ITALIC, etc.).
(fill-rect! g col row w h)(fill-rect! g col row w h ch)Fill a rectangle at (col, row) of size wxh with the given char (default space).
Fill a rectangle at (col, row) of size wxh with the given char (default space).
(fold-cols s max-cols)Character-fold s into a vector of segments, each at most max-cols
display columns wide, never splitting a grapheme cluster.
This is a terminal-style SOFT WRAP, the soft-wrap primitive shared by
the agent code rail and the tool-result code blocks: unlike word-wrap it
never drops or reflows whitespace — the bytes are preserved exactly and a
break is inserted only at the column boundary. So a pathologically wide
single line (a one-line git_commit message arg, a wide clj_eval
value map) folds at the bubble edge instead of overflowing or being
clipped, while indentation and in-row column alignment survive.
The fold itself is lanterna's TerminalTextUtils/foldColumns — the same
grapheme/EAW-aware text-flow family the screen paints with (displayWidth
/ wordWrap / justify). A line already within budget comes back [s]
unchanged, so normal multi-line source is untouched and only the over-wide
rows fold. nil/empty returns [""].
Input carrying an ESC (0x1b) is returned unfolded — foldColumns is
plain-text-only (the grapheme splitter throws on ESC); callers that need
ANSI-aware width clip such rows separately.
Character-fold `s` into a vector of segments, each at most `max-cols` display columns wide, never splitting a grapheme cluster. This is a terminal-style SOFT WRAP, the soft-wrap primitive shared by the agent code rail and the tool-result code blocks: unlike word-wrap it never drops or reflows whitespace — the bytes are preserved exactly and a break is inserted only at the column boundary. So a pathologically wide single line (a one-line `git_commit` message arg, a wide `clj_eval` value map) folds at the bubble edge instead of overflowing or being clipped, while indentation and in-row column alignment survive. The fold itself is lanterna's `TerminalTextUtils/foldColumns` — the same grapheme/EAW-aware text-flow family the screen paints with (`displayWidth` / `wordWrap` / `justify`). A line already within budget comes back `[s]` unchanged, so normal multi-line source is untouched and only the over-wide rows fold. nil/empty returns `[""]`. Input carrying an ESC (0x1b) is returned unfolded — `foldColumns` is plain-text-only (the grapheme splitter throws on ESC); callers that need ANSI-aware width clip such rows separately.
(horiz-line n)Return a string of n horizontal box-drawing chars. Delegates to lanterna's
primitive terminal-rule builder instead of rebuilding via Clojure seqs.
Return a string of `n` horizontal box-drawing chars. Delegates to lanterna's primitive terminal-rule builder instead of rebuilding via Clojure seqs.
(inline-sentinel? g)True when g (a single grapheme String) is one of the eight inline
span sentinels above. Cheap range check, no map lookup.
True when `g` (a single grapheme String) is one of the eight inline span sentinels above. Cheap range check, no map lookup.
(joined-horiz-line widths junction)Return horizontal runs joined by junction, e.g. ──┬───.
Return horizontal runs joined by `junction`, e.g. `──┬───`.
(justify s width)(justify s width justify-last?)Word-wrap s to width columns, then FULL-JUSTIFY every line, leaving each
paragraph's final line left-aligned (the standard typographic convention).
Pass justify-last? true to stretch the final line too. Returns a vec of
lines.
Word-wrap `s` to `width` columns, then FULL-JUSTIFY every line, leaving each paragraph's final line left-aligned (the standard typographic convention). Pass `justify-last?` true to stretch the final line too. Returns a vec of lines.
(justify-line line width)Full-justify the words on line to EXACTLY width display columns by
distributing inter-word spaces (flush to both margins). A blank or
single-word line is left-aligned (right-padded) instead — nothing to
stretch.
Full-justify the words on `line` to EXACTLY `width` display columns by distributing inter-word spaces (flush to both margins). A blank or single-word line is left-aligned (right-padded) instead — nothing to stretch.
(pad-left s w)Pad string to w terminal columns, left-filling with spaces.
Truncates (column-aware) if too wide. Backed by the lanterna fork's
TerminalTextUtils/padLeft (>= 3.1.5-vis.25).
Pad string to `w` terminal columns, left-filling with spaces. Truncates (column-aware) if too wide. Backed by the lanterna fork's `TerminalTextUtils/padLeft` (>= 3.1.5-vis.25).
(pad-right s w)Pad string to w terminal columns, right-filling with spaces.
Truncates (column-aware) if too wide. Backed by the lanterna fork's
TerminalTextUtils/padRight (>= 3.1.5-vis.25).
Pad string to `w` terminal columns, right-filling with spaces. Truncates (column-aware) if too wide. Backed by the lanterna fork's `TerminalTextUtils/padRight` (>= 3.1.5-vis.25).
(paint-styled-line! g x y line base-fg base-bg code-fg code-bg)Paint a single line at (x, y) honouring inline span sentinels.
The line may contain interleaved text and INLINE_*_ON/OFF
sentinels (range \uE110...\uE117). Sentinels themselves are NEVER
painted; they toggle the SGR style (BOLD/ITALIC/CROSSED-OUT) or
the fg/bg colors (CODE) for the spans that follow.
base-fg / base-bg are the colors used for non-code spans.
Code spans force code-fg / code-bg until INLINE_CODE_OFF.
Robustness contract:
TextCharacter/fromString),
so column tracking is exact for emoji + CJK.g at entry (e.g.
a wrapping (p/styled g [p/ITALIC] ...) for blockquotes). Inline
toggles STACK on top of the inherited set: > **bold** inside a
quote renders as bold-italic, not bold-without-italic. The pre-fix
version called clearModifiers at entry and silently dropped the
wrapping italic - user-visible bug on every blockquote that
contained inline emphasis.put-str! call - the same as the old non-styled path - so the
hot path is not penalised for the common case.This is the entry point that lets the markdown renderer support
bold / italic / ~~strike~~ / code mid-line without
replacing the marker-prefix-per-line architecture above.
Paint a single line at (x, y) honouring inline span sentinels. The line may contain interleaved text and `INLINE_*_ON`/`OFF` sentinels (range \uE110...\uE117). Sentinels themselves are NEVER painted; they toggle the SGR style (BOLD/ITALIC/CROSSED-OUT) or the fg/bg colors (CODE) for the spans that follow. `base-fg` / `base-bg` are the colors used for non-code spans. Code spans force `code-fg` / `code-bg` until INLINE_CODE_OFF. Robustness contract: - Walks by GRAPHEME CLUSTER (lanterna's `TextCharacter/fromString`), so column tracking is exact for emoji + CJK. - INHERITS any SGR modifiers already enabled on `g` at entry (e.g. a wrapping `(p/styled g [p/ITALIC] ...)` for blockquotes). Inline toggles STACK on top of the inherited set: `> **bold**` inside a quote renders as bold-italic, not bold-without-italic. The pre-fix version called `clearModifiers` at entry and silently dropped the wrapping italic - user-visible bug on every blockquote that contained inline emphasis. - Unmatched / dangling sentinels (e.g. an OFF without a prior ON, or a line that ends mid-bold) are tolerated: at exit we restore exactly the inherited modifier set, so SGR state never leaks past the call. - When NO sentinel appears in the line this collapses to a single `put-str!` call - the same as the old non-styled path - so the hot path is not penalised for the common case. This is the entry point that lets the markdown renderer support **bold** / *italic* / ~~strike~~ / `code` mid-line without replacing the marker-prefix-per-line architecture above.
(put-str! g col row text)Draw a string at (col, row). Control characters are sanitized.
Draw a string at (col, row). Control characters are sanitized.
(selection-prefix selected?)Return the leading marker string for a list/menu row.
Use this for rows where the marker can be inlined into the body
text (simple list items, checkbox rows, slash-command rows). For
rows where the marker must live OUTSIDE a fixed-column body
(file/session pickers, provider/model cards) call
draw-selection-marker! from the caller's row loop instead.
Return the leading marker string for a list/menu row. Use this for rows where the marker can be inlined into the body text (simple list items, checkbox rows, slash-command rows). For rows where the marker must live OUTSIDE a fixed-column body (file/session pickers, provider/model cards) call `draw-selection-marker!` from the caller's row loop instead.
Two-col selection marker. Selected rows show •+space, unselected
rows show two spaces, so the body content stays column-aligned.
The glyph MUST be display-width 1 so glyph+space == 2 cols and
exactly fills the reserved gutter. • is the project-wide selector
dot, kept bare (no VS-16) to avoid terminal width surprises.
Two-col selection marker. Selected rows show `•`+space, unselected rows show two spaces, so the body content stays column-aligned. The glyph MUST be display-width 1 so glyph+space == 2 cols and exactly fills the reserved gutter. `•` is the project-wide selector dot, kept bare (no VS-16) to avoid terminal width surprises.
(set-char! g col row ch)Draw a single character at (col, row).
Draw a single character at (col, row).
(set-colors! g fg bg)Set both foreground and background color in one call.
Set both foreground and background color in one call.
(space-around items w)Distribute items across w terminal columns with equal space
around each item. Like CSS justify-content: space-around. Backed by the
lanterna fork's TerminalTextUtils/spaceAround (>= 3.1.5-vis.25).
Distribute items across `w` terminal columns with equal space around each item. Like CSS justify-content: space-around. Backed by the lanterna fork's `TerminalTextUtils/spaceAround` (>= 3.1.5-vis.25).
(space-between items w)Distribute items across w terminal columns with equal gaps.
First item flush-left, last item flush-right, rest evenly spaced.
Like CSS justify-content: space-between. Backed by the lanterna fork's
TerminalTextUtils/spaceBetween (>= 3.1.5-vis.25).
Distribute items across `w` terminal columns with equal gaps. First item flush-left, last item flush-right, rest evenly spaced. Like CSS justify-content: space-between. Backed by the lanterna fork's `TerminalTextUtils/spaceBetween` (>= 3.1.5-vis.25).
(status-mark! g col row glyph fg bg)Paint a 1-col status glyph in colour fg on bg at (col,row) and return
the next col (col + STATUS_WIDTH) so the caller can place the label right
after. The reusable mark behind settings rows + resource rows.
Paint a 1-col status `glyph` in colour `fg` on `bg` at (col,row) and return the next col (`col + STATUS_WIDTH`) so the caller can place the label right after. The reusable mark behind settings rows + resource rows.
(styled g styles & body)Draw with styles temporarily enabled. Restores previous styles after body. Usage: (styled g [BOLD ITALIC] (put-str! g 0 0 "hello"))
Draw with styles temporarily enabled. Restores previous styles after body. Usage: (styled g [BOLD ITALIC] (put-str! g 0 0 "hello"))
(tab-at layout col)Return the tab geometry under col, or nil. Expects tab-layout output.
Return the tab geometry under `col`, or nil. Expects `tab-layout` output.
(tab-display-label {:keys [id label dirty? state]})Compact label for a tab map.
Supported tab keys:
:label / :id - visible base label.:dirty? - appends a dirty marker.:state - appends a compact state badge for running / verified /
accepted / error tabs.This is terminal-column aware when later passed through tab-layout, so
callers may use emoji or CJK labels without hand-padding.
Compact label for a tab map. Supported tab keys: - `:label` / `:id` - visible base label. - `:dirty?` - appends a dirty marker. - `:state` - appends a compact state badge for running / verified / accepted / error tabs. This is terminal-column aware when later passed through `tab-layout`, so callers may use emoji or CJK labels without hand-padding.
(tab-layout tabs left width active-id)(tab-layout tabs left width active-id {:keys [gap]})Return tab geometry for a horizontal tab strip.
tabs is a seq of maps. Returned maps include :left, :width,
:active?, and :text truncated to fit :width. Width math uses terminal
columns, not Java chars. When the strip is too narrow, later tabs may get
zero width; draw helpers skip those safely.
Return tab geometry for a horizontal tab strip. `tabs` is a seq of maps. Returned maps include `:left`, `:width`, `:active?`, and `:text` truncated to fit `:width`. Width math uses terminal columns, not Java chars. When the strip is too narrow, later tabs may get zero width; draw helpers skip those safely.
(truncate-cols s max-cols)Return the longest prefix of s that fits in at most max-cols
terminal columns, never splitting a grapheme cluster.
Edge cases honoured:
max-cols <= 0 returns "".display-width is exactly max-cols. This keeps
pad-right / pad-left idempotent under repeated truncation.Delegates to the lanterna fork's TerminalTextUtils/truncateColumns
(>= 3.1.5-vis.24).
Return the longest prefix of `s` that fits in at most `max-cols` terminal columns, never splitting a grapheme cluster. Edge cases honoured: - nil or `max-cols <= 0` returns `""`. - If a double-width grapheme would straddle the cut, it is dropped (NOT half-included), and one space is appended in its place so the returned string's `display-width` is exactly `max-cols`. This keeps `pad-right` / `pad-left` idempotent under repeated truncation. - Trailing zero-width inline-span sentinels (style closers) are always emitted, never stranded past the cut. Delegates to the lanterna fork's `TerminalTextUtils/truncateColumns` (>= 3.1.5-vis.24).
(truncate-middle s max-cols)Shorten s to at most max-cols columns by ELIDING THE MIDDLE behind a
single …, keeping both the HEAD and the TAIL. Ideal for file paths, where
the basename (tail) is as informative as the leading dirs — plain
truncate-cols drops the filename. Falls back to head truncation when there
isn't room for both sides plus the ellipsis. Grapheme-cluster safe.
Backed by the lanterna fork's TerminalTextUtils/truncateMiddle
(>= 3.1.5-vis.25).
Shorten `s` to at most `max-cols` columns by ELIDING THE MIDDLE behind a single `…`, keeping both the HEAD and the TAIL. Ideal for file paths, where the basename (tail) is as informative as the leading dirs — plain `truncate-cols` drops the filename. Falls back to head truncation when there isn't room for both sides plus the ellipsis. Grapheme-cluster safe. Backed by the lanterna fork's `TerminalTextUtils/truncateMiddle` (>= 3.1.5-vis.25).
(underline-cell! g col row)(underline-cell! g col row fg)Add the UNDERLINE modifier to the SINGLE already-painted cell at (col,row),
preserving its glyph + background. Reads the cell back via getCharacter
and re-sets it with the modifier folded in, so callers can paint a
PER-COLUMN border (a steady / marching underline) that the global
enable!/UNDERLINE attribute — which would underline every cell touched
after it — can't express. No-op when the cell is empty/off-screen.
3-arity keeps the cell's own foreground (the underline inherits it).
4-arity RECOLOURS the foreground to fg first, so the underline — which a
terminal draws in the cell's foreground colour — renders in fg. Recolour
is uniform across blank padding AND glyph cells, giving ONE consistent
underline shape/weight everywhere (no block-glyph vs SGR-line mismatch).
Add the UNDERLINE modifier to the SINGLE already-painted cell at (col,row), preserving its glyph + background. Reads the cell back via `getCharacter` and re-sets it with the modifier folded in, so callers can paint a PER-COLUMN border (a steady / marching underline) that the global `enable!`/`UNDERLINE` attribute — which would underline every cell touched after it — can't express. No-op when the cell is empty/off-screen. 3-arity keeps the cell's own foreground (the underline inherits it). 4-arity RECOLOURS the foreground to `fg` first, so the underline — which a terminal draws in the cell's foreground colour — renders in `fg`. Recolour is uniform across blank padding AND glyph cells, giving ONE consistent underline shape/weight everywhere (no block-glyph vs SGR-line mismatch).
(v-center-offset content-h container-h)Compute vertical offset to center content-h rows within container-h rows.
Backed by the lanterna fork's TerminalTextUtils/verticalCenterOffset
(>= 3.1.5-vis.25).
Compute vertical offset to center `content-h` rows within `container-h` rows. Backed by the lanterna fork's `TerminalTextUtils/verticalCenterOffset` (>= 3.1.5-vis.25).
(with-style g styles body-fn)Execute body-fn (fn [g] ...) with the given styles enabled, then restore.
Returns the result of body-fn.
Execute `body-fn` (fn [g] ...) with the given styles enabled, then restore. Returns the result of `body-fn`.
(word-wrap s width)Greedy word-wrap s into a vec of lines, each fitting width DISPLAY
columns. Breaks on whitespace; a token wider than width is hard-split at
grapheme boundaries (never mid-cluster); embedded newlines are honoured.
Blank input or width <= 0 yields [""].
Greedy word-wrap `s` into a vec of lines, each fitting `width` DISPLAY columns. Breaks on whitespace; a token wider than `width` is hard-split at grapheme boundaries (never mid-cluster); embedded newlines are honoured. Blank input or `width` <= 0 yields `[""]`.
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 |