|
22 | 22 |
|
23 | 23 | from .. import config |
24 | 24 | from ..diffrender import render_diff |
| 25 | +from ..models import ImagePart |
25 | 26 | from ..prompts import _is_mode_reminder_text |
26 | 27 |
|
27 | 28 | if TYPE_CHECKING: |
@@ -123,6 +124,22 @@ def _is_injected_user_text(text: str) -> bool: |
123 | 124 | return _is_mode_reminder_text(text) |
124 | 125 |
|
125 | 126 |
|
| 127 | +def _image_indicator(msg: Any) -> str: |
| 128 | + """A short ``[📎 N image(s)]`` suffix for user messages with images. |
| 129 | +
|
| 130 | + ``Message.text()`` skips ``ImagePart`` objects, so without this the |
| 131 | + conversation panel gives no visual cue that an image was attached. |
| 132 | + """ |
| 133 | + content = getattr(msg, "content", None) |
| 134 | + if not isinstance(content, list): |
| 135 | + return "" |
| 136 | + count = sum(1 for p in content if isinstance(p, ImagePart)) |
| 137 | + if not count: |
| 138 | + return "" |
| 139 | + noun = "image" if count == 1 else "images" |
| 140 | + return f" [📎 {count} {noun}]" |
| 141 | + |
| 142 | + |
126 | 143 | def _strip_final_check(text: str) -> str: |
127 | 144 | """Drop a completion-check block from an assistant reply. |
128 | 145 |
|
@@ -252,8 +269,9 @@ def _build_history_rows(self, full: bool = False) -> list[Any]: |
252 | 269 | if m.injected or _is_injected_user_text(m.text()): |
253 | 270 | continue |
254 | 271 | body = m.text() if full else _tail_lines(m.text(), 12) |
255 | | - if body.strip(): |
256 | | - rows.append(Markdown(f"**user:** {body}", style=USER_STYLE)) |
| 272 | + indicator = _image_indicator(m) |
| 273 | + if body.strip() or indicator: |
| 274 | + rows.append(Markdown(f"**user:** {body.strip()}{indicator}", style=USER_STYLE)) |
257 | 275 | elif m.role == "assistant": |
258 | 276 | body = m.text() |
259 | 277 | collapsed_reasoning = False |
|
0 commit comments