resizable viewport (#1732)

Proof of concept for a resizable viewport.

The general approach here is to duplicate the `Terminal` struct from
ratatui, but with our own logic. This is a "light fork" in that we are
still using all the base ratatui functions (`Buffer`, `Widget` and so
on), but we're doing our own bookkeeping at the top level to determine
where to draw everything.

This approach could use improvement—e.g, when the window is resized to a
smaller size, if the UI wraps, we don't correctly clear out the
artifacts from wrapping. This is possible with a little work (i.e.
tracking what parts of our UI would have been wrapped), but this
behavior is at least at par with the existing behavior.


https://github.com/user-attachments/assets/4eb17689-09fd-4daa-8315-c7ebc654986d


cc @joshka who might have Thoughts™
This commit is contained in:
Jeremy Rose
2025-07-30 17:06:55 -07:00
committed by GitHub
parent 221ebfcccc
commit f2134f6633
11 changed files with 668 additions and 23 deletions

View File

@@ -71,6 +71,15 @@ impl ChatComposer<'_> {
this
}
pub fn desired_height(&self) -> u16 {
2 + self.textarea.lines().len() as u16
+ match &self.active_popup {
ActivePopup::None => 0u16,
ActivePopup::Command(c) => c.calculate_required_height(),
ActivePopup::File(c) => c.calculate_required_height(),
}
}
/// Returns true if the composer currently contains no user input.
pub(crate) fn is_empty(&self) -> bool {
self.textarea.is_empty()
@@ -651,7 +660,7 @@ impl WidgetRef for &ChatComposer<'_> {
fn render_ref(&self, area: Rect, buf: &mut Buffer) {
match &self.active_popup {
ActivePopup::Command(popup) => {
let popup_height = popup.calculate_required_height(&area);
let popup_height = popup.calculate_required_height();
// Split the provided rect so that the popup is rendered at the
// *top* and the textarea occupies the remaining space below.
@@ -673,7 +682,7 @@ impl WidgetRef for &ChatComposer<'_> {
self.textarea.render(textarea_rect, buf);
}
ActivePopup::File(popup) => {
let popup_height = popup.calculate_required_height(&area);
let popup_height = popup.calculate_required_height();
let popup_rect = Rect {
x: area.x,