Restore transcript scrollbar
This commit is contained in:
@@ -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
@@ -674,7 +674,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "deepseek-tui"
|
||||
version = "0.3.12"
|
||||
version = "0.3.13"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arboard",
|
||||
|
||||
+1
-1
@@ -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
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user