Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 14 additions & 54 deletions packages/claude-code-plugin/hooks/lib/tiny_actor_preview.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
from typing import Optional

from tiny_actor_card import create_actor_card, TinyActorCard
from tiny_actor_grid import layout_grid
from tiny_actor_presets import get_cast_preset, BUDDY_FACE
from tiny_actor_renderer import render_card

# ---------------------------------------------------------------------------
# Feature flag
Expand All @@ -25,62 +27,10 @@ def is_tiny_actors_enabled() -> bool:


# ---------------------------------------------------------------------------
# Card rendering helpers
# Public API
# ---------------------------------------------------------------------------

_CARD_WIDTH = 14
_SEPARATOR = " "


def _render_card_lines(card: TinyActorCard, width: int) -> list[str]:
"""Render a single actor card as a list of fixed-width lines."""
label = card.label[:width]
face = card.face
lines = [
label.center(width),
face.center(width),
]
if card.quote:
quote = card.quote[:width]
lines.append(quote.center(width))
return lines


def _arrange_cards_horizontal(
cards: list[TinyActorCard],
available_width: int,
) -> str:
"""Arrange actor cards in a horizontal row, wrapping to fit *available_width*."""
if not cards:
return ""

card_w = min(_CARD_WIDTH, available_width)
sep_w = len(_SEPARATOR)
# Cards per row: at least 1
cards_per_row = max(1, (available_width + sep_w) // (card_w + sep_w))

rows_output: list[str] = []
for row_start in range(0, len(cards), cards_per_row):
row_cards = cards[row_start : row_start + cards_per_row]
rendered = [_render_card_lines(c, card_w) for c in row_cards]

# Pad all to same number of lines
max_lines = max(len(r) for r in rendered)
for r in rendered:
while len(r) < max_lines:
r.append(" " * card_w)

# Merge horizontally
for line_idx in range(max_lines):
merged = _SEPARATOR.join(r[line_idx] for r in rendered)
rows_output.append(merged[:available_width])

return "\n".join(rows_output)


# ---------------------------------------------------------------------------
# Public API
# ---------------------------------------------------------------------------


def render_actor_preview(
Expand Down Expand Up @@ -130,7 +80,17 @@ def render_actor_preview(
)
cards.append(card)

return _arrange_cards_horizontal(cards, available_width)
# Render each card through the width-safe renderer
rendered = [render_card(c, card_width=_CARD_WIDTH) for c in cards]

# Assemble via the responsive grid layout
grid_lines = layout_grid(
rendered,
available_width=available_width,
card_width=_CARD_WIDTH,
)

return "\n".join(grid_lines) if grid_lines else None

except Exception:
return None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
if _lib_dir not in sys.path:
sys.path.insert(0, _lib_dir)

from buddy_renderer import display_width
from tiny_actor_preview import is_tiny_actors_enabled, render_actor_preview

# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -100,9 +101,8 @@ def test_respects_available_width(self):
result = render_actor_preview("PLAN", available_width=40)
assert result is not None
for line in result.split("\n"):
# Each line should not exceed available_width
# (stripped of ANSI codes for measurement)
assert len(line) <= 40
# Use display_width for correct measurement of Unicode content
assert display_width(line) <= 40

def test_all_preset_modes_render(self):
for mode in ("PLAN", "EVAL", "AUTO", "SHIP"):
Expand Down
Loading