feat: Show spinner while waiting for first stream content

This commit is contained in:
Paul Gauthier (aider) 2025-05-08 12:30:06 -07:00
parent 82f33c1220
commit 6e1327f66d

View file

@ -2,6 +2,8 @@
import io
import time
import threading
from aider.utils import Spinner
from rich import box
from rich.console import Console
@ -115,9 +117,20 @@ class MarkdownStream:
else:
self.mdargs = dict()
# Initialize rich Live display with empty text
self.live = Live(Text(""), refresh_per_second=1.0 / self.min_delay)
self.live.start()
# Defer Live creation until the first update so the Spinner can be shown.
self.live = None
self.spinner = Spinner("Streaming markdown...")
self._spinner_stop_event = threading.Event()
self._spinner_thread = threading.Thread(target=self._spin, daemon=True)
self._spinner_thread.start()
self._live_started = False
def _spin(self):
"""Background thread that keeps the spinner moving until stopped."""
while not self._spinner_stop_event.is_set():
time.sleep(0.1)
self.spinner.step()
self.spinner.end()
def _render_markdown_to_lines(self, text):
"""Render markdown text to a list of lines.
@ -146,6 +159,15 @@ class MarkdownStream:
except Exception:
pass # Ignore any errors during cleanup
# Ensure the spinner thread is properly shut down
if hasattr(self, "_spinner_stop_event"):
self._spinner_stop_event.set()
if hasattr(self, "_spinner_thread") and self._spinner_thread.is_alive():
try:
self._spinner_thread.join(timeout=0.1)
except Exception:
pass
def update(self, text, final=False):
"""Update the displayed markdown content.
@ -163,6 +185,16 @@ class MarkdownStream:
Markdown going to the console works better in terminal scrollback buffers.
The live window doesn't play nice with terminal scrollback.
"""
# On the first call, stop the spinner and start the Live renderer
if not getattr(self, "_live_started", False):
if hasattr(self, "_spinner_stop_event"):
self._spinner_stop_event.set()
if hasattr(self, "_spinner_thread"):
self._spinner_thread.join()
self.live = Live(Text(""), refresh_per_second=1.0 / self.min_delay)
self.live.start()
self._live_started = True
now = time.time()
# Throttle updates to maintain smooth rendering
if not final and now - self.when < self.min_delay: