Merge branch 'agentydragon-21-compact-markdown-rendering' into agentydragon

# Conflicts:
#	agentydragon/tasks/21-compact-markdown-rendering.md
This commit is contained in:
Rai (Michael Pokorny)
2025-06-24 22:27:00 -07:00
3 changed files with 87 additions and 4 deletions

View File

@@ -1,9 +1,9 @@
+++
id = "21"
title = "Compact Markdown Rendering Option"
status = "Not started"
dependencies = "" # No prerequisites
last_updated = "2025-06-25T01:40:09.600000"
status = "Done"
dependencies = "03,06,08,13,15,32,18,19,22,23"
last_updated = "2025-06-24T22:25:59.864216"
+++
## Summary

View File

@@ -90,6 +90,11 @@ pub struct Tui {
#[serde(default)]
pub disable_mouse_capture: bool,
/// When `true`, omit blank lines immediately following Markdown headings
/// (levels 16) in TUI rendering for more compact vertical spacing.
#[serde(default)]
pub markdown_compact: bool,
/// Maximum number of visible lines in the chat input composer before scrolling.
/// The composer will expand up to this many lines; additional content will enable
/// an internal scrollbar.
@@ -114,6 +119,7 @@ impl Default for Tui {
fn default() -> Self {
Self {
disable_mouse_capture: Default::default(),
markdown_compact: Default::default(),
composer_max_rows: default_composer_max_rows(),
editor: default_editor(),
}

View File

@@ -12,7 +12,15 @@ pub(crate) fn append_markdown(
lines: &mut Vec<Line<'static>>,
config: &Config,
) {
append_markdown_with_opener_and_cwd(markdown_source, lines, config.file_opener, &config.cwd);
let mut new_lines = Vec::new();
append_markdown_with_opener_and_cwd(markdown_source, &mut new_lines, config.file_opener, &config.cwd);
if config.tui.markdown_compact {
for line in collapse_heading_blank_lines(new_lines) {
lines.push(line);
}
} else {
lines.extend(new_lines);
}
}
fn append_markdown_with_opener_and_cwd(
@@ -53,6 +61,29 @@ fn append_markdown_with_opener_and_cwd(
}
}
/// Remove blank lines immediately following Markdown headings (levels 16).
fn collapse_heading_blank_lines(lines: Vec<Line<'static>>) -> Vec<Line<'static>> {
let mut result = Vec::with_capacity(lines.len());
let mut prev_was_heading = false;
for line in lines {
let content = line.spans.iter().map(|s| s.content.clone()).collect::<String>();
if prev_was_heading && content.trim().is_empty() {
continue;
}
prev_was_heading = {
let s = content.as_str();
s.starts_with("# ")
|| s.starts_with("## ")
|| s.starts_with("### ")
|| s.starts_with("#### ")
|| s.starts_with("##### ")
|| s.starts_with("###### ")
};
result.push(line);
}
result
}
/// Rewrites file citations in `src` into markdown hyperlinks using the
/// provided `scheme` (`vscode`, `cursor`, etc.). The resulting URI follows the
/// format expected by VS Code-compatible file openers:
@@ -104,6 +135,52 @@ mod tests {
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn collapse_blank_after_heading() {
let lines = vec![
Line::from(vec![Span::raw("# Heading")]),
Line::from(vec![Span::raw("")]),
Line::from(vec![Span::raw("Paragraph")]),
];
let collapsed = collapse_heading_blank_lines(lines);
let rendered: Vec<String> = collapsed
.iter()
.map(|l| l.spans.iter().map(|s| s.content.clone()).collect())
.collect();
assert_eq!(rendered, vec!["# Heading", "Paragraph"]);
}
#[test]
fn preserve_blank_not_after_heading() {
let lines = vec![
Line::from(vec![Span::raw("Normal")]),
Line::from(vec![Span::raw("")]),
Line::from(vec![Span::raw("Paragraph")]),
];
let collapsed = collapse_heading_blank_lines(lines);
let rendered: Vec<String> = collapsed
.iter()
.map(|l| l.spans.iter().map(|s| s.content.clone()).collect())
.collect();
assert_eq!(rendered, vec!["Normal", "", "Paragraph"]);
}
#[test]
fn collapse_multiple_blanks_after_heading() {
let lines = vec![
Line::from(vec![Span::raw("## Heading2")]),
Line::from(vec![Span::raw("")]),
Line::from(vec![Span::raw("")]),
Line::from(vec![Span::raw("Para")]),
];
let collapsed = collapse_heading_blank_lines(lines);
let rendered: Vec<String> = collapsed
.iter()
.map(|l| l.spans.iter().map(|s| s.content.clone()).collect())
.collect();
assert_eq!(rendered, vec!["## Heading2", "Para"]);
}
#[test]
fn citation_is_rewritten_with_absolute_path() {
let markdown = "See 【F:/src/main.rs†L42-L50】 for details.";