Skip to content

🎨 Palette: Add persistent CLI status indicator#63

Open
Whamp wants to merge 1 commit intomainfrom
palette-persistent-status-11651727359360095606
Open

🎨 Palette: Add persistent CLI status indicator#63
Whamp wants to merge 1 commit intomainfrom
palette-persistent-status-11651727359360095606

Conversation

@Whamp
Copy link
Owner

@Whamp Whamp commented Feb 5, 2026

User description

Adds a persistent visual status indicator to the CLI using rich. The app now displays "Ready", "Recording...", or "Transcribing..." with spinners to provide better feedback on its internal state. Includes comprehensive unit tests.


PR created automatically by Jules for task 11651727359360095606 started by @Whamp


PR Type

Enhancement, Tests


Description

  • Add persistent CLI status indicator using rich.console.Status

  • Display "Ready", "Recording...", "Transcribing..." states with spinners

  • Wrap keyboard.wait() in status context for continuous feedback

  • Add comprehensive unit tests for status lifecycle transitions


Diagram Walkthrough

flowchart LR
  Init["Initialize Status<br/>Ready"] --> Run["Run with<br/>Status Context"]
  Run --> Record["Start Recording<br/>Recording..."]
  Record --> Stop["Stop Recording<br/>Transcribing..."]
  Stop --> Transcribe["Transcribe Audio"]
  Transcribe --> Ready["Reset to Ready"]
Loading

File Walkthrough

Relevant files
Enhancement
main.py
Integrate persistent status indicator throughout app lifecycle

src/chirp/main.py

  • Store console instance and create persistent status_indicator in
    __init__
  • Wrap keyboard.wait() in status context manager for continuous display
  • Update status to "Recording..." when audio capture starts
  • Update status to "Transcribing..." when recording stops
  • Reset status to "Ready" after transcription completes or fails
  • Refactor _transcribe_and_inject() with try-finally block for status
    reset
+28/-18 
Tests
test_ui_status.py
Add unit tests for UI status indicator lifecycle                 

tests/test_ui_status.py

  • Create new test file with comprehensive unit tests for status
    indicator
  • Mock all low-level dependencies (sounddevice, keyboard, winsound)
    before imports
  • Test status lifecycle: initialization, recording, transcribing, and
    ready states
  • Verify status context manager wraps keyboard.wait() correctly
  • Validate status updates are called with correct messages and spinners
+97/-0   
Documentation
palette.md
Document persistent status implementation learnings           

.jules/palette.md

  • Document learning about rich.console.Status context managers
  • Record best practice for wrapping blocking calls with persistent
    status
  • Note thread-safety of status updates from worker threads
+3/-0     

- Add `rich.console.Status` to `ChirpApp` for visual feedback.
- Show "Ready" when idle, "Recording..." during capture, and "Transcribing..." during processing.
- Ensure status is reset to "Ready" after transcription completes or fails.
- Add unit tests in `tests/test_ui_status.py` to verify lifecycle.

This improves user experience by providing clear visual feedback on the application's current state in the terminal.

Co-authored-by: Whamp <1115485+Whamp@users.noreply.github.com>
@google-labs-jules
Copy link
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@qodo-code-review
Copy link

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
Sensitive data in logs

Description: The new debug logging emits the full transcription text (self.logger.debug("Transcription:
%s", text)), which may contain sensitive user speech and can be exposed via logs (e.g., CI
logs, local log files, or log aggregation systems) if debug logging is enabled.
main.py [168-174]

Referred Code
duration = time.perf_counter() - start_time
self.logger.debug("Transcription finished in %.2fs (chars=%s)", duration, len(text))
if not text.strip():
    self.logger.info("Transcription empty; skipping paste")
    return
self.logger.debug("Transcription: %s", text)
self.text_injector.inject(text)
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
🟢
Generic: Comprehensive Audit Trails

Objective: To create a detailed and reliable record of critical system actions for security analysis
and compliance.

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Meaningful Naming and Self-Documenting Code

Objective: Ensure all identifiers clearly express their purpose and intent, making code
self-documenting

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Robust Error Handling and Edge Case Management

Objective: Ensure comprehensive error handling that provides meaningful context and graceful
degradation

Status: Passed

Learn more about managing compliance generic rules or creating your own custom rules

🔴
Generic: Secure Logging Practices

Objective: To ensure logs are useful for debugging and auditing without exposing sensitive
information like PII, PHI, or cardholder data.

Status:
Sensitive data logged: The PR adds logging of full transcribed text via self.logger.debug("Transcription:
%s", text), which can capture and persist sensitive user speech content in logs.

Referred Code
self.logger.debug("Transcription finished in %.2fs (chars=%s)", duration, len(text))
if not text.strip():
    self.logger.info("Transcription empty; skipping paste")
    return
self.logger.debug("Transcription: %s", text)
self.text_injector.inject(text)

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Secure Error Handling

Objective: To prevent the leakage of sensitive system information through error messages while
providing sufficient detail for internal debugging.

Status:
Stacktrace exposure risk: The new logger.exception("Transcription failed: %s", exc) may emit stack traces
to end-users depending on logger/RichHandler configuration, which cannot be verified from
this diff.

Referred Code
    text = self.parakeet.transcribe(waveform, sample_rate=16_000, language=self.config.language)
except Exception as exc:
    self.logger.exception("Transcription failed: %s", exc)
    self.audio_feedback.play_error(self.config.error_sound_path)
    return

Learn more about managing compliance generic rules or creating your own custom rules

Generic: Security-First Input Validation and Data Handling

Objective: Ensure all data inputs are validated, sanitized, and handled securely to prevent
vulnerabilities

Status:
Unsanitized text handling: The transcribed text is injected via self.text_injector.inject(text) without any visible
sanitization/validation, and whether this is safe depends on the downstream injector and
target context (not shown in the diff).

Referred Code
if not text.strip():
    self.logger.info("Transcription empty; skipping paste")
    return
self.logger.debug("Transcription: %s", text)
self.text_injector.inject(text)

Learn more about managing compliance generic rules or creating your own custom rules

Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

@qodo-code-review
Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
Possible issue
Fix a race condition in status updates

To fix a race condition in the status indicator, introduce a counter and a lock
to track active transcription tasks. Only reset the status to "Ready" when all
transcriptions are complete and no recording is active.

src/chirp/main.py [156-178]

 class ChirpApp:
     def __init__(self, *, verbose: bool = False) -> None:
         ...
+        self._active_transcriptions = 0
+        self._transcription_lock = threading.Lock()
 ...
     def _stop_recording(self) -> None:
         if self._stop_timer:
             self._stop_timer.cancel()
             self._stop_timer = None
 
         self.logger.debug("Stopping audio capture")
         waveform = self.audio_capture.stop()
         self._recording = False
         self.status_indicator.update("Transcribing...", spinner="dots")
         self.audio_feedback.play_stop(self.config.stop_sound_path)
         self.logger.info("Recording stopped (%s samples)", waveform.size)
+        with self._transcription_lock:
+            self._active_transcriptions += 1
         self._executor.submit(self._transcribe_and_inject, waveform)
 
     def _transcribe_and_inject(self, waveform) -> None:
         try:
             ...
         finally:
-            if not self._recording:
-                self.status_indicator.update("Ready", spinner="dots")
+            with self._transcription_lock:
+                self._active_transcriptions -= 1
+                if not self._recording and self._active_transcriptions == 0:
+                    self.status_indicator.update("Ready", spinner="dots")

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a race condition in the UI status update logic and proposes a robust solution using a lock and a counter, which significantly improves the application's reliability under rapid use.

Medium
  • More

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant