fix: keep explorer selections readable on light terminals
This commit is contained in:
@@ -131,6 +131,7 @@ Diff LSP integration checks reuse the real framed-protocol subprocess with test-
|
||||
|
||||
Changed-file explorer checks cover reuse of the compact directory tree, removed/historical paths, file-to-directory transitions, remapped tree/selection bindings, filter/open/focus transitions, right-dock geometry, and wheel routing without edits to the underlying document.
|
||||
Visibility checks preserve browsing and pane state across Ctrl+e, exclude hidden explorers from focus/wheel routing, and verify full-width review. Status checks preserve catalog associations through sorting, compaction, duplicate inputs, and file/directory collisions, and cover colored A/M/D/R markers, selected/unfocused rows, Tree/List filtering, and sanitized rename paths.
|
||||
Light-terminal explorer selection checks render focused/unfocused rows through theme changes, require readable filename/status/directory contrast on Pumpkin Light, and retain dark/unknown-terminal fallbacks. A live PTY check answers native OSC color queries, opens the explorer with Space e, and observes the pale truecolor selection without changing source bytes.
|
||||
Diff styling checks cover independent snapshot language/shebang/injection detection, accepted/stale snapshot identity, real-parser cache freshness/reuse/release, syntax foreground over changed backgrounds, real empty rows versus missing counterparts, scrolling-stable diagonal hatching, and tab/wide/combining/control-character clipping. TestBackend omits wide-glyph continuation cells; terminal smoke verifies their actual styling.
|
||||
|
||||
## Performance and process limits
|
||||
|
||||
@@ -297,6 +297,9 @@ Pane-focus bindings also work while the ordinary file explorer or picker has foc
|
||||
|
||||
Floating windows overlay the entire editor, above all workspace panes. File pickers, revision selectors, hover documentation, and completion lists are not clipped to the focused pane; completion remains anchored to its caret. Docked exploration reserves space at the editor edge before the remaining workspace is split into panes. The changed-file explorer follows the same layout, on the editor's right edge.
|
||||
|
||||
On light terminals that answer color queries, selected explorer rows use a pale shade of the terminal background, preserving filename, directory and repository-status foregrounds when focused or unfocused. Dark terminals and unavailable color reports retain the ANSI blue/dark-gray selection backgrounds. Theme changes update visible and parked panes.
|
||||
|
||||
|
||||
Diff comparisons reuse the split geometry and directional-focus engine. Option/Alt navigation moves between Before, After, and Files first, then to a neighboring workspace pane when there is no internal target in that direction. Splitting a diff text view creates an ordinary sibling showing the same buffer; historical snapshots remain read-only, including after the comparison closes.
|
||||
|
||||
## Tabs
|
||||
|
||||
+22
-5
@@ -484,9 +484,22 @@ impl BufferViewports {
|
||||
}
|
||||
self.theme = theme;
|
||||
let colors = DiffColors::new(theme);
|
||||
let file_selection_background = theme.and_then(|theme| {
|
||||
let [r, g, b] = theme.background.map(u32::from);
|
||||
if r * 299 + g * 587 + b * 114 < 128_000 {
|
||||
return None;
|
||||
}
|
||||
// Keep ANSI status/directory foregrounds readable on light terminals.
|
||||
let [r, g, b] = theme
|
||||
.background
|
||||
.map(|c| ((u16::from(c) * 95 + 50) / 100) as u8);
|
||||
Some(Color::Rgb(r, g, b))
|
||||
});
|
||||
self.panels.diff_colors = colors;
|
||||
self.panels.file_selection_background = file_selection_background;
|
||||
for pane in self.parked.values_mut() {
|
||||
pane.panels.diff_colors = colors;
|
||||
pane.panels.file_selection_background = file_selection_background;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,6 +555,7 @@ impl BufferViewports {
|
||||
})()
|
||||
.unwrap_or_default();
|
||||
inherited.panels.diff_colors = self.panels.diff_colors;
|
||||
inherited.panels.file_selection_background = self.panels.file_selection_background;
|
||||
self.parked.insert(id, inherited);
|
||||
}
|
||||
let outgoing = self
|
||||
@@ -5699,6 +5713,7 @@ struct WheelPanels {
|
||||
picker_scroll: PickerScroll,
|
||||
diff: Option<DiffGeometry>,
|
||||
diff_colors: DiffColors,
|
||||
file_selection_background: Option<Color>,
|
||||
diff_syntax: Option<DiffSyntaxCache>,
|
||||
previous_diff: Option<DiffGeometry>,
|
||||
diff_wrap: Option<DiffWrapViewport>,
|
||||
@@ -6380,11 +6395,13 @@ fn draw_file_list(
|
||||
};
|
||||
if selected {
|
||||
style = style
|
||||
.bg(if picker.focused {
|
||||
Color::Blue
|
||||
} else {
|
||||
Color::DarkGray
|
||||
})
|
||||
.bg(panels
|
||||
.file_selection_background
|
||||
.unwrap_or(if picker.focused {
|
||||
Color::Blue
|
||||
} else {
|
||||
Color::DarkGray
|
||||
}))
|
||||
.add_modifier(Modifier::BOLD);
|
||||
}
|
||||
let changed = entry
|
||||
|
||||
@@ -32,6 +32,74 @@ impl Drop for Directory {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn explorer_selection_follows_light_background_and_restores_dark_styles() {
|
||||
let directory = Directory::new();
|
||||
directory.file("arguments.hon", "return 0\n");
|
||||
let mut picker =
|
||||
FilePicker::from_paths(directory.0.clone(), [(0, PathBuf::from("arguments.hon"))]);
|
||||
let light = ThemeColors {
|
||||
background: [255, 246, 230],
|
||||
red: [177, 52, 37],
|
||||
green: [82, 101, 31],
|
||||
};
|
||||
let dark = ThemeColors {
|
||||
background: [31, 23, 20],
|
||||
red: [224, 120, 100],
|
||||
green: [169, 182, 101],
|
||||
};
|
||||
let mut views = BufferViewports::default();
|
||||
let mut terminal = Terminal::new(ratatui::backend::TestBackend::new(70, 12)).unwrap();
|
||||
for theme in [Some(light), Some(dark), None, Some(light)] {
|
||||
views.set_theme(theme);
|
||||
for focused in [false, true] {
|
||||
picker.focused = focused;
|
||||
terminal
|
||||
.draw(|frame| {
|
||||
draw_file_list(frame, &picker, frame.area(), "", &mut views.panels, None);
|
||||
})
|
||||
.unwrap();
|
||||
let buffer = terminal.backend().buffer();
|
||||
let cell = buffer
|
||||
.content
|
||||
.iter()
|
||||
.find(|cell| cell.symbol() == "a" && cell.modifier.contains(Modifier::BOLD))
|
||||
.expect("selected arguments.hon row");
|
||||
if theme == Some(light) {
|
||||
let Color::Rgb(r, g, b) = cell.bg else {
|
||||
panic!("light selection must not use a dark ANSI background");
|
||||
};
|
||||
let luminance = |rgb: [u8; 3]| {
|
||||
rgb.into_iter()
|
||||
.zip([0.2126, 0.7152, 0.0722])
|
||||
.map(|(c, w)| {
|
||||
let c = f64::from(c) / 255.0;
|
||||
w * if c <= 0.04045 {
|
||||
c / 12.92
|
||||
} else {
|
||||
((c + 0.055) / 1.055).powf(2.4)
|
||||
}
|
||||
})
|
||||
.sum::<f64>()
|
||||
};
|
||||
for foreground in [[69, 49, 38], [138, 98, 28], [153, 91, 43]] {
|
||||
assert!((luminance([r, g, b]) + 0.05) / (luminance(foreground) + 0.05) >= 4.5);
|
||||
}
|
||||
assert_ne!([r, g, b], light.background, "selection must remain visible");
|
||||
} else {
|
||||
assert_eq!(
|
||||
cell.bg,
|
||||
if focused {
|
||||
Color::Blue
|
||||
} else {
|
||||
Color::DarkGray
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn rendered_whitespace_line(
|
||||
source: &str,
|
||||
|
||||
Reference in New Issue
Block a user