Shrink agent terminals on clear (#62504)

Makes agent panel a bit more ergonomic: on terminal reset, user clear
(demonstrated on videos), etc. shrink its size down.

Before:


https://github.com/user-attachments/assets/ce722f39-0ac7-4b86-9ff6-ecd8e327cc43

After:


https://github.com/user-attachments/assets/f1825c9c-e52e-4d08-ac95-1403a6b0b1ea


Release Notes:

- Improved terminal behavior in agent panel on clear
This commit is contained in:
Kirill Bulatov authored and GitHub committed 2026-08-12 07:11:15 +00:00
1 parent 315ea37410
commit 1c9cbd3b24
3 files changed
+77 -3

No files matched your search

+13
View File
@@ -806,6 +806,19 @@ pub(super) fn shrink_to_used(term: &mut Term<ZedListener>) {
term.grid_mut().truncate();
}
pub(super) fn used_lines(term: &Term<ZedListener>) -> usize {
if term.mode().contains(TermMode::ALT_SCREEN) {
return term.total_lines();
}
let grid = term.grid();
let cursor_line = grid.cursor.point.line.0.max(0) as usize;
let last_occupied_line = (cursor_line + 1..term.screen_lines())
.rev()
.find(|&line| !grid[Line(line as i32)].is_clear())
.unwrap_or(cursor_line);
term.history_size() + last_occupied_line + 1
}
pub(super) fn make_content(term: &Term<ZedListener>, last_content: &Content) -> Content {
let content = term.renderable_content();
+6 -1
View File
@@ -70,7 +70,8 @@ use crate::alacritty::{
scroll_display, scroll_to_point, search_matches, selection_text, set_default_cursor_style,
set_selection as set_term_selection, shrink_to_used, spawn_event_loop,
toggle_vi_mode as toggle_term_vi_mode, total_lines, update_selection as update_term_selection,
update_selection_to_vi_cursor, update_vi_cursor_for_scroll, vi_goto_point, vi_motion,
update_selection_to_vi_cursor, update_vi_cursor_for_scroll, used_lines, vi_goto_point,
vi_motion,
};
use crate::mappings::colors::to_vte_rgb;
use crate::mappings::keys::to_esc_str;
@@ -1891,6 +1892,10 @@ impl Terminal {
screen_lines(&self.term.lock_unfair())
}
pub fn used_lines(&self) -> usize {
used_lines(&self.term.lock_unfair())
}
//To test:
//- Activate match on terminal (scrolling and selection)
//- Editor search snapping behavior
+58 -2
View File
@@ -346,12 +346,13 @@ impl TerminalView {
TerminalMode::Embedded {
max_lines_when_unfocused,
} => {
let total_lines = self.terminal.read(cx).total_lines();
let terminal = self.terminal.read(cx);
let total_lines = terminal.total_lines();
if total_lines > Self::MAX_EMBEDDED_LINES {
ContentMode::Scrollable
} else {
let mut displayed_lines = total_lines;
let mut displayed_lines = terminal.used_lines().min(total_lines);
if !self.focus_handle.is_focused(window)
&& let Some(max_lines) = max_lines_when_unfocused
@@ -3168,6 +3169,61 @@ mod tests {
}
}
#[gpui::test]
async fn test_inline_terminal_shrinks_after_clear(cx: &mut TestAppContext) {
let (project, workspace) = init_test(cx).await;
let terminal = cx.new(|cx| {
terminal::TerminalBuilder::new_display_only(
CursorShape::default(),
terminal::terminal_settings::AlternateScroll::On,
None,
0,
cx.background_executor(),
PathStyle::local(),
)
.subscribe(cx)
});
let (terminal_view, cx) = cx.add_window_view(|window, cx| {
let mut terminal_view = TerminalView::new(
terminal.clone(),
workspace.downgrade(),
None,
project.downgrade(),
window,
cx,
);
terminal_view.set_embedded_mode(None, cx);
terminal_view
});
for _ in 1..=20 {
terminal.update(cx, |terminal, cx| {
terminal.write_output(b"line\n", cx);
});
cx.draw(
gpui::Point::default(),
gpui::size(px(400.), px(100.)),
|_, _| terminal_view.clone().into_any_element(),
);
}
terminal.read_with(cx, |terminal, _| {
assert_eq!(terminal.total_lines(), 21);
});
terminal.update(cx, |terminal, _| terminal.clear());
for _ in 1..=2 {
cx.draw(
gpui::Point::default(),
gpui::size(px(400.), px(100.)),
|_, _| terminal_view.clone().into_any_element(),
);
}
terminal.read_with(cx, |terminal, _| {
assert_eq!(terminal.total_lines(), 1);
assert_eq!(terminal.viewport_lines(), 1);
});
}
#[gpui::test]
async fn test_tab_content_shows_terminal_title_when_custom_title_directly_set_empty(
cx: &mut TestAppContext,