|
| str | indent_text (str text, str indent=TURN_INDENT) |
| str | start_rule_label (str ts) |
| str | turn_rule_label (int turn_number, str ts) |
| str | turn_end_rule_label (int turn_number, str ts, str stats="") |
| list[tuple[str, str]] | turn_end_rule_segments (int turn_number, str ts, str stats="") |
| str | running_total_text (int turn_tokens, int session_tokens, "Callable[[int], str] | None" fmt=None) |
| list[tuple[str, str]] | compose_rule (str|list[tuple[str, str]] label, int width, str trailer="", str fill=RULE_FILL) |
| str | rule_line (str|list[tuple[str, str]] label, int width, str trailer="", str fill=RULE_FILL) |
| bool | rule_fits (str|list[tuple[str, str]] label, int width) |
| str | plain_box_top (int width, str title=ANSWER_BOX_TITLE, str indent=TURN_INDENT) |
| str | plain_box_bottom (int width, str subtitle="", str indent=TURN_INDENT) |
Turn-scoped layout primitives shared by every terminal presenter (plan 218).
Purpose
-------
Plan 218 makes each chat turn read as ONE bounded, indented block:
a ``start @`` rule opens the session, the assistant's answer sits in a
box whose top border carries ``Assistant Answer:`` and whose bottom
border carries that iteration's metadata, the turn-end rule absorbs the
turn stats plus the running total, and every line between the turn
rules is indented by :data:`TURN_INDENT`.
None of that geometry is presenter-specific, so it lives here as pure
string logic: :class:`~oc_ui.terminal.rich_presenter.RichPresenter`
maps the composed segments onto ``rich`` renderables and
:class:`~oc_ui.terminal.presenter.ConsolePresenter` maps the same
segments onto ASCII marker lines. One algorithm, two skins — the plain
presenter can never drift out of structural agreement with the rich one.
Framework-free by the same reasoning as
:mod:`~oc_ui.terminal.statusline`: no ``rich`` / ``prompt_toolkit``
import, so both presenters and the prompt driver can share it.
Responsibilities
----------------
- :data:`TURN_INDENT` / :func:`indent_text` — the three-space turn indent.
- :class:`LineIndenter` — line-buffered indenting for streamed chunks,
so a presenter that prints as it streams can still prefix whole lines.
- :func:`compose_rule` — the two-cluster rule layout (centred label plus
a trailer centred in the right-hand fill run) used by the turn-end rule.
- :func:`start_rule_label` / :func:`turn_rule_label` /
:func:`turn_end_rule_label` / :func:`running_total_text` — the rule
label strings.
- :func:`plain_box_top` / :func:`plain_box_bottom` — the glyph-free
degraded box markers for the plain presenter (P4).
Diagnostics
-----------
Domain: OC_UI.TERMINAL
Levels:
L2 — lifecycle events
L3 — semantic details
L4 — deep tracing
Contracts
---------
- Pure functions: no I/O, no clock reads, no framework imports. Callers
pass the timestamp/width they want rendered.
- :func:`compose_rule` always returns segments whose joined text is
EXACTLY *width* columns when the label fits, and never wraps: a label
wider than the available run is emitted un-truncated with the fill
runs collapsed to zero rather than being cut (the same "never wrap,
never lie" discipline :func:`~oc_ui.terminal.statusline.render_status_line`
applies to the status bar).
- The trailer is DROPPED, not truncated, when it cannot fit beside the
label — a partial running total would be a wrong number on screen.
- :class:`LineIndenter` is append-only and never re-emits a line it has
already returned; ``flush`` returns whatever tail is still buffered.
- No colour/style decisions here — segments are TAGGED (``"fill"`` /
``"label"`` / ``"trailer"``) and each presenter maps tags onto its own
existing style tokens, so plan 218's byte-preserved-colour pin (P3)
stays enforceable at the presenter boundary.
Dependencies
------------
- oc_diagnostics (structured debug logging via _dbg)
- oc_ui.terminal.statusline (lazy; fmt_tokens_short as the default running total formatter)
| list[tuple[str, str]] oc_ui.terminal.turn_layout.compose_rule |
( |
str | list[tuple[str, str]] | label, |
|
|
int | width, |
|
|
str | trailer = "", |
|
|
str | fill = RULE_FILL ) |
Compose a rule line as tagged segments.
Geometry (plan 218 P1 element 4, read off the operator's mock): the
*label* block is centred over the full *width* exactly as the pre-218
``rich.rule.Rule`` centred its title, and the *trailer* block is then
centred within the right-hand fill run that remains. Each block is
padded with two spaces either side, matching the two-space gutter
``Rule`` already produced for a label that itself carried one space.
*label* is either a plain string or a list of ``(text, tag)`` pairs
(see :func:`turn_end_rule_segments`) when parts of the label must
keep distinct styles.
Returns a list of ``(text, tag)`` pairs where *tag* is ``"fill"``,
``"trailer"``, or whichever tag the caller supplied for a label part
(``"label"`` for a plain-string label). Callers map tags onto their
own style tokens; this function makes no colour decision.
Definition at line 233 of file turn_layout.py.
| bool oc_ui.terminal.turn_layout.rule_fits |
( |
str | list[tuple[str, str]] | label, |
|
|
int | width ) |
Can *label* be absorbed into a *width*-column rule and still read as one?
Plan 218's turn-end rule swallows the whole turn-stats string, which on
a narrow terminal is longer than the terminal itself. Callers use this
to choose between the absorbed one-line rule and the pre-218 two-line
form (rule, then stats beneath): degrading to the OLD layout keeps
every figure legible, whereas letting an oversized label wrap would
split a rule across lines and cut a ``ctx:NN% of <model>`` gauge in
half. Never truncate, never wrap — degrade.
Definition at line 305 of file turn_layout.py.
| list[tuple[str, str]] oc_ui.terminal.turn_layout.turn_end_rule_segments |
( |
int | turn_number, |
|
|
str | ts, |
|
|
str | stats = "" ) |
:func:`turn_end_rule_label` split so the stats keep their own style.
Returns ``[(rule-label, "label"), (stats, "stats")]``. The split
exists for exactly one reason (plan 218 P3): the rule title was dim
cyan and the stats line was dim, and moving the stats INTO the rule
must not repaint them in the rule's colour. Presenters map the two
tags onto the two style tokens they already used.
Definition at line 196 of file turn_layout.py.