diff --git a/honcho-cli/src/honcho_tui/widgets/peer_panel.py b/honcho-cli/src/honcho_tui/widgets/peer_panel.py index 395ea7d1..ddddf968 100644 --- a/honcho-cli/src/honcho_tui/widgets/peer_panel.py +++ b/honcho-cli/src/honcho_tui/widgets/peer_panel.py @@ -2,6 +2,7 @@ from __future__ import annotations +from rich.markup import escape as markup_escape from textual.app import ComposeResult from textual.widget import Widget from textual.widgets import Static @@ -86,11 +87,11 @@ class PeerPanel(Widget): scroll.mount(Static(f"[{ACCENT}]CARD[/{ACCENT}]", classes="section-label")) if card and card.strip(): - # Wrap long lines, show first 400 chars - preview = card.strip()[:400] - if len(card.strip()) > 400: - preview += f"\n[{FG_MUTED}]… {len(card.strip()) - 400} more chars[/{FG_MUTED}]" - scroll.mount(Static(preview, classes="card-body")) + raw = card.strip() + preview = markup_escape(raw[:400]) + if len(raw) > 400: + preview += f"\n[{FG_MUTED}]… {len(raw) - 400} more chars[/{FG_MUTED}]" + scroll.mount(Static(preview, classes="card-body", markup=True)) else: scroll.mount(Static(f"[{FG_DIM}]no card yet[/{FG_DIM}]", classes="card-body")) @@ -103,11 +104,14 @@ class PeerPanel(Widget): else: for i, c in enumerate(conclusions[:12]): content = c.get("content", "") - # Truncate to fit panel width - preview = content[:55] + "…" if len(content) > 55 else content + safe = markup_escape(content[:55] + "…" if len(content) > 55 else content) prefix = TREE_LAST if i == min(len(conclusions), 12) - 1 else TREE_MID scroll.mount( - Static(f"[{FG_DIM}]{prefix}[/{FG_DIM}][{FG}]{preview}[/{FG}]", classes="conclusion-row") + Static( + f"[{FG_DIM}]{prefix}[/{FG_DIM}][{FG}]{safe}[/{FG}]", + classes="conclusion-row", + markup=True, + ) ) if count > 12: scroll.mount(Static(f"[{FG_MUTED}] … {count - 12} more[/{FG_MUTED}]")) diff --git a/honcho-cli/src/honcho_tui/widgets/transcript.py b/honcho-cli/src/honcho_tui/widgets/transcript.py index 34cb22c0..76633ab5 100644 --- a/honcho-cli/src/honcho_tui/widgets/transcript.py +++ b/honcho-cli/src/honcho_tui/widgets/transcript.py @@ -4,6 +4,7 @@ from __future__ import annotations import datetime +from rich.markup import escape as markup_escape from rich.text import Text from textual.app import ComposeResult from textual.message import Message @@ -72,21 +73,26 @@ class TranscriptPanel(Widget): if self._streaming: self._update_stream_cursor() - def _update_stream_cursor(self) -> None: - log = self.query_one("#log", RichLog) - cursor_char = CURSOR if self._cursor_on else " " - # Cursor line is updated via the streaming append path - _ = cursor_char # referenced during actual stream writes - # ── Public API ──────────────────────────────────────────────────────────── def show_welcome(self) -> None: log = self.query_one("#log", RichLog) log.clear() t = Text() - t.append(" HONCHO\n", style=f"bold {ACCENT}") + t.append("\n HONCHO\n", style=f"bold {ACCENT}") t.append(" memory that reasons\n\n", style=FG_MUTED) - t.append(f" [{FG_MUTED}]select a session or query the dialectic[/{FG_MUTED}]\n") + t.append(" select a session from the left panel\n", style=FG_DIM) + t.append(" or query the dialectic below\n\n", style=FG_DIM) + t.append(" bindings\n", style=f"bold {FG_DIM}") + binds = [ + ("ctrl+d", "focus dialectic input"), + ("tab", "focus session list"), + ("r", "refresh"), + ("q", "quit"), + ] + for key, desc in binds: + t.append(f" {key}", style=f"bold {ACCENT}") + t.append(f" {desc}\n", style=FG_MUTED) log.write(t) def show_no_workspace(self) -> None: @@ -153,10 +159,12 @@ class TranscriptPanel(Widget): header.append("\n") log.write(header) - # Content + # Content — escape raw text so Rich markup chars don't break rendering body = Text() for line in content.splitlines(): - body.append(f" {line}\n", style=FG) + body.append(" ", style=FG) + body.append(line, style=FG) + body.append("\n") if not content.strip(): body.append(" (empty)\n", style=FG_MUTED) log.write(body) @@ -176,7 +184,9 @@ class TranscriptPanel(Widget): resp = Text() resp.append(" dialectic\n", style=f"bold {GREEN}") for line in content.splitlines(): - resp.append(f" {line}\n", style=FG) + resp.append(" ", style=FG) + resp.append(line, style=FG) + resp.append("\n") resp.append("\n") log.write(resp)