| """Render and truncate ordered transcript segments.""" |
|
|
| from collections import deque |
| from collections.abc import Iterable |
|
|
| from .context import RenderCtx |
| from .segments import Segment |
|
|
|
|
| def render_segments( |
| segments: Iterable[Segment], |
| ctx: RenderCtx, |
| *, |
| limit_chars: int, |
| status: str | None, |
| ) -> str: |
| rendered: list[str] = [] |
| for segment in segments: |
| try: |
| output = segment.render(ctx) |
| except Exception: |
| continue |
| if output: |
| rendered.append(output) |
|
|
| status_text = f"\n\n{status}" if status else "" |
| prefix_marker = ctx.escape_text("... (truncated)\n") |
|
|
| def _join(parts: Iterable[str], add_marker: bool) -> str: |
| body = "\n".join(parts) |
| if add_marker and body: |
| body = prefix_marker + body |
| return body + status_text if (body or status_text) else status_text |
|
|
| candidate = _join(rendered, add_marker=False) |
| if len(candidate) <= limit_chars: |
| return candidate |
|
|
| parts: deque[str] = deque(rendered) |
| dropped = False |
| last_part: str | None = None |
| while parts: |
| candidate = _join(parts, add_marker=True) |
| if len(candidate) <= limit_chars: |
| return candidate |
| last_part = parts.popleft() |
| dropped = True |
|
|
| if dropped and last_part: |
| budget = limit_chars - len(prefix_marker) - len(status_text) |
| if budget > 20: |
| tail = ( |
| "..." + last_part[-(budget - 3) :] |
| if len(last_part) > budget |
| else last_part |
| ) |
| candidate = prefix_marker + tail + status_text |
| if len(candidate) <= limit_chars: |
| return candidate |
|
|
| if dropped: |
| minimal = prefix_marker + status_text.lstrip("\n") |
| if len(minimal) <= limit_chars: |
| return minimal |
| return status or "" |
|
|