Restore transcript scrollbar

This commit is contained in:
Hunter Bown
2026-02-03 19:05:44 -06:00
parent 9f47d78a03
commit 4ded9e2a4f
4 changed files with 89 additions and 6 deletions
+5
View File
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
## [0.3.13] - 2026-02-04
### Fixed
- Restore an in-app scrollbar for the transcript view
## [0.3.12] - 2026-02-04
### Fixed
Generated
+1 -1
View File
@@ -674,7 +674,7 @@ dependencies = [
[[package]]
name = "deepseek-tui"
version = "0.3.12"
version = "0.3.13"
dependencies = [
"anyhow",
"arboard",
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
name = "deepseek-tui"
version = "0.3.12"
version = "0.3.13"
edition = "2024"
description = "Unofficial DeepSeek CLI - Just run 'deepseek' to start chatting"
license = "MIT"
+82 -4
View File
@@ -21,14 +21,19 @@ use unicode_width::UnicodeWidthStr;
pub struct ChatWidget {
content_area: Rect,
scrollbar_area: Option<Rect>,
lines: Vec<Line<'static>>,
total_lines: usize,
visible_lines: usize,
top: usize,
}
impl ChatWidget {
pub fn new(app: &mut App, area: Rect) -> Self {
let content_area = area;
let mut content_area = area;
let visible_lines = content_area.height as usize;
let render_options = app.transcript_render_options();
app.transcript_cache.ensure(
&app.history,
content_area.width.max(1),
@@ -36,8 +41,26 @@ impl ChatWidget {
render_options,
);
let total_lines = app.transcript_cache.total_lines();
let visible_lines = content_area.height as usize;
let mut total_lines = app.transcript_cache.total_lines();
let mut scrollbar_area = None;
if total_lines > visible_lines && content_area.width > 1 {
scrollbar_area = Some(Rect {
x: content_area.x + content_area.width.saturating_sub(1),
y: content_area.y,
width: 1,
height: content_area.height,
});
content_area.width = content_area.width.saturating_sub(1).max(1);
app.transcript_cache.ensure(
&app.history,
content_area.width.max(1),
app.history_version,
render_options,
);
total_lines = app.transcript_cache.total_lines();
}
let line_meta = app.transcript_cache.line_meta();
if app.pending_scroll_delta != 0 {
@@ -75,7 +98,11 @@ impl ChatWidget {
Self {
content_area,
scrollbar_area,
lines,
total_lines,
visible_lines,
top,
}
}
}
@@ -84,6 +111,10 @@ impl Renderable for ChatWidget {
fn render(&self, _area: Rect, buf: &mut Buffer) {
let paragraph = Paragraph::new(self.lines.clone());
paragraph.render(self.content_area, buf);
if let Some(area) = self.scrollbar_area {
render_scrollbar(buf, area, self.total_lines, self.visible_lines, self.top);
}
}
fn desired_height(&self, _width: u16) -> u16 {
@@ -91,6 +122,53 @@ impl Renderable for ChatWidget {
}
}
fn render_scrollbar(
buf: &mut Buffer,
area: Rect,
total_lines: usize,
visible_lines: usize,
top: usize,
) {
if area.width == 0 || area.height == 0 || total_lines == 0 {
return;
}
let height = area.height as usize;
let track_style = Style::default().fg(palette::TEXT_DIM);
let thumb_style = Style::default().fg(palette::DEEPSEEK_SKY);
for row in 0..height {
if let Some(cell) = buf.cell_mut((area.x, area.y + row as u16)) {
cell.set_symbol("").set_style(track_style);
}
}
if total_lines <= visible_lines {
return;
}
let thumb_height = ((visible_lines as f32 / total_lines as f32) * height as f32)
.ceil()
.clamp(1.0, height as f32) as usize;
let max_top = total_lines.saturating_sub(visible_lines);
let thumb_top = if max_top == 0 {
0
} else {
let available = height.saturating_sub(thumb_height);
let ratio = top as f32 / max_top as f32;
(ratio * available as f32).round() as usize
};
for row in thumb_top..thumb_top.saturating_add(thumb_height) {
if row >= height {
break;
}
if let Some(cell) = buf.cell_mut((area.x, area.y + row as u16)) {
cell.set_symbol("").set_style(thumb_style);
}
}
}
pub struct ComposerWidget<'a> {
app: &'a App,
prompt: &'a str,