Fix TUI console response display

- Add thread-safe invalidation for cross-task updates
- Fix response handler to properly route to TUI output
- Add debug output if console hook fails

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
dullfig 2026-01-11 14:30:36 -08:00
parent 6a5c047d1e
commit ad507cd54a
2 changed files with 24 additions and 7 deletions

View file

@ -253,8 +253,7 @@ class TUIConsole:
self._print_simple(text, style) self._print_simple(text, style)
else: else:
self.output.append(text, style) self.output.append(text, style)
if self.app and self.app.is_running: self._invalidate()
self.app.invalidate()
def print_raw(self, text: str, style: str = "output"): def print_raw(self, text: str, style: str = "output"):
"""Print without timestamp.""" """Print without timestamp."""
@ -262,7 +261,19 @@ class TUIConsole:
self._print_simple(text, style) self._print_simple(text, style)
else: else:
self.output.append_raw(text, style) self.output.append_raw(text, style)
if self.app and self.app.is_running: self._invalidate()
def _invalidate(self):
"""Invalidate the app to trigger redraw (thread-safe)."""
if self.app and self.app.is_running:
# Use call_soon_threadsafe for cross-task updates
try:
loop = self.app.loop
if loop and loop.is_running():
loop.call_soon_threadsafe(self.app.invalidate)
else:
self.app.invalidate()
except Exception:
self.app.invalidate() self.app.invalidate()
def _print_simple(self, text: str, style: str = "output"): def _print_simple(self, text: str, style: str = "output"):

View file

@ -126,15 +126,21 @@ async def handle_response_print(payload: ShoutedResponse, metadata: HandlerMetad
Routes output to the TUI console if available, otherwise prints to stdout. Routes output to the TUI console if available, otherwise prints to stdout.
""" """
console = None
try: try:
from run_organism import get_console from run_organism import get_console
console = get_console() console = get_console()
if console and hasattr(console, 'on_response'):
console.on_response("response", payload)
return None
except ImportError: except ImportError:
pass pass
# Fallback: print to stdout if console and hasattr(console, 'on_response'):
try:
console.on_response("shouter", payload)
return # Success - don't fall through to print
except Exception as e:
# Debug: show error but continue to fallback
print(f"\n\033[31m[console error] {e}\033[0m")
# Fallback: print to stdout (only if console not available or failed)
print(f"\n\033[36m[response] {payload.message}\033[0m") print(f"\n\033[36m[response] {payload.message}\033[0m")
return None return None