Skip to content

Commit 13151d1

Browse files
Kasper JungeRalphify
authored andcommitted
refactor: lift shared rendering methods into _LivePanelBase
_IterationPanel and _IterationSpinner had identical _build_title(), __rich_console__(), and footer grid logic. Move these to the base class so subclasses only override what actually differs (token display, subtitle, footer summary text). Co-authored-by: Ralphify <noreply@ralphify.co>
1 parent 0cf7e28 commit 13151d1

1 file changed

Lines changed: 43 additions & 60 deletions

File tree

src/ralphify/_console_emitter.py

Lines changed: 43 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -308,10 +308,33 @@ def set_peek_visible(self, visible: bool) -> None:
308308

309309
# ── Shared rendering ─────────────────────────────────────────────
310310

311+
def _build_title(self) -> Text:
312+
"""Title bar text: elapsed time. Subclasses may override."""
313+
elapsed = time.monotonic() - self._start
314+
title = Text()
315+
title.append(" ⏱ ", style=_brand.PURPLE)
316+
title.append(format_duration(elapsed), style=f"bold {_brand.PURPLE}")
317+
title.append(" ", style="dim")
318+
return title
319+
320+
def _build_subtitle(self) -> Text | None:
321+
"""Subtitle text. Returns ``None`` by default."""
322+
return None
323+
311324
def _build_footer(self) -> Table:
312325
"""Subclasses must override to provide the footer summary row."""
313326
raise NotImplementedError
314327

328+
def _footer_grid(self, summary: Text) -> Table:
329+
"""Three-column footer row: spinner | summary | peek hint."""
330+
hint = Text("Shift+P full screen", style="dim", no_wrap=True)
331+
grid = Table.grid(expand=True)
332+
grid.add_column(width=2, no_wrap=True)
333+
grid.add_column(ratio=1, no_wrap=True, overflow="ellipsis")
334+
grid.add_column(no_wrap=True, justify="right")
335+
grid.add_row(self._spinner, summary, hint)
336+
return grid
337+
315338
def _build_body(self) -> Group:
316339
"""Body group: scroll lines (or peek message) + spacer + footer."""
317340
rows: list[Any] = []
@@ -330,6 +353,21 @@ def _build_body(self) -> Group:
330353
rows.append(self._build_footer())
331354
return Group(*rows)
332355

356+
def __rich_console__(
357+
self, console: Console, options: ConsoleOptions
358+
) -> RenderResult:
359+
panel = Panel(
360+
self._build_body(),
361+
box=box.ROUNDED,
362+
title=self._build_title(),
363+
title_align="left",
364+
subtitle=self._build_subtitle(),
365+
subtitle_align="right",
366+
border_style=_brand.PURPLE,
367+
padding=(0, 2),
368+
)
369+
yield panel
370+
333371

334372
class _IterationPanel(_LivePanelBase):
335373
"""Rich renderable for the live peek panel.
@@ -490,15 +528,12 @@ def _format_categories(self) -> str:
490528

491529
def _build_title(self) -> Text:
492530
"""Title bar text: elapsed time + token usage."""
493-
elapsed = time.monotonic() - self._start
494-
title = Text()
495-
title.append(" ⏱ ", style=_brand.PURPLE)
496-
title.append(format_duration(elapsed), style=f"bold {_brand.PURPLE}")
531+
title = super()._build_title()
497532
tokens = self._format_tokens()
498533
if tokens:
499-
title.append(" ", style="dim")
534+
title.append(" ", style="dim")
500535
title.append(tokens, style=f"bold {_brand.LAVENDER}")
501-
title.append(" ", style="dim")
536+
title.append(" ", style="dim")
502537
return title
503538

504539
def _build_subtitle(self) -> Text | None:
@@ -525,30 +560,7 @@ def _build_footer(self) -> Table:
525560
summary.append(cats, style="dim")
526561
else:
527562
summary.append("waiting for first tool call…", style="dim italic")
528-
529-
hint = Text("Shift+P full screen", style="dim", no_wrap=True)
530-
531-
grid = Table.grid(expand=True)
532-
grid.add_column(width=2, no_wrap=True)
533-
grid.add_column(ratio=1, no_wrap=True, overflow="ellipsis")
534-
grid.add_column(no_wrap=True, justify="right")
535-
grid.add_row(self._spinner, summary, hint)
536-
return grid
537-
538-
def __rich_console__(
539-
self, console: Console, options: ConsoleOptions
540-
) -> RenderResult:
541-
panel = Panel(
542-
self._build_body(),
543-
box=box.ROUNDED,
544-
title=self._build_title(),
545-
title_align="left",
546-
subtitle=self._build_subtitle(),
547-
subtitle_align="right",
548-
border_style=_brand.PURPLE,
549-
padding=(0, 2),
550-
)
551-
yield panel
563+
return self._footer_grid(summary)
552564

553565

554566
# ── Full-screen peek ─────────────────────────────────────────────────
@@ -1164,14 +1176,6 @@ class _IterationSpinner(_LivePanelBase):
11641176
lines vs. structured tool rows).
11651177
"""
11661178

1167-
def _build_title(self) -> Text:
1168-
elapsed = time.monotonic() - self._start
1169-
title = Text()
1170-
title.append(" ⏱ ", style=_brand.PURPLE)
1171-
title.append(format_duration(elapsed), style=f"bold {_brand.PURPLE}")
1172-
title.append(" ", style="dim")
1173-
return title
1174-
11751179
def _build_footer(self) -> Table:
11761180
line_count = len(self._scroll_lines)
11771181
summary = Text(no_wrap=True, overflow="ellipsis")
@@ -1183,25 +1187,4 @@ def _build_footer(self) -> Table:
11831187
summary.append(" of agent output", style="dim")
11841188
else:
11851189
summary.append("waiting for agent output…", style="dim italic")
1186-
1187-
hint = Text("Shift+P full screen", style="dim", no_wrap=True)
1188-
1189-
grid = Table.grid(expand=True)
1190-
grid.add_column(width=2, no_wrap=True)
1191-
grid.add_column(ratio=1, no_wrap=True, overflow="ellipsis")
1192-
grid.add_column(no_wrap=True, justify="right")
1193-
grid.add_row(self._spinner, summary, hint)
1194-
return grid
1195-
1196-
def __rich_console__(
1197-
self, console: Console, options: ConsoleOptions
1198-
) -> RenderResult:
1199-
panel = Panel(
1200-
self._build_body(),
1201-
box=box.ROUNDED,
1202-
title=self._build_title(),
1203-
title_align="left",
1204-
border_style=_brand.PURPLE,
1205-
padding=(0, 2),
1206-
)
1207-
yield panel
1190+
return self._footer_grid(summary)

0 commit comments

Comments
 (0)