bug: Missing newlines before paragraphs and code blocks in streamed Markdown rendering
Valutazione
- Difficoltà
- 2/5
- Tempo stimato
- 1-3 ore
- Idoneità per principianti
- 78/100
Direzione di ricerca
Inizia in rust/crates/rusty-claude-cli/src/render.rs, in TerminalRenderer::render_markdown e MarkdownStreamState::push, quindi confronta il percorso di streaming con flush. Preserva i ritorni a capo dei paragrafi e dei blocchi di codice per i chunk trasmessi, mantenendo invariato il comportamento di trimming dell'output finale.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Describe the bug
When streaming responses from an OpenAI-compatible API (such as Grok or custom proxy gateways), paragraphs and code blocks sometimes lose their leading and trailing newlines, causing them to merge directly into the previous output line without proper line breaks in the terminal.
For example, a response might render as:
最简单的Python入门例子:Hello World!1. 打开记事本 (instead of having double newlines before 1.)
And:
粘贴进去(全部内容就是它):╭─ python (instead of having double newlines before the code block)
Root Cause Analysis
The bug is located in TerminalRenderer::render_markdown inside rust/crates/rusty-claude-cli/src/render.rs:
#[must_use]
pub fn render_markdown(&self, markdown: &str) -> String {
let normalized = normalize_nested_fences(markdown);
let mut output = String::new();
// ...
for event in Parser::new_ext(&normalized, Options::all()) {
self.render_event(...);
}
output.trim_end().to_string() // <--- HERE
}
During streaming, MarkdownStreamState::push parses intermediate chunks (split at boundaries using find_stream_safe_boundary).
- The
readychunk (e.g., up to an empty line representing a paragraph break) correctly ends with trailing newlines (e.g.,\n\ngenerated by paragraph boundaries). - However,
renderer.markdown_to_ansi(&ready)callsrender_markdown(), which unconditionally performs.trim_end()on the rendered string. - This strips all trailing newlines (including the structural
\n\nrepresenting paragraph/block separators). - Since these newlines are stripped from the output chunk and the original
readytext has been drained fromself.pending, the newlines are completely lost from the terminal print. - When the next chunk (e.g. list items or code block starts) is printed, it is outputted directly appended to the previous text without any line breaks.
Suggested Fix
Introduce a stream-friendly rendering method in TerminalRenderer that preserves trailing whitespace/newlines during stream chunk generation, and use it inside MarkdownStreamState::push.
For example:
- In
TerminalRenderer(insiderust/crates/rusty-claude-cli/src/render.rs):
#[must_use]
pub fn render_markdown_stream(&self, markdown: &str) -> String {
let normalized = normalize_nested_fences(markdown);
let mut output = String::new();
let mut state = RenderState::default();
let mut code_language = String::new();
let mut code_buffer = String::new();
let mut in_code_block = false;
for event in Parser::new_ext(&normalized, Options::all()) {
self.render_event(
event,
&mut state,
&mut output,
&mut code_buffer,
&mut code_language,
&mut in_code_block,
);
}
output // Do not call trim_end() here
}
#[must_use]
pub fn markdown_to_ansi_stream(&self, markdown: &str) -> String {
self.render_markdown_stream(markdown)
}
- In
MarkdownStreamState::push:
#[must_use]
pub fn push(&mut self, renderer: &TerminalRenderer, delta: &str) -> Option<String> {
self.pending.push_str(delta);
let split = find_stream_safe_boundary(&self.pending)?;
let ready = self.pending[..split].to_string();
self.pending.drain(..split);
Some(renderer.markdown_to_ansi_stream(&ready)) // Use the stream-specific method
}
This keeps MarkdownStreamState::flush using the standard markdown_to_ansi (which trims trailing newlines at the very end of the stream), while correctly outputting structural paragraph and code block newlines during streaming.
- Lingua principale
- Rust
- Stelle
- 195k
- Fork
- 108k
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di ultraworkers/claw-code
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
ultraworkers/claw-code#3259 · 7 commenti ·
-
fix(cli): resolve duplicate assistant text printing after 'Done' in non-compact text modeForse già presa @nankingjing l’ha presa 87 giorni fa. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
ultraworkers/claw-code#3258 · 1 commento ·
-
[CRITICAL][SECURITY] Untrusted project hooks bypass read-only mode for arbitrary command executionAperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 68/100
ultraworkers/claw-code#3301 · 4 commenti ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 20/100
ultraworkers/claw-code#3300 · 2 commenti ·
-
Pin all third-party GitHub Actions to immutable commit SHAs across CI workflowsForse di nuovo libera Una pull request per questa issue è stata chiusa senza essere unita. Aperta
Difficoltà 3/5 1-2 giorni Idoneità per principianti 72/100
ultraworkers/claw-code#3287 · 6 commenti · 1 reazione ·
Tutte le issue di ultraworkers/claw-code
Issue simili
-
agent/sec-check hive/hive-school-tunaos security
Difficoltà 2/5 1-3 ore Idoneità per principianti 90/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 90/100
Sovereign-Labs/sovereign-sdk#3070 ·
-
Accepts Invalid URLApertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 66/100
I maintainer di solito rispondono entro 1 giorno
-
area:prove bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
Chelis-Lang/chelis#3317 ·
I maintainer di solito rispondono entro 1 giorno
-
[macOS Desktop] New sidebar hover navigation accidentally switches sections while reaching a chatApertaapp bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 70/100
I maintainer di solito rispondono entro 1 giorno