@@ -11,7 +11,7 @@ use crate::preview::mermaid::{ImageProtocol, ImageSupport, MermaidRenderState};
1111use ratatui:: layout:: Rect ;
1212use ratatui:: style:: { Color , Modifier , Style } ;
1313use ratatui:: text:: { Line , Span } ;
14- use ratatui:: widgets:: Paragraph ;
14+ use ratatui:: widgets:: { Paragraph , Wrap } ;
1515use ratatui:: Frame ;
1616use std:: io:: Write ;
1717
@@ -117,7 +117,9 @@ pub fn render_preview(app: &App, frame: &mut Frame, area: Rect) -> Vec<PendingIm
117117
118118 match segment {
119119 Segment :: Text ( lines) => {
120- let para = Paragraph :: new ( lines. clone ( ) ) . scroll ( ( clip_top, 0 ) ) ;
120+ let para = Paragraph :: new ( lines. clone ( ) )
121+ . wrap ( Wrap { trim : false } )
122+ . scroll ( ( clip_top, 0 ) ) ;
121123 frame. render_widget ( para, seg_area) ;
122124 }
123125 Segment :: Image { ref path } => {
@@ -177,20 +179,36 @@ pub fn flush_images(
177179 let _ = stdout. flush ( ) ;
178180}
179181
180- /// Clear any stale inline images by forcing a full terminal redraw .
182+ /// Clear any stale inline images by overwriting all cells .
181183/// Call when previous frame had images but current frame does not.
184+ ///
185+ /// `terminal.clear()` alone is insufficient: it resets ratatui's buffer and
186+ /// queues `\x1b[2J`, but ratatui's diff algorithm skips cells that are empty
187+ /// in both the old and new buffers. Image pixels in those cells persist.
188+ /// We explicitly write spaces to every cell to guarantee overwrite.
182189pub fn clear_stale_images (
183190 protocol : ImageProtocol ,
184191 terminal : & mut ratatui:: Terminal < ratatui:: backend:: CrosstermBackend < std:: io:: Stdout > > ,
185192) {
186- // For Kitty: explicitly delete all placement images
193+ let mut stdout = std:: io:: stdout ( ) ;
194+
195+ // For Kitty: explicitly delete all image placements
187196 if protocol == ImageProtocol :: Kitty {
188- let mut stdout = std:: io:: stdout ( ) ;
189- let _ = write ! ( stdout, "\x1b _Ga=d;\x1b \\ " ) ;
190- let _ = stdout. flush ( ) ;
197+ let _ = write ! ( stdout, "\x1b _Ga=d,d=a;\x1b \\ " ) ;
198+ }
199+
200+ // Write spaces to every cell to overwrite lingering image pixels.
201+ // Inline images (iTerm2 OSC 1337, Kitty) bypass ratatui's buffer,
202+ // so we must physically overwrite the cells they occupied.
203+ if let Ok ( size) = terminal. size ( ) {
204+ let blank_line = " " . repeat ( size. width as usize ) ;
205+ for row in 0 ..size. height {
206+ let _ = write ! ( stdout, "\x1b [{};1H{blank_line}" , row + 1 ) ;
207+ }
191208 }
192- // Force ratatui to redraw every cell on the next frame,
193- // which overwrites any leftover image pixels.
209+ let _ = stdout. flush ( ) ;
210+
211+ // Reset ratatui's buffer state so the next draw rewrites all content.
194212 let _ = terminal. clear ( ) ;
195213}
196214
@@ -248,7 +266,23 @@ enum Segment {
248266impl Segment {
249267 fn height ( & self , pane_width : u16 ) -> u16 {
250268 match self {
251- Segment :: Text ( lines) => lines. len ( ) as u16 ,
269+ Segment :: Text ( lines) => {
270+ if pane_width == 0 {
271+ return lines. len ( ) as u16 ;
272+ }
273+ let w = pane_width as usize ;
274+ lines
275+ . iter ( )
276+ . map ( |line| {
277+ let char_width: usize = line. spans . iter ( ) . map ( |s| s. content . len ( ) ) . sum ( ) ;
278+ if char_width == 0 {
279+ 1
280+ } else {
281+ char_width. div_ceil ( w)
282+ }
283+ } )
284+ . sum :: < usize > ( ) as u16
285+ }
252286 Segment :: Image { ref path } => estimate_image_height ( path, pane_width) ,
253287 }
254288 }
0 commit comments