Re-add markdown streaming (#2029)

Wait for newlines, then render markdown on a line by line basis. Word wrap it for the current terminal size and then spit it out line by line into the UI. Also adds tests and fixes some UI regressions.
This commit is contained in:
easong-openai
2025-08-12 17:37:28 -07:00
committed by GitHub
parent 97a27ffc77
commit 6340acd885
42 changed files with 35887 additions and 1026 deletions

View File

@@ -184,7 +184,7 @@ fn pretty_provider_name(id: &str) -> String {
impl HistoryCell {
/// Return a cloned, plain representation of the cell's lines suitable for
/// oneshot insertion into the terminal scrollback. Image cells are
/// represented with a simple placeholder for now.
/// represented with a simple placeholder.
/// These lines are also rendered directly by ratatui wrapped in a Paragraph.
pub(crate) fn plain_lines(&self) -> Vec<Line<'static>> {
match self {
@@ -215,6 +215,16 @@ impl HistoryCell {
}
}
pub(crate) fn new_background_event(message: String) -> Self {
let mut lines: Vec<Line<'static>> = Vec::new();
lines.push(Line::from("event".dim()));
lines.extend(message.lines().map(|line| ansi_escape_line(line).dim()));
lines.push(Line::from(""));
HistoryCell::BackgroundEvent {
view: TextBlock::new(lines),
}
}
pub(crate) fn desired_height(&self, width: u16) -> u16 {
Paragraph::new(Text::from(self.plain_lines()))
.wrap(Wrap { trim: false })
@@ -533,17 +543,6 @@ impl HistoryCell {
view: TextBlock::new(lines),
}
}
// allow dead code for now. maybe we'll use it again.
#[allow(dead_code)]
pub(crate) fn new_background_event(message: String) -> Self {
let mut lines: Vec<Line<'static>> = Vec::new();
lines.push(Line::from("event".dim()));
lines.extend(message.lines().map(|line| ansi_escape_line(line).dim()));
lines.push(Line::from(""));
HistoryCell::BackgroundEvent {
view: TextBlock::new(lines),
}
}
pub(crate) fn new_diff_output(message: String) -> Self {
let mut lines: Vec<Line<'static>> = Vec::new();
@@ -881,6 +880,33 @@ impl HistoryCell {
view: TextBlock::new(lines),
}
}
pub(crate) fn new_patch_apply_success(stdout: String) -> Self {
let mut lines: Vec<Line<'static>> = Vec::new();
// Success title
lines.push(Line::from("✓ Applied patch".magenta().bold()));
if !stdout.trim().is_empty() {
let mut iter = stdout.lines();
for (i, raw) in iter.by_ref().take(TOOL_CALL_MAX_LINES).enumerate() {
let prefix = if i == 0 { "" } else { " " };
let s = format!("{prefix}{raw}");
lines.push(ansi_escape_line(&s).dim());
}
let remaining = iter.count();
if remaining > 0 {
lines.push(Line::from(""));
lines.push(Line::from(format!("... +{remaining} lines")).dim());
}
}
lines.push(Line::from(""));
HistoryCell::PatchApplyResult {
view: TextBlock::new(lines),
}
}
}
impl WidgetRef for &HistoryCell {