# `Minga.Core.Decorations.BlockDecoration`
[🔗](https://github.com/jsmestad/minga/blob/main/lib/minga/core/decorations/block_decoration.ex#L1)

A block decoration: fully custom-rendered lines injected between buffer lines.

Block decorations produce styled content from a render callback. That content
is inserted into the display line stream at an anchor position. They scroll
with the buffer, occupy display rows, and participate in viewport height
calculations, but have no buffer line number and no gutter.

This is the equivalent of Zed's `BlockProperties` (message headers, image
previews, slash command output in the AI chat).

## Render callback

The render callback receives the available width and returns styled content
in one of three forms:

- `[{text, style}]` — single-line block (most common: headers, separators)
- `[[{text, style}]]` — multi-line block (list of segment lists, one per row)
- `[DisplayList.draw()]` — raw draw tuples for maximum control

The content renderer normalizes the first two forms into draw tuples
positioned at the correct screen rows.

## Height

Set `height` to an explicit integer for blocks with a known, stable height.
Set `height` to `:dynamic` for blocks whose height depends on the render
callback output. Dynamic heights are resolved by invoking the callback
during DisplayMap construction. The callback is cached per block per frame,
so it's only called once regardless of height.

# `click_fn`

```elixir
@type click_fn() :: (row :: non_neg_integer(), col :: non_neg_integer() -&gt; :ok) | nil
```

Click callback: receives row offset within block and column.

# `render_fn`

```elixir
@type render_fn() :: (width :: pos_integer() -&gt; render_result())
```

Render callback: receives available width, returns styled content.

# `render_result`

```elixir
@type render_result() ::
  [{String.t(), Minga.Core.Face.t()}] | [[{String.t(), Minga.Core.Face.t()}]]
```

Result from a render callback.

- `[{text, style}]` — single-line block
- `[[{text, style}]]` — multi-line block (list of lines, each a list of segments)

# `t`

```elixir
@type t() :: %Minga.Core.Decorations.BlockDecoration{
  anchor_line: non_neg_integer(),
  group: term() | nil,
  height: pos_integer() | :dynamic,
  id: reference(),
  on_click: click_fn(),
  placement: :above | :below,
  priority: integer(),
  render: render_fn()
}
```

# `normalize_render_result`

```elixir
@spec normalize_render_result(render_result()) :: [
  [{String.t(), Minga.Core.Face.t()}]
]
```

Normalizes the render callback result into a list of segment lists
(one per display line).

# `resolve_height`

```elixir
@spec resolve_height(t(), pos_integer()) :: pos_integer()
```

Returns the height of the block decoration in display lines.

For explicit heights, returns the stored value. For `:dynamic` heights,
invokes the render callback with the given width to determine the height
from the result.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
