diff --git a/src/user/chat.rs b/src/user/chat.rs index 8c1e8c3..451c10e 100644 --- a/src/user/chat.rs +++ b/src/user/chat.rs @@ -456,6 +456,43 @@ impl InteractScreen { } } + fn push_routed(&mut self, node: &AstNode) { + for (target, text, marker) in Self::route_node(node) { + match target { + PaneTarget::Conversation => { + self.conversation.current_color = Color::Cyan; + self.conversation.append_text(&text); + self.conversation.pending_marker = marker; + self.conversation.flush_pending(); + }, + PaneTarget::ConversationAssistant => { + self.conversation.current_color = Color::Reset; + self.conversation.append_text(&text); + self.conversation.pending_marker = marker; + self.conversation.flush_pending(); + }, + PaneTarget::Tools => + self.tools.push_line(text, Color::Yellow), + PaneTarget::ToolResult => { + for line in text.lines().take(20) { + self.tools.push_line(format!(" {}", line), Color::DarkGray); + } + } + } + } + } + + fn pop_routed(&mut self, node: &AstNode) { + for (target, _, _) in Self::route_node(node) { + match target { + PaneTarget::Conversation | PaneTarget::ConversationAssistant + => self.conversation.pop_line(), + PaneTarget::Tools | PaneTarget::ToolResult + => self.tools.pop_line(), + } + } + } + fn sync_from_agent(&mut self) { for _ in 0..self.pending_display_count { self.conversation.pop_line(); @@ -476,38 +513,37 @@ impl InteractScreen { (generation, ctx.conversation().to_vec()) }; - if generation != self.last_generation || entries.len() < self.last_entries.len() { + // Full reset on generation change + if generation != self.last_generation { self.conversation = PaneState::new(true); self.autonomous = PaneState::new(true); self.tools = PaneState::new(false); self.last_entries.clear(); } - let start = self.last_entries.len(); - for node in entries.iter().skip(start) { - for (target, text, marker) in Self::route_node(node) { - match target { - PaneTarget::Conversation => { - self.conversation.current_color = Color::Cyan; - self.conversation.append_text(&text); - self.conversation.pending_marker = marker; - self.conversation.flush_pending(); - }, - PaneTarget::ConversationAssistant => { - self.conversation.current_color = Color::Reset; - self.conversation.append_text(&text); - self.conversation.pending_marker = marker; - self.conversation.flush_pending(); - }, - PaneTarget::Tools => - self.tools.push_line(text, Color::Yellow), - PaneTarget::ToolResult => { - for line in text.lines().take(20) { - self.tools.push_line(format!(" {}", line), Color::DarkGray); - } - } - } + // Detect changed entries (streaming updates mutate the last entry) + // Walk backwards from the end, pop any that differ + let mut pop_from = self.last_entries.len(); + for i in (0..self.last_entries.len()).rev() { + if i >= entries.len() { + pop_from = i; + continue; } + // Compare token count as a cheap change detector + if self.last_entries[i].tokens() != entries[i].tokens() { + pop_from = i; + } else { + break; // entries before this haven't changed + } + } + while self.last_entries.len() > pop_from { + let popped = self.last_entries.pop().unwrap(); + self.pop_routed(&popped); + } + + // Push new/changed entries + for node in entries.iter().skip(self.last_entries.len()) { + self.push_routed(node); self.last_entries.push(node.clone()); }