Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/terminal/panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,11 @@ impl TerminalPanel {
.pty
.as_ref()
.is_some_and(|pty| pty.should_hide_cursor_for_streaming_output());
if hide_cursor_for_output {
// Schedule repaint so cursor reappears promptly when streaming stops
ui.ctx()
.request_repaint_after(std::time::Duration::from_millis(150));
}

if let Some(pty) = &self.pty {
// Check pending mode reset: if Ctrl+C was sent while in ALT_SCREEN
Expand Down
21 changes: 18 additions & 3 deletions src/terminal/pty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,24 @@ impl PtyHandle {
}

pub fn should_hide_cursor_for_streaming_output(&self) -> bool {
// Cursor is always visible — hiding it during output caused
// the cursor to flicker/disappear while the user was typing.
false
let last_input = self
.last_input_at
.lock()
.map(|t| t.elapsed())
.unwrap_or(Duration::from_secs(999));
let last_output = self
.last_output_at
.lock()
.map(|t| t.elapsed())
.unwrap_or(Duration::from_secs(999));

// Hide cursor when output is actively streaming and the user isn't typing.
// This keeps the cursor clean during AI tool output (Codex, Claude, etc.)
// while ensuring it stays visible when the user is interacting.
let streaming = last_output < Duration::from_millis(100);
let user_idle = last_input > Duration::from_millis(150);

streaming && user_idle
}
}

Expand Down
Loading