Raxol.UI.Rendering.PaintAuthority behaviour (Raxol v2.6.1)

View Source

The single seam both inline-render emit paths go through: the printed- history append path and the pinned-viewport repaint path.

One owner module, both paths go through it: this behaviour is the CONTRACT that makes that true — implementations execute it against the real terminal (IOAuthority, below); tests drive it against Raxol.Harness.Test.CaptureAuthority, which records every emit as an origin-tagged entry instead of writing bytes. Because the tag layer is a property of the TEST double, not the behaviour, production callers pay nothing for it.

Callback origins (mirrors Raxol.Harness.Test.CaptureAuthority.Emit.origin):

  • append_sealed/2:seal, append at the region's bottom line.
  • repaint_footer/2:footer, repaint the pinned footer rows.
  • keyframe_footer/2:keyframe, Ctrl-L / resize footer redraw.
  • with_cursor/3:cursor, the save -> position -> emit -> restore bracket; the sole owner of cursor save/restore so the two paths never race.
  • resize/3 — policy-bearing: which of seal-time-only / soft-owned / live-region-only a resize follows is a terminal- compatibility decision (D-PA; docs/proposals/t0-verdict-schema.md tracks the measurement matrix that resolves it). This behaviour does not pick a policy; implementers do.
  • region_top/1 — the current split point H - N (number of rows in the scrolling history region; rows below it are the footer).

This module intentionally carries NO implementation logic — it is the interface implementations build against and the test harness doubles. See Raxol.Harness.Test.CaptureAuthority for the recording test double and Raxol.Harness.Test.SealOracle for the assertions built on top of it.

Summary

Types

Which pinned region a with_cursor/3 bracket is positioning into.

t()

Opaque authority state; each implementation defines its own shape.

Callbacks

Append sealed lines at the bottom of the scrolling history region.

Footer-only keyframe redraw (Ctrl-L recovery, resize). Never \e[2J.

Current footer boundary: row count of the scrolling history region (H - N).

Repaint some or all of the pinned footer (live tail + strip + composer).

Applies a resize. Policy-bearing per D-PA: what happens to already-sealed history on resize is a terminal-compatibility decision this behaviour stays agnostic to (docs/proposals/t0-verdict-schema.md tracks the measurement matrix that resolves it). Implementations MUST emit their DECSTBM re-set (one Dialect.region_set/2 per resize) through their own emit path, so INV-5's "re-set exactly once as CSI 1;(h-N) r" is byte-assertable.

Runs fun under a save -> position-into-region -> ... -> restore cursor bracket. The single owner of the save/restore protocol; both append_sealed and repaint_footer paths must route cursor movement through this so saves and restores never interleave across paths.

Types

cursor_region()

@type cursor_region() :: :history | :footer

Which pinned region a with_cursor/3 bracket is positioning into.

t()

@type t() :: term()

Opaque authority state; each implementation defines its own shape.

Callbacks

append_sealed(t, iodata)

@callback append_sealed(t(), iodata()) :: t()

Append sealed lines at the bottom of the scrolling history region.

keyframe_footer(t, iodata)

@callback keyframe_footer(t(), iodata()) :: t()

Footer-only keyframe redraw (Ctrl-L recovery, resize). Never \e[2J.

region_top(t)

@callback region_top(t()) :: pos_integer()

Current footer boundary: row count of the scrolling history region (H - N).

repaint_footer(t, iodata)

@callback repaint_footer(t(), iodata()) :: t()

Repaint some or all of the pinned footer (live tail + strip + composer).

resize(t, width, height)

@callback resize(t(), width :: pos_integer(), height :: pos_integer()) :: t()

Applies a resize. Policy-bearing per D-PA: what happens to already-sealed history on resize is a terminal-compatibility decision this behaviour stays agnostic to (docs/proposals/t0-verdict-schema.md tracks the measurement matrix that resolves it). Implementations MUST emit their DECSTBM re-set (one Dialect.region_set/2 per resize) through their own emit path, so INV-5's "re-set exactly once as CSI 1;(h-N) r" is byte-assertable.

with_cursor(t, cursor_region, function)

@callback with_cursor(t(), cursor_region(), (t() -> t())) :: t()

Runs fun under a save -> position-into-region -> ... -> restore cursor bracket. The single owner of the save/restore protocol; both append_sealed and repaint_footer paths must route cursor movement through this so saves and restores never interleave across paths.