Liking cljdoc? Tell your friends :D

com.blockether.vis.ext.channel-tui.render-ir

TUI walker over canonical answer-IR (com.blockether.vis.internal.render/->ast).

Pure data: IR -> vector of lines. Each line is a vector of styled runs. The screen layer turns runs into ANSI / Lanterna cells.

Why a dedicated walker (and not ir/render :plain):

  • We need styling metadata per character run (bold / italic / inline code background / link target) so the TUI can paint colours and attach click + selection regions.
  • We need word-wrap at an arbitrary terminal width with hanging indent inside lists and quotes.
  • We need stable per-node identifiers so selection / hover / click can map a glyph back to its IR node.

Canonical-IR invariants we rely on (see internal.render docstring):

  • text only inside :span / raw bodies of :code / :c / :kbd;
  • no '\n' inside :span;
  • hard breaks are explicit [:br {}];
  • attrs map present on every vector node.

Run shape: {:text String ; never empty, never contains '\n' :style #{:bold :italic :code :dim :link :heading :marker :quote} :href String? ; present iff :link in style :node any?} ; opaque node identity for click/select

Public API: (ir->lines ir width) ; total walker (ir->lines ir width opts) Opts: :heading-prefix? bool ; render '#'-style markers (default false; bold suffices) :code-fence? bool ; render ``` lines around code blocks (default false) :max-lines int ; hard cap (default unlimited)

TUI walker over canonical answer-IR (`com.blockether.vis.internal.render/->ast`).

Pure data: IR -> vector of lines. Each line is a vector of styled
runs. The screen layer turns runs into ANSI / Lanterna cells.

Why a dedicated walker (and not `ir/render :plain`):
- We need styling metadata per character run (bold / italic / inline
  code background / link target) so the TUI can paint colours and
  attach click + selection regions.
- We need word-wrap at an arbitrary terminal width with hanging
  indent inside lists and quotes.
- We need stable per-node identifiers so selection / hover / click
  can map a glyph back to its IR node.

Canonical-IR invariants we rely on (see internal.render docstring):
- text only inside `:span` / raw bodies of `:code` / `:c` / `:kbd`;
- no '\n' inside `:span`;
- hard breaks are explicit `[:br {}]`;
- attrs map present on every vector node.

Run shape:
  {:text   String          ; never empty, never contains '\n'
   :style  #{:bold :italic :code :dim :link :heading :marker :quote}
   :href   String?         ; present iff :link in style
   :node   any?}           ; opaque node identity for click/select

Public API:
  (ir->lines ir width)             ; total walker
  (ir->lines ir width opts)
Opts:
  :heading-prefix? bool   ; render '#'-style markers (default false; bold suffices)
  :code-fence?     bool   ; render ``` lines around code blocks (default false)
  :max-lines       int    ; hard cap (default unlimited)
raw docstring

ir->entriesclj

(ir->entries ir width)
(ir->entries ir width opts)

Drop-in replacement for the legacy render/markdown->entries. Returns a vector of {:line :meta} maps where :line is the sentinel-prefixed string the bubble painter consumes, and :meta is per-line click-region metadata. nil :meta for ordinary content lines.

:mode selects the marker set: :answer (default) — answer-zone PUA chars (answer-bg paint) :thinking — thinking-zone PUA chars (iter-header-bg + italic) :channel — in-place channel/tool IR; plain paragraphs use no background marker, structural rows keep explicit styling

This is the IR-side analogue of markdown->entries. Every bubble rendering path that used to parse the rendered markdown back into entries should call this directly on the canonical IR — no markdown round-trip.

Drop-in replacement for the legacy `render/markdown->entries`.
Returns a vector of `{:line :meta}` maps where `:line` is the
sentinel-prefixed string the bubble painter consumes, and `:meta`
is per-line click-region metadata. `nil` `:meta` for ordinary content
lines.

`:mode` selects the marker set:
  `:answer`   (default) — answer-zone PUA chars (answer-bg paint)
  `:thinking` — thinking-zone PUA chars (iter-header-bg + italic)
  `:channel`  — in-place channel/tool IR; plain paragraphs use no
                background marker, structural rows keep explicit styling

This is the IR-side analogue of `markdown->entries`. Every
bubble rendering path that used to parse the rendered markdown
back into entries should call this directly on the canonical IR
— no markdown round-trip.
sourceraw docstring

ir->inline-sentinel-stringclj

(ir->inline-sentinel-string ir)

Flatten a canonical IR into a single sentinel-wrapped inline string suitable for chrome-row labels. NO block markers — just inline sentinels (INLINE_BOLD_ON/OFF, INLINE_CODE_ON/OFF, etc.) wrapping styled runs. Hard breaks [:br] become spaces (single line contract). Used by chrome labels and other surfaces that previously ran through markdown->inline.

Flatten a canonical IR into a single sentinel-wrapped inline string
suitable for chrome-row labels. NO block markers — just inline
sentinels (`INLINE_BOLD_ON/OFF`, `INLINE_CODE_ON/OFF`, etc.) wrapping
styled runs. Hard breaks `[:br]` become spaces (single line
contract). Used by chrome labels and other surfaces that previously
ran through `markdown->inline`.
sourceraw docstring

ir->linesclj

(ir->lines input width)
(ir->lines input width opts)

Walk canonical IR (or any input that ir/->ast accepts) at a given terminal width and return a vector of styled lines.

Walk canonical IR (or any input that `ir/->ast` accepts) at a given
terminal `width` and return a vector of styled lines.
sourceraw docstring

ir->lines-tailclj

(ir->lines-tail input width tail-n)
(ir->lines-tail input width tail-n opts)

Render only the last tail-n styled lines of the IR.

Walks the top-level blocks BACKWARD, accumulating a cheap per-block line estimate (text-chars / content-width), until accumulated estimate ≥ tail-n * 2 (slack covers blank-collapse

  • per-block trailing blanks). Then renders ONLY those tail blocks via the normal ir->lines and (take-last tail-n).

Per-frame cost = O(visible-tail), independent of total body length. The semantically-correct path for an auto-scrolled tail-pinned bubble (vs the broken :max-lines cap which would give the FIRST tail-n).

tail-n must be positive. If the body has fewer total estimated lines than the budget, this falls back to a full walk.

Render only the last `tail-n` styled lines of the IR.

Walks the top-level blocks BACKWARD, accumulating a cheap
per-block line estimate (text-chars / content-width), until
accumulated estimate ≥ `tail-n * 2` (slack covers blank-collapse
+ per-block trailing blanks). Then renders ONLY those tail blocks
via the normal `ir->lines` and `(take-last tail-n)`.

Per-frame cost = O(visible-tail), independent of total body
length. The semantically-correct path for an auto-scrolled
tail-pinned bubble (vs the broken `:max-lines` cap which would
give the FIRST tail-n).

`tail-n` must be positive. If the body has fewer total estimated
lines than the budget, this falls back to a full walk.
sourceraw docstring

ir->lines-windowclj

(ir->lines-window input width start num)
(ir->lines-window input width start num opts)

Render only rows [start, start+num) of the IR.

Internally walks blocks forward with :max-lines (start + num + slack) so the walker short-circuits via the A1 blocks->lines path - work is O(start + num), not O(body). Output is bit-identical to (subvec (ir->lines ir w opts) start (+ start num)) when both are in range; result is clamped if the body has fewer lines.

Use cases:

  • start = 0: equivalent to (:max-lines num) head-cap.
  • start > 0: scrolled into a long bubble; only render the window. Note: skipping is still done by the walker producing start lines then discarding them - true zero-cost prefix skip would require per-block estimate-based skipping which this fn does not do.

num must be positive. start must be non-negative.

Render only rows `[start, start+num)` of the IR.

Internally walks blocks forward with `:max-lines (start + num +
slack)` so the walker short-circuits via the A1 `blocks->lines`
path - work is O(start + num), not O(body). Output is
bit-identical to `(subvec (ir->lines ir w opts) start (+ start
num))` when both are in range; result is clamped if the body has
fewer lines.

Use cases:
  - `start = 0`: equivalent to `(:max-lines num)` head-cap.
  - `start > 0`: scrolled into a long bubble; only render the
    window. Note: skipping is still done by the walker producing
    `start` lines then discarding them - true zero-cost prefix
    skip would require per-block estimate-based skipping which
    this fn does not do.

`num` must be positive. `start` must be non-negative.
sourceraw docstring

ir->sentinel-stringsclj

(ir->sentinel-strings ir width)
(ir->sentinel-strings ir width opts)

One-shot helper: canonical IR → vector of sentinel-prefixed strings ready for the existing bubble painter. Composes ir->lines with the sentinel adapter. :mode (:answer, :thinking, or :channel) selects the marker set.

One-shot helper: canonical IR → vector of sentinel-prefixed strings
ready for the existing bubble painter. Composes `ir->lines` with
the sentinel adapter. `:mode` (`:answer`, `:thinking`, or `:channel`)
selects the marker set.
sourceraw docstring

lines->plainclj

(lines->plain lines)

Concatenate the text of every run in lines. Useful for tests + clipboard fallback (preferred clipboard path: ir/render :markdown).

Concatenate the text of every run in `lines`. Useful for tests +
clipboard fallback (preferred clipboard path: `ir/render :markdown`).
sourceraw docstring

lines->sentinel-stringsclj

(lines->sentinel-strings lines)
(lines->sentinel-strings lines opts)

Convert walker output (vector of {:runs :block-tag :block-level?} maps) into the painter's sentinel-prefixed string contract. Each line: <block-marker><inline-sentinel-wrapped body>. :mode selects the marker set (:answer default, :thinking, or :channel).

Convert walker output (vector of `{:runs :block-tag :block-level?}`
maps) into the painter's sentinel-prefixed string contract. Each
line: `<block-marker><inline-sentinel-wrapped body>`. `:mode`
selects the marker set (`:answer` default, `:thinking`, or `:channel`).
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