@@ -579,6 +579,51 @@ describe('renderer.scroll_to_bottom', function()
579579 config .values .ui .output .always_scroll_to_bottom = false
580580 end )
581581
582+ it (' follows visible binary output without converting NUL-containing strings to Blobs' , function ()
583+ local scroll = require (' opencode.ui.renderer.scroll' )
584+ local text = string.rep (' x\0 ' , 15 )
585+ vim .api .nvim_win_set_width (win , 20 )
586+ vim .api .nvim_win_set_height (win , 5 )
587+ vim .api .nvim_set_option_value (' wrap' , true , { win = win })
588+ vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , { ' header' , text })
589+ vim .api .nvim_win_set_cursor (win , { 1 , 0 })
590+
591+ assert .equals (2 , output_window .get_visible_bottom_line (win ))
592+ scroll .scroll_win_to_bottom (win , buf )
593+ assert .same ({ 2 , # text - 1 }, vim .api .nvim_win_get_cursor (win ))
594+
595+ local snapshot = scroll .pre_flush (buf )
596+ assert .is_true (snapshot .follow )
597+ text = text .. string.rep (' y\0 ' , 10 )
598+ vim .api .nvim_buf_set_lines (buf , 1 , 2 , false , { text })
599+ scroll .post_flush (snapshot , buf )
600+
601+ assert .same ({ 2 , # text - 1 }, vim .api .nvim_win_get_cursor (win ))
602+ assert .equals (text , vim .api .nvim_buf_get_lines (buf , 1 , 2 , false )[1 ])
603+ end )
604+
605+ it (' bottom-aligns binary output using the display width of NUL bytes' , function ()
606+ local scroll = require (' opencode.ui.renderer.scroll' )
607+ local lines = {}
608+ for i = 1 , 20 do
609+ lines [i ] = ' line ' .. i
610+ end
611+ lines [19 ] = string.rep (' x\0 ' , 10 )
612+ vim .api .nvim_buf_set_lines (buf , 0 , - 1 , false , lines )
613+ vim .api .nvim_win_set_width (win , 20 )
614+ vim .api .nvim_win_set_height (win , 5 )
615+ vim .api .nvim_set_option_value (' wrap' , true , { win = win })
616+ vim .api .nvim_win_set_cursor (win , { 1 , 0 })
617+
618+ assert .is_true (output_window .get_visible_bottom_line (win ) < 20 )
619+ scroll .scroll_win_to_bottom (win , buf )
620+
621+ assert .equals (17 , output_window .get_visible_top_line (win ))
622+ assert .equals (20 , output_window .get_visible_bottom_line (win ))
623+ assert .equals (20 , vim .api .nvim_win_get_cursor (win )[1 ])
624+ assert .equals (lines [19 ], vim .api .nvim_buf_get_lines (buf , 18 , 19 , false )[1 ])
625+ end )
626+
582627 it (' moves to the visual end when the last wrapped line grows' , function ()
583628 local long_line = string.rep (' x' , 120 )
584629 local longer_line = string.rep (' x' , 180 )
0 commit comments