11local state = require (' opencode.state' )
22local config = require (' opencode.config' )
3- local formatter = require (' opencode.ui.formatter' )
43local output_window = require (' opencode.ui.output_window' )
54local permission_window = require (' opencode.ui.permission_window' )
65local Promise = require (' opencode.promise' )
76local ctx = require (' opencode.ui.renderer.ctx' )
8- local buf = require (' opencode.ui.renderer.buffer' )
97local events = require (' opencode.ui.renderer.events' )
8+ local flush = require (' opencode.ui.renderer.flush' )
109
1110local M = {}
1211
1312-- Expose event handlers on M so tests can call them directly and subscriptions
1413-- can be stubbed cleanly (e.g. stub(renderer, '_render_full_session_data'))
1514M .on_session_updated = events .on_session_updated
1615
17- local trigger_on_data_rendered = require (' opencode.util' ).debounce (function ()
18- local cb_type = type (config .ui .output .rendering .on_data_rendered )
19- if cb_type == ' boolean' then
20- return
21- end
22- if not state .windows or not state .windows .output_buf or not state .windows .output_win then
23- return
24- end
25- if cb_type == ' function' then
26- pcall (config .ui .output .rendering .on_data_rendered , state .windows .output_buf , state .windows .output_win )
27- elseif vim .fn .exists (' :RenderMarkdown' ) > 0 then
28- vim .cmd (' :RenderMarkdown' )
29- elseif vim .fn .exists (' :Markview' ) > 0 then
30- vim .cmd (' :Markview render ' .. state .windows .output_buf )
31- end
32- end , config .ui .output .rendering .markdown_debounce_ms or 250 )
33-
3416--- Reset all renderer state and clear the output buffer
3517function M .reset ()
3618 ctx :reset ()
@@ -45,7 +27,7 @@ function M.reset()
4527 permission_window .clear_all ()
4628 state .renderer .reset ()
4729
48- trigger_on_data_rendered ()
30+ flush . trigger_on_data_rendered ()
4931end
5032
5133--- Unsubscribe from all events and reset
@@ -152,9 +134,32 @@ function M._render_full_session_data(session_data)
152134 end
153135
154136 if revert_index then
155- buf .write_formatted_data (formatter ._format_revert_message (state .messages , revert_index ))
137+ local revert_message = {
138+ info = {
139+ id = ' __opencode_revert_message__' ,
140+ sessionID = state .active_session .id ,
141+ role = ' system' ,
142+ },
143+ parts = {
144+ {
145+ id = ' __opencode_revert_part__' ,
146+ messageID = ' __opencode_revert_message__' ,
147+ sessionID = state .active_session .id ,
148+ type = ' revert-display' ,
149+ state = {
150+ revert_index = revert_index ,
151+ },
152+ },
153+ },
154+ }
155+
156+ table.insert (state .messages , revert_message )
157+ events .on_message_updated (revert_message )
158+ events .on_part_updated ({ part = revert_message .parts [1 ] })
156159 end
157160
161+ flush .flush ()
162+
158163 if set_mode_from_messages then
159164 set_model_and_mode_from_messages ()
160165 end
@@ -192,6 +197,7 @@ function M.render_output(output_data)
192197 output_window .set_lines (output_data .lines or {})
193198 output_window .clear_extmarks ()
194199 output_window .set_extmarks (output_data .extmarks )
200+ flush .trigger_on_data_rendered ()
195201 M .scroll_to_bottom ()
196202end
197203
@@ -218,15 +224,13 @@ function M.scroll_to_bottom(force)
218224 local prev_line_count = ctx .prev_line_count
219225 ctx .prev_line_count = line_count
220226
221- trigger_on_data_rendered ( )
227+ local ok_cursor , cursor = pcall ( vim . api . nvim_win_get_cursor , output_win )
222228
223229 local should_scroll = force
224230 or prev_line_count == 0
225231 or config .ui .output .always_scroll_to_bottom
226- or (function ()
227- local ok_cursor , cursor = pcall (vim .api .nvim_win_get_cursor , output_win )
228- return ok_cursor and cursor and (cursor [1 ] >= prev_line_count or cursor [1 ] >= line_count )
229- end )()
232+ or (ok_cursor and cursor and cursor [1 ] >= prev_line_count )
233+ or output_window .is_at_bottom (output_win )
230234
231235 if should_scroll then
232236 local last_line = vim .api .nvim_buf_get_lines (output_buf , line_count - 1 , line_count , false )[1 ] or ' '
@@ -242,8 +246,8 @@ function M.on_focus_changed()
242246 if not permission_window .get_all_permissions ()[1 ] then
243247 return
244248 end
245- buf . rerender_part (' permission-display-part' )
246- trigger_on_data_rendered ()
249+ flush . mark_part_dirty (' permission-display-part' , ' permission-display-message ' )
250+ flush . flush ()
247251end
248252
249253--- Re-render when the active session changes
0 commit comments