fix: TUIウィジェットの表示を修正
- diff表示の修正 - historyリストの修正 - markdown表示の修正 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
aa2651465f
commit
6d5b55ad74
3 changed files with 169 additions and 18 deletions
|
|
@ -1,14 +1,70 @@
|
|||
//! Diff viewer widget placeholder.
|
||||
//! Diff viewer widget for displaying file changes.
|
||||
//!
|
||||
//! This will eventually wrap `diff_render.rs` for reuse across overlays.
|
||||
//! Wraps `diff_render.rs` for reuse across overlays with scrolling support.
|
||||
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::Frame;
|
||||
use ratatui::{
|
||||
layout::Rect,
|
||||
widgets::{Block, Borders, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::diff_render::FileDiff;
|
||||
use crate::diff_render::{DiffRender, FileDiff};
|
||||
|
||||
/// Render a diff within the given area.
|
||||
pub fn render_diff_widget(frame: &mut Frame, area: Rect, diff: &FileDiff) {
|
||||
let _ = (frame, area, diff);
|
||||
// TODO: integrate syntax highlighting and line numbers.
|
||||
/// Properties for rendering a diff widget
|
||||
pub struct DiffWidgetProps {
|
||||
/// The file diff to display
|
||||
pub diff: FileDiff,
|
||||
/// Current scroll offset
|
||||
pub scroll: u16,
|
||||
/// Optional title for the block
|
||||
pub title: Option<String>,
|
||||
}
|
||||
|
||||
/// Render a diff within the given area with scrolling support.
|
||||
pub fn render_diff_widget(frame: &mut Frame, area: Rect, diff: &FileDiff) {
|
||||
render_diff_widget_with_scroll(frame, area, diff, 0, None);
|
||||
}
|
||||
|
||||
/// Render a diff with scroll offset and optional title.
|
||||
pub fn render_diff_widget_with_scroll(
|
||||
frame: &mut Frame,
|
||||
area: Rect,
|
||||
diff: &FileDiff,
|
||||
scroll: u16,
|
||||
title: Option<&str>,
|
||||
) {
|
||||
// Create a DiffRender with the single file
|
||||
let mut renderer = DiffRender::new();
|
||||
renderer.files.push(diff.clone());
|
||||
|
||||
// Get rendered lines
|
||||
let lines = renderer.render();
|
||||
|
||||
// Create block with optional title
|
||||
let block = if let Some(t) = title {
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(t.to_string())
|
||||
} else {
|
||||
Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.title(format!("{} → {}", diff.old_path, diff.new_path))
|
||||
};
|
||||
|
||||
// Create paragraph with scroll
|
||||
let paragraph = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: false })
|
||||
.scroll((scroll, 0));
|
||||
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
||||
/// Calculate the total number of lines in a diff for scroll bounds
|
||||
pub fn diff_line_count(diff: &FileDiff) -> usize {
|
||||
let mut count = 1; // File header
|
||||
for hunk in &diff.hunks {
|
||||
count += hunk.lines.len();
|
||||
}
|
||||
count
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,12 +3,15 @@
|
|||
//! Intended to wrap `HistoryCell` rendering with consistent padding and theming.
|
||||
|
||||
use ratatui::{
|
||||
layout::Rect,
|
||||
widgets::{Block, Borders},
|
||||
layout::{Alignment, Rect},
|
||||
style::Style,
|
||||
text::Line,
|
||||
widgets::{Block, Borders, List, ListItem, Paragraph},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::history_cell::HistoryCell;
|
||||
use crate::ui::colors;
|
||||
|
||||
/// Properties required to render the history list.
|
||||
pub struct HistoryListProps<'a> {
|
||||
|
|
@ -21,10 +24,53 @@ pub struct HistoryList;
|
|||
|
||||
impl HistoryList {
|
||||
pub fn render(frame: &mut Frame, area: Rect, props: HistoryListProps<'_>) {
|
||||
let block = Block::default().borders(Borders::ALL);
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(colors::BORDER))
|
||||
.title("History");
|
||||
let inner = block.inner(area);
|
||||
frame.render_widget(block, area);
|
||||
|
||||
let _ = props;
|
||||
// TODO: integrate with virtualized list + markdown rendering.
|
||||
if inner.height == 0 || inner.width == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
if props.items.is_empty() {
|
||||
let empty = Paragraph::new("No history yet")
|
||||
.style(Style::default().fg(colors::COMMENT))
|
||||
.alignment(Alignment::Center);
|
||||
frame.render_widget(empty, inner);
|
||||
return;
|
||||
}
|
||||
|
||||
let scroll = props.scroll as usize;
|
||||
let max_lines = inner.height as usize;
|
||||
let mut visible_lines: Vec<Line> = Vec::with_capacity(max_lines);
|
||||
let mut line_index = 0usize;
|
||||
|
||||
'outer: for (idx, cell) in props.items.iter().enumerate() {
|
||||
let mut rendered = cell.render(inner.width);
|
||||
if idx + 1 < props.items.len() {
|
||||
rendered.push(Line::from(""));
|
||||
}
|
||||
|
||||
for line in rendered {
|
||||
if line_index >= scroll {
|
||||
visible_lines.push(line);
|
||||
if visible_lines.len() >= max_lines {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
line_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
let list_items: Vec<ListItem> = visible_lines
|
||||
.into_iter()
|
||||
.map(ListItem::new)
|
||||
.collect();
|
||||
|
||||
let list = List::new(list_items).style(Style::default().fg(colors::FG));
|
||||
frame.render_widget(list, inner);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,62 @@
|
|||
//! This file is the landing zone for `markdown_stream` integration to keep
|
||||
//! rendering logic out of `history_cell.rs`.
|
||||
|
||||
use ratatui::layout::Rect;
|
||||
use ratatui::Frame;
|
||||
use ratatui::{
|
||||
layout::Rect,
|
||||
style::{Modifier, Style},
|
||||
text::{Line, Span},
|
||||
widgets::{Block, Borders, Paragraph, Wrap},
|
||||
Frame,
|
||||
};
|
||||
|
||||
use crate::markdown_render::MarkdownRenderer;
|
||||
use crate::markdown_stream::MarkdownStream;
|
||||
use crate::ui::colors;
|
||||
|
||||
/// Render a markdown stream into the provided frame area.
|
||||
pub fn render_stream(frame: &mut Frame, area: Rect, stream: &MarkdownStream) {
|
||||
let _ = (frame, area, stream);
|
||||
// TODO: reuse MarkdownRenderer and add code block scroll support.
|
||||
let block = Block::default()
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(colors::BORDER))
|
||||
.title("Markdown");
|
||||
|
||||
let inner_height = area.height.saturating_sub(2) as usize;
|
||||
|
||||
let mut lines = if stream.is_empty() {
|
||||
vec![Line::from(Span::styled(
|
||||
"Waiting for response...",
|
||||
Style::default().fg(colors::COMMENT),
|
||||
))]
|
||||
} else {
|
||||
let renderer = MarkdownRenderer::default();
|
||||
renderer.render(stream.content())
|
||||
};
|
||||
|
||||
if stream.is_streaming() {
|
||||
if let Some(last) = lines.last_mut() {
|
||||
last.spans.push(Span::styled(
|
||||
"▌",
|
||||
Style::default()
|
||||
.fg(colors::FG)
|
||||
.add_modifier(Modifier::SLOW_BLINK),
|
||||
));
|
||||
} else {
|
||||
lines.push(Line::from(Span::styled(
|
||||
"▌",
|
||||
Style::default()
|
||||
.fg(colors::FG)
|
||||
.add_modifier(Modifier::SLOW_BLINK),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
let max_offset = lines.len().saturating_sub(inner_height.max(1));
|
||||
let offset = stream.scroll_offset().min(max_offset);
|
||||
|
||||
let paragraph = Paragraph::new(lines)
|
||||
.block(block)
|
||||
.wrap(Wrap { trim: false })
|
||||
.scroll((offset as u16, 0));
|
||||
|
||||
frame.render_widget(paragraph, area);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue