feat: Add mouse capture functionality to terminal and enhance markdown rendering with table support

This commit is contained in:
asepharyana
2026-07-15 04:36:00 +07:00
parent b2e848d124
commit 4428e8bc01
3 changed files with 82 additions and 4 deletions
+5
View File
@@ -119,6 +119,7 @@ fn run_single_process() -> Result<()> {
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
execute!(stdout, crossterm::event::EnableBracketedPaste)?;
execute!(stdout, crossterm::event::EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
terminal.clear()?;
@@ -127,6 +128,7 @@ fn run_single_process() -> Result<()> {
let mut restore_stdout = io::stdout();
let _ = execute!(restore_stdout, crossterm::event::DisableBracketedPaste);
let _ = execute!(restore_stdout, crossterm::event::DisableMouseCapture);
let _ = execute!(restore_stdout, LeaveAlternateScreen);
let _ = disable_raw_mode();
@@ -500,6 +502,7 @@ fn run_attach(session_id: &str) -> Result<()> {
let mut stdout = io::stdout();
execute!(stdout, EnterAlternateScreen)?;
execute!(stdout, crossterm::event::EnableBracketedPaste)?;
execute!(stdout, crossterm::event::EnableMouseCapture)?;
let backend = CrosstermBackend::new(stdout);
let mut terminal = Terminal::new(backend)?;
terminal.clear()?;
@@ -591,6 +594,7 @@ fn run_attach(session_id: &str) -> Result<()> {
}
let _ = execute!(io::stdout(), crossterm::event::DisableBracketedPaste);
let _ = execute!(io::stdout(), crossterm::event::DisableMouseCapture);
let _ = execute!(io::stdout(), LeaveAlternateScreen);
let _ = disable_raw_mode();
@@ -617,6 +621,7 @@ fn run_loop(
let _ = disable_raw_mode();
let _ = execute!(io::stdout(), crossterm::event::DisableBracketedPaste);
let _ = execute!(io::stdout(), crossterm::event::DisableMouseCapture);
let _ = execute!(io::stdout(), LeaveAlternateScreen);
}
result
+75 -4
View File
@@ -30,11 +30,19 @@ use super::theme::Theme;
#[allow(clippy::too_many_lines)]
pub fn render_markdown(text: &str, width: u16) -> Vec<Span<'static>> {
let mut spans = Vec::new();
let parser = pulldown_cmark::Parser::new(text);
let mut options = pulldown_cmark::Options::empty();
options.insert(pulldown_cmark::Options::ENABLE_TABLES);
let parser = pulldown_cmark::Parser::new_ext(text, options);
let mut in_code_block = false;
let mut in_heading = false;
let mut heading_level = 0;
let mut in_table = false;
let mut in_table_cell = false;
let mut table_rows: Vec<Vec<Vec<Span<'static>>>> = Vec::new();
let mut current_row: Vec<Vec<Span<'static>>> = Vec::new();
let mut current_cell: Vec<Span<'static>> = Vec::new();
for event in parser {
match event {
pulldown_cmark::Event::Start(tag) => {
@@ -90,6 +98,17 @@ pub fn render_markdown(text: &str, width: u16) -> Vec<Span<'static>> {
Style::default().fg(Theme::BLOCKQUOTE_BAR),
));
}
pulldown_cmark::Tag::Table(_) => {
in_table = true;
table_rows.clear();
}
pulldown_cmark::Tag::TableHead | pulldown_cmark::Tag::TableRow => {
current_row.clear();
}
pulldown_cmark::Tag::TableCell => {
in_table_cell = true;
current_cell.clear();
}
_ => {}
}
}
@@ -114,6 +133,52 @@ pub fn render_markdown(text: &str, width: u16) -> Vec<Span<'static>> {
pulldown_cmark::TagEnd::Item | pulldown_cmark::TagEnd::BlockQuote(_) => {
spans.push(Span::raw("\n"));
}
pulldown_cmark::TagEnd::TableCell => {
in_table_cell = false;
current_row.push(std::mem::take(&mut current_cell));
}
pulldown_cmark::TagEnd::TableHead | pulldown_cmark::TagEnd::TableRow => {
table_rows.push(std::mem::take(&mut current_row));
}
pulldown_cmark::TagEnd::Table => {
in_table = false;
let mut col_widths = Vec::new();
for row in &table_rows {
for (i, cell) in row.iter().enumerate() {
let width: usize = cell.iter().map(|s| s.content.chars().count()).sum();
if i >= col_widths.len() {
col_widths.push(width);
} else if width > col_widths[i] {
col_widths[i] = width;
}
}
}
spans.push(Span::raw("\n"));
for (r, row) in table_rows.iter().enumerate() {
spans.push(Span::styled(" | ", Style::default().fg(Theme::BORDER)));
for (i, cell) in row.iter().enumerate() {
let width: usize = cell.iter().map(|s| s.content.chars().count()).sum();
let pad = col_widths.get(i).copied().unwrap_or(0).saturating_sub(width);
for span in cell {
spans.push(span.clone());
}
spans.push(Span::raw(" ".repeat(pad)));
spans.push(Span::styled(" | ", Style::default().fg(Theme::BORDER)));
}
spans.push(Span::raw("\n"));
if r == 0 {
spans.push(Span::styled(" |", Style::default().fg(Theme::BORDER)));
for width in &col_widths {
spans.push(Span::styled(format!("{}-|", "-".repeat(*width + 2)), Style::default().fg(Theme::BORDER)));
}
spans.push(Span::raw("\n"));
}
}
spans.push(Span::raw("\n"));
}
_ => {}
}
}
@@ -136,19 +201,25 @@ pub fn render_markdown(text: &str, width: u16) -> Vec<Span<'static>> {
s,
Style::default().fg(color).add_modifier(Modifier::BOLD),
));
} else if in_table_cell {
current_cell.push(Span::raw(s));
} else {
spans.push(Span::raw(s));
}
}
pulldown_cmark::Event::Code(text) => {
// Inline code with background
spans.push(Span::styled(
let span = Span::styled(
format!(" {text} "),
Style::default()
.fg(Theme::ACCENT_TEAL)
.bg(Theme::CODE_BAR)
.add_modifier(Modifier::BOLD),
));
);
if in_table_cell {
current_cell.push(span);
} else {
spans.push(span);
}
}
pulldown_cmark::Event::SoftBreak => {
spans.push(Span::raw(" "));
+2
View File
@@ -0,0 +1,2 @@
use crossterm::event::{EnableMouseCapture, EnableMouseScrollCapture};
fn main() {}