From e17118e4c9fa1d81cde8e1ec2db5856af32ef46a Mon Sep 17 00:00:00 2001 From: ProofOfConcept Date: Sat, 11 Apr 2026 01:42:49 -0400 Subject: [PATCH] Convert SectionTree and all remaining callers to ScrollPane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SectionTree.scroll is now a ScrollPaneState. All callers of render_scrollable replaced with ScrollPane::render_stateful_widget. Deleted render_scrollable and its imports — no hand-rolled scroll rendering remains outside of scroll_pane.rs. Co-Authored-By: Kent Overstreet --- src/user/context.rs | 5 ++-- src/user/subconscious.rs | 12 ++++++---- src/user/widgets.rs | 49 ++++++++-------------------------------- 3 files changed, 19 insertions(+), 47 deletions(-) diff --git a/src/user/context.rs b/src/user/context.rs index f860514..c4d96fa 100644 --- a/src/user/context.rs +++ b/src/user/context.rs @@ -6,7 +6,7 @@ use ratatui::{ }; use super::{App, ScreenView, screen_legend}; -use super::widgets::{SectionTree, SectionView, section_to_view, pane_block, render_scrollable, tree_legend}; +use super::widgets::{SectionTree, SectionView, section_to_view, pane_block, tree_legend}; use crate::agent::context::{AstNode, NodeBody, Ast}; pub(crate) struct ConsciousScreen { @@ -177,6 +177,7 @@ impl ScreenView for ConsciousScreen { .title_top(Line::from(screen_legend()).left_aligned()) .title_bottom(tree_legend()); - render_scrollable(frame, area, lines, block, self.tree.scroll); + let widget = super::scroll_pane::ScrollPane::new(&lines).block(block); + frame.render_stateful_widget(widget, area, &mut self.tree.scroll); } } diff --git a/src/user/subconscious.rs b/src/user/subconscious.rs index 3fade9a..214adea 100644 --- a/src/user/subconscious.rs +++ b/src/user/subconscious.rs @@ -15,7 +15,7 @@ use ratatui::{ }; use super::{App, ScreenView, screen_legend}; -use super::widgets::{SectionTree, SectionView, section_to_view, pane_block_focused, render_scrollable, tree_legend, format_age, format_ts_age}; +use super::widgets::{SectionTree, SectionView, section_to_view, pane_block_focused, tree_legend, format_age, format_ts_age}; #[derive(Clone, Copy, PartialEq)] enum Pane { Agents, Outputs, History, Context } @@ -282,7 +282,7 @@ impl SubconsciousScreen { frame.render_stateful_widget(list, area, &mut self.list_state); } - fn draw_outputs(&self, frame: &mut Frame, area: Rect, app: &App) { + fn draw_outputs(&mut self, frame: &mut Frame, area: Rect, app: &App) { let sections = self.output_sections(app); let mut lines: Vec = Vec::new(); @@ -297,7 +297,8 @@ impl SubconsciousScreen { let mut block = pane_block_focused("state", self.focus == Pane::Outputs); if self.focus == Pane::Outputs { block = block.title_bottom(tree_legend()); } - render_scrollable(frame, area, lines, block, self.output_tree.scroll); + let widget = super::scroll_pane::ScrollPane::new(&lines).block(block); + frame.render_stateful_widget(widget, area, &mut self.output_tree.scroll); } fn draw_history(&mut self, frame: &mut Frame, area: Rect, app: &App) { @@ -351,7 +352,7 @@ impl SubconsciousScreen { } fn draw_context( - &self, + &mut self, frame: &mut Frame, area: Rect, sections: &[SectionView], @@ -374,6 +375,7 @@ impl SubconsciousScreen { let mut block = pane_block_focused(title, self.focus == Pane::Context); if self.focus == Pane::Context { block = block.title_bottom(tree_legend()); } - render_scrollable(frame, area, lines, block, self.context_tree.scroll); + let widget = super::scroll_pane::ScrollPane::new(&lines).block(block); + frame.render_stateful_widget(widget, area, &mut self.context_tree.scroll); } } diff --git a/src/user/widgets.rs b/src/user/widgets.rs index 876fbdc..82a0f05 100644 --- a/src/user/widgets.rs +++ b/src/user/widgets.rs @@ -1,11 +1,9 @@ // widgets.rs — Shared TUI helpers and reusable components use ratatui::{ - layout::{Margin, Rect}, style::{Color, Modifier, Style}, text::Line, - widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState, Wrap}, - Frame, + widgets::{Block, Borders}, crossterm::event::KeyCode, }; use crate::agent::context::{AstNode, Ast}; @@ -102,35 +100,6 @@ pub fn tree_legend() -> Line<'static> { ) } -/// Render a scrollable paragraph with a vertical scrollbar. -/// -/// Legacy wrapper — callers that manage their own `u16` scroll offset -/// use this. For new code, prefer ScrollPane + ScrollPaneState. -pub fn render_scrollable( - frame: &mut Frame, - area: Rect, - lines: Vec>, - block: Block<'_>, - scroll: u16, -) { - let content_len = lines.len(); - let para = Paragraph::new(lines) - .block(block) - .wrap(Wrap { trim: false }) - .scroll((scroll, 0)); - frame.render_widget(para, area); - - let visible = area.height.saturating_sub(2) as usize; - if content_len > visible { - let mut sb_state = ScrollbarState::new(content_len) - .position(scroll as usize); - frame.render_stateful_widget( - Scrollbar::new(ScrollbarOrientation::VerticalRight), - area.inner(Margin { vertical: 1, horizontal: 0 }), - &mut sb_state, - ); - } -} // --------------------------------------------------------------------------- // SectionTree — expand/collapse tree renderer for ContextSection @@ -139,12 +108,12 @@ pub fn render_scrollable( pub struct SectionTree { pub selected: Option, pub expanded: std::collections::HashSet, - pub scroll: u16, + pub scroll: super::scroll_pane::ScrollPaneState, } impl SectionTree { pub fn new() -> Self { - Self { selected: None, expanded: std::collections::HashSet::new(), scroll: 0 } + Self { selected: None, expanded: std::collections::HashSet::new(), scroll: super::scroll_pane::ScrollPaneState::new() } } fn total_nodes(&self, sections: &[SectionView]) -> usize { @@ -184,14 +153,14 @@ impl SectionTree { KeyCode::PageUp => { let sel = self.selected.unwrap_or(0); self.selected = Some(sel.saturating_sub(page)); - self.scroll = self.scroll.saturating_sub(page as u16); + self.scroll.scroll_up(page as u16); return; } KeyCode::PageDown => { let max = item_count.saturating_sub(1); let sel = self.selected.map_or(0, |s| (s + page).min(max)); self.selected = Some(sel); - self.scroll += page as u16; + self.scroll.scroll_down(page as u16); return; } KeyCode::Home => { @@ -228,10 +197,10 @@ impl SectionTree { if let Some(sel) = self.selected { let sel_line = sel as u16; let visible = height.saturating_sub(2); - if sel_line < self.scroll { - self.scroll = sel_line; - } else if sel_line >= self.scroll + visible { - self.scroll = sel_line.saturating_sub(visible.saturating_sub(1)); + if sel_line < self.scroll.offset { + self.scroll.offset = sel_line; + } else if sel_line >= self.scroll.offset + visible { + self.scroll.offset = sel_line.saturating_sub(visible.saturating_sub(1)); } } }