From 6e1327f66d99a06fc58d8185f5eb9b6e4b5e02ec Mon Sep 17 00:00:00 2001 From: "Paul Gauthier (aider)" Date: Thu, 8 May 2025 12:30:06 -0700 Subject: [PATCH] feat: Show spinner while waiting for first stream content --- aider/mdstream.py | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) diff --git a/aider/mdstream.py b/aider/mdstream.py index 24c14f0d4..c24d97928 100755 --- a/aider/mdstream.py +++ b/aider/mdstream.py @@ -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: