diff --git a/nebula_app/src/input/mod.rs b/nebula_app/src/input/mod.rs index 1ae0723..4d1e761 100644 --- a/nebula_app/src/input/mod.rs +++ b/nebula_app/src/input/mod.rs @@ -1847,6 +1847,17 @@ impl> Processor { crate::display::ChromeHit::None => {}, } + // A press inside the chrome band (top bar / tab sidebar) that + // hit no control ends here. Falling through used to reach the + // terminal's selection arming, which clamps the point into the + // nearest grid cell — dragging from the sidebar then painted a + // stray selection across the pane (the "drag ghost"). + let window_size = self.ctx.display().size_info; + let scale = self.ctx.window().scale_factor as f32; + if crate::display::in_chrome_bar(&window_size, scale, x, y) { + return; + } + // Nebula: grab the scrollback thumb (or jump on a track press). // Only live while scrolled into history, since the bar auto-hides. let view = self.ctx.size_info(); diff --git a/nebula_app/src/window_context.rs b/nebula_app/src/window_context.rs index 3463c98..6ffafe5 100644 --- a/nebula_app/src/window_context.rs +++ b/nebula_app/src/window_context.rs @@ -1924,7 +1924,12 @@ impl WindowContext { } else { self.display.begin_pane_frame(&self.config); let mut dim_rects = Vec::new(); - for (i, (id, view)) in pane_rects.iter().enumerate() { + // The whole-window clear must not be tied to pane_rects[0]: a + // layout leaf whose pane is gone (or a doc sentinel) is skipped + // below, and skipping the clearing pane would leave every later + // frame compositing over stale buffer contents (ghost frames). + let mut cleared = false; + for (id, view) in pane_rects.iter() { let Some(idx) = self.pane_index(*id) else { continue }; let is_focused = *id == focused; if !is_focused { @@ -1946,8 +1951,9 @@ impl WindowContext { &mut pane.nebula_state, *view, is_focused, - i == 0, + !cleared, ); + cleared = true; } self.display.draw_split_overlays(&dim_rects, ÷r_rects); self.display.finish_pane_frame(scheduler); diff --git a/nebula_app/src/window_context/split.rs b/nebula_app/src/window_context/split.rs index 1edba72..a0041b7 100644 --- a/nebula_app/src/window_context/split.rs +++ b/nebula_app/src/window_context/split.rs @@ -411,6 +411,16 @@ impl WindowContext { /// focus. Works at any depth, so repeated splits nest into a layout tree /// (panes close via the close-pane action). pub(super) fn split_focused(&mut self, direction: SplitDirection) { + // Doc/settings tabs own no real pane: splitting one would graft the + // DOC sentinel leaf into a split tree, whose draw pass then skips it + // and leaves that half of the frame stale. + if self + .tabs + .get(self.active_tab) + .is_some_and(|tab| tab.doc.is_some() || tab.settings) + { + return; + } self.zoom = None; let focused = self.focused_pane_id(); let cwd = self.focused_cwd();