Skip to content

Commit 62b3f8e

Browse files
committed
fix(plugin): make Tiny Actor preview use width-safe renderer and grid pipeline (#1303)
Replace manual _render_card_lines/_arrange_cards_horizontal with the display-width-safe render_card() + layout_grid() pipeline, eliminating naive string slicing and .center() that could break CJK/ANSI alignment. - Import render_card from tiny_actor_renderer and layout_grid from tiny_actor_grid - Remove 52 lines of duplicate rendering logic - Update test to use display_width() instead of len() for width assertion
1 parent 2aac5b5 commit 62b3f8e

2 files changed

Lines changed: 17 additions & 57 deletions

File tree

packages/claude-code-plugin/hooks/lib/tiny_actor_preview.py

Lines changed: 14 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99
from typing import Optional
1010

1111
from tiny_actor_card import create_actor_card, TinyActorCard
12+
from tiny_actor_grid import layout_grid
1213
from tiny_actor_presets import get_cast_preset, BUDDY_FACE
14+
from tiny_actor_renderer import render_card
1315

1416
# ---------------------------------------------------------------------------
1517
# Feature flag
@@ -25,62 +27,10 @@ def is_tiny_actors_enabled() -> bool:
2527

2628

2729
# ---------------------------------------------------------------------------
28-
# Card rendering helpers
30+
# Public API
2931
# ---------------------------------------------------------------------------
3032

3133
_CARD_WIDTH = 14
32-
_SEPARATOR = " "
33-
34-
35-
def _render_card_lines(card: TinyActorCard, width: int) -> list[str]:
36-
"""Render a single actor card as a list of fixed-width lines."""
37-
label = card.label[:width]
38-
face = card.face
39-
lines = [
40-
label.center(width),
41-
face.center(width),
42-
]
43-
if card.quote:
44-
quote = card.quote[:width]
45-
lines.append(quote.center(width))
46-
return lines
47-
48-
49-
def _arrange_cards_horizontal(
50-
cards: list[TinyActorCard],
51-
available_width: int,
52-
) -> str:
53-
"""Arrange actor cards in a horizontal row, wrapping to fit *available_width*."""
54-
if not cards:
55-
return ""
56-
57-
card_w = min(_CARD_WIDTH, available_width)
58-
sep_w = len(_SEPARATOR)
59-
# Cards per row: at least 1
60-
cards_per_row = max(1, (available_width + sep_w) // (card_w + sep_w))
61-
62-
rows_output: list[str] = []
63-
for row_start in range(0, len(cards), cards_per_row):
64-
row_cards = cards[row_start : row_start + cards_per_row]
65-
rendered = [_render_card_lines(c, card_w) for c in row_cards]
66-
67-
# Pad all to same number of lines
68-
max_lines = max(len(r) for r in rendered)
69-
for r in rendered:
70-
while len(r) < max_lines:
71-
r.append(" " * card_w)
72-
73-
# Merge horizontally
74-
for line_idx in range(max_lines):
75-
merged = _SEPARATOR.join(r[line_idx] for r in rendered)
76-
rows_output.append(merged[:available_width])
77-
78-
return "\n".join(rows_output)
79-
80-
81-
# ---------------------------------------------------------------------------
82-
# Public API
83-
# ---------------------------------------------------------------------------
8434

8535

8636
def render_actor_preview(
@@ -130,7 +80,17 @@ def render_actor_preview(
13080
)
13181
cards.append(card)
13282

133-
return _arrange_cards_horizontal(cards, available_width)
83+
# Render each card through the width-safe renderer
84+
rendered = [render_card(c, card_width=_CARD_WIDTH) for c in cards]
85+
86+
# Assemble via the responsive grid layout
87+
grid_lines = layout_grid(
88+
rendered,
89+
available_width=available_width,
90+
card_width=_CARD_WIDTH,
91+
)
92+
93+
return "\n".join(grid_lines) if grid_lines else None
13494

13595
except Exception:
13696
return None

packages/claude-code-plugin/tests/test_tiny_actor_preview.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
if _lib_dir not in sys.path:
1111
sys.path.insert(0, _lib_dir)
1212

13+
from buddy_renderer import display_width
1314
from tiny_actor_preview import is_tiny_actors_enabled, render_actor_preview
1415

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

107107
def test_all_preset_modes_render(self):
108108
for mode in ("PLAN", "EVAL", "AUTO", "SHIP"):

0 commit comments

Comments
 (0)