|
13 | 13 | from rich.console import Console |
14 | 14 | from rich.markdown import Markdown |
15 | 15 | from termflow.ansi.codes import BOLD_ON, DIM_ON, RESET |
| 16 | +from termflow.ansi.color import fg_color |
| 17 | +from termflow.render.style import RenderStyle |
16 | 18 | from termflow.tui import MenuBuilder, MenuItem |
17 | 19 | from termflow.tui.menu import MenuResult |
18 | 20 |
|
|
22 | 24 | iter_alphabet_bindings, |
23 | 25 | ) |
24 | 26 | from code_puppy.command_line.menu_session import menu_session |
25 | | -from code_puppy.command_line.tui_style import themed |
| 27 | +from code_puppy.command_line.tui_style import menu_style, themed |
26 | 28 | from code_puppy.config import AUTOSAVE_DIR |
27 | 29 | from code_puppy.session_storage import compute_scope_key, list_sessions, load_session |
28 | 30 | from code_puppy.tools.command_runner import set_awaiting_user_input |
@@ -116,50 +118,81 @@ def _markdown(text: str, width: int = 72) -> str: |
116 | 118 | def _render_message_browser_panel( |
117 | 119 | history: list, message_idx: int, session_name: str |
118 | 120 | ) -> list: |
| 121 | + lines = [("class:tui.header", "MESSAGE BROWSER"), ("", "\n\n")] |
119 | 122 | if not history: |
120 | | - return [ |
121 | | - ("class:tui.warning", "MESSAGE BROWSER\n\nNo messages in this session.") |
122 | | - ] |
| 123 | + return lines + [("class:tui.error", "No messages in this session.")] |
123 | 124 | message_idx = max(0, min(message_idx, len(history) - 1)) |
124 | 125 | role, content = _extract_message_content(history[-1 - message_idx]) |
125 | 126 | rendered = content if role == "tool" else _markdown(content) |
126 | | - return [ |
127 | | - ( |
128 | | - "class:tui.header", |
129 | | - f"MESSAGE BROWSER\n\nSession: {session_name}\nMessage {message_idx + 1} of {len(history)}\n\n{role.upper()}\n{'─' * 40}\n{rendered}\n\nUp older Down newer Esc exit", |
130 | | - ) |
| 127 | + role_style = "class:tui.user" if role == "user" else "class:tui.title" |
| 128 | + return lines + [ |
| 129 | + ("class:tui.label", "Session: "), |
| 130 | + ("class:tui.header", session_name), |
| 131 | + ("", "\n"), |
| 132 | + ("class:tui.label", "Message: "), |
| 133 | + ("", f"{message_idx + 1} of {len(history)}\n\n"), |
| 134 | + (role_style, role.upper()), |
| 135 | + ("", "\n"), |
| 136 | + ("class:tui.divider", "─" * 40), |
| 137 | + ("", f"\n{rendered}\n\n"), |
| 138 | + ("class:tui.hint", "Up older Down newer Esc exit"), |
131 | 139 | ] |
132 | 140 |
|
133 | 141 |
|
134 | 142 | def _render_preview_panel(base_dir: Path, entry: Optional[Tuple[str, dict]]) -> list: |
135 | 143 | if not entry: |
136 | | - return [("class:tui.warning", "PREVIEW\n\nNo session selected.")] |
| 144 | + return [ |
| 145 | + ("class:tui.header", "PREVIEW"), |
| 146 | + ("class:tui.error", "\n\nNo session selected."), |
| 147 | + ] |
137 | 148 | name, metadata = entry |
138 | 149 | timestamp = metadata.get("timestamp", "unknown") |
139 | 150 | try: |
140 | 151 | timestamp = datetime.fromisoformat(timestamp).strftime("%Y-%m-%d %H:%M:%S") |
141 | 152 | except (TypeError, ValueError): |
142 | 153 | pass |
| 154 | + error = None |
143 | 155 | try: |
144 | 156 | message = _markdown( |
145 | 157 | _extract_last_user_message(load_session(name, base_dir)), 76 |
146 | 158 | ) |
147 | 159 | except Exception as exc: |
148 | | - message = f"Error loading preview: {exc}" |
149 | | - text = f"PREVIEW\n\nSession: {name}\nSaved: {timestamp}\nMessages: {metadata.get('message_count', 0)} • Tokens: {metadata.get('total_tokens', 0):,}\n\nLast Message:\n(press 'e' to browse full history)\n{message}" |
150 | | - return [("class:tui.muted", text)] |
| 160 | + message = "" |
| 161 | + error = f"Error loading preview: {exc}" |
| 162 | + lines = [ |
| 163 | + ("class:tui.header", "PREVIEW"), |
| 164 | + ("", "\n\n"), |
| 165 | + ("class:tui.label", "Session: "), |
| 166 | + ("", f"{name}\n"), |
| 167 | + ("class:tui.label", "Saved: "), |
| 168 | + ("", f"{timestamp}\n"), |
| 169 | + ("class:tui.label", "Messages: "), |
| 170 | + ("", str(metadata.get("message_count", 0))), |
| 171 | + ("class:tui.label", " Tokens: "), |
| 172 | + ("", f"{metadata.get('total_tokens', 0):,}\n\n"), |
| 173 | + ("class:tui.title", "Last Message:"), |
| 174 | + ("class:tui.hint", "\n(press 'e' to browse full history)\n"), |
| 175 | + ] |
| 176 | + lines.append(("class:tui.error" if error else "", error or message)) |
| 177 | + return lines |
151 | 178 |
|
152 | 179 |
|
153 | 180 | def _fragments_to_ansi(fragments: list) -> str: |
154 | | - styles = { |
155 | | - "class:tui.header": BOLD_ON, |
156 | | - "class:tui.title": BOLD_ON, |
157 | | - "class:tui.label": BOLD_ON, |
158 | | - "class:tui.muted": DIM_ON, |
| 181 | + """Color semantic preview fragments using the current terminal theme.""" |
| 182 | + style = menu_style() or RenderStyle.default() |
| 183 | + sgr = { |
| 184 | + "class:tui.header": fg_color(style.bright) + BOLD_ON, |
| 185 | + "class:tui.label": fg_color(style.symbol), |
| 186 | + "class:tui.title": fg_color(style.head) + BOLD_ON, |
| 187 | + "class:tui.user": fg_color(style.symbol) + BOLD_ON, |
| 188 | + "class:tui.hint": fg_color(style.grey) + DIM_ON, |
| 189 | + "class:tui.divider": fg_color(style.grey), |
| 190 | + "class:tui.muted": fg_color(style.grey) + DIM_ON, |
| 191 | + "class:tui.error": fg_color(style.error), |
159 | 192 | } |
160 | 193 | return "".join( |
161 | | - f"{styles.get(style, '')}{text}{RESET if style else ''}" |
162 | | - for style, text in fragments |
| 194 | + f"{sgr.get(fragment_style, '')}{text}{RESET if fragment_style else ''}" |
| 195 | + for fragment_style, text in fragments |
163 | 196 | ) |
164 | 197 |
|
165 | 198 |
|
|
0 commit comments