refactor(tui): trim the folder column change to what it needs

The path splitter is a one-line stdlib call with a single caller, so it
lives beside that caller as a private fn, without its own tests. The pane
tests assert the two columns in one screen fragment each, the end-offset
test keeps only the two cases the existing name tests do not cover, and
the key-handling test that repeated one of them goes. The website mock
splits on the backslash the same way its Info pane already does.
This commit is contained in:
Michel de Graaf
2026-09-08 05:59:39 +00:00
parent da371d4176
commit 2733ee26f7
4 changed files with 22 additions and 61 deletions
+2 -2
View File
@@ -98,8 +98,8 @@ underneath. `Tab`/`Shift-Tab` or `1`–`3` move the focus between panes, `z`
zooms the focused pane to the whole window, and `w` hides it so the others
widen (its number brings it back). Every list takes `↑`/`↓`, `Home`/`End`,
`PgUp`/`PgDn` and the vim keys, and `h`/`l` or `←`/`→` scroll a long file name,
folder or query sideways, `0` and `$` to either end. `PgUp`/`PgDn` scroll a chat log back through its history, and `?`
shows every key for where you are.
folder or query sideways, `0` and `$` to either end. `PgUp`/`PgDn` scroll a
chat log back through its history, and `?` shows every key for where you are.
### Daemon mode
-12
View File
@@ -1274,18 +1274,6 @@ mod tests {
);
}
#[test]
fn scrolling_runs_to_the_end_of_the_folder_when_it_is_the_longer() {
let mut tui = results_tui(3);
tui.state.results_items[1].filename =
format!("{}\\short.mp3", "f".repeat(20));
tui.state.results_table_state.select(Some(1));
press(&mut tui, KeyCode::Char('$'));
assert_eq!(tui.state.results_name_offset, 14);
press(&mut tui, KeyCode::Char('l'));
assert_eq!(tui.state.results_name_offset, 14, "stops at the end");
}
#[test]
fn picking_another_search_starts_its_results_unscrolled() {
let mut tui = results_tui(3);
+17 -36
View File
@@ -3,7 +3,7 @@ use crate::models::FileDisplayData;
use crate::ui::{
BYTES_PER_MB, HIGHLIGHT_SYMBOL, body_style, dimmed_style, format_bytes,
header_style, info_style, pane_block, pane_title, row_highlight_style,
split_path, success_style, warning_style,
success_style, warning_style,
};
use ratatui::{
Frame,
@@ -47,6 +47,12 @@ pub struct ResultsPaneParams<'a> {
const NAME_COLUMN: usize = 1;
const FOLDER_COLUMN: usize = 2;
/// A shared path as `(folder, name)`. Peers send backslashes, but a slash
/// counts too.
fn split_path(path: &str) -> (&str, &str) {
path.rsplit_once(['\\', '/']).unwrap_or(("", path))
}
/// The offset that brings the end of a result's longer column into view.
/// One offset scrolls the name and the folder together, so it runs until
/// both tails are showing.
@@ -285,55 +291,30 @@ mod tests {
}
#[test]
fn a_path_shows_its_name_and_folder_in_separate_columns() {
fn a_path_shows_its_name_before_its_folder() {
let items = [file("@@abc\\Music\\Album\\01.flac"), file("cover.jpg")];
let screen = render_rows(&items, 0);
let header = screen.lines().nth(1).expect("header");
let name = header.find("Filename").expect("name header");
let folder = header.find("Folder").expect("folder header");
assert!(name < folder, "{header}");
let row = screen.lines().nth(2).expect("first row");
assert!(row.contains("01.flac "), "{row}");
assert!(!row.contains("Album\\01.flac"), "the name alone: {row}");
assert!(row.contains("@@abc\\M"), "{row}");
assert!(
row.find("01.flac") < row.find("@@abc"),
"the name before the folder: {row}"
);
let row = screen.lines().nth(3).expect("second row");
assert!(row.contains("cover.jpg"), "{row}");
assert!(screen.contains("01.flac @@abc\\M"), "{screen}");
assert!(screen.contains("cover.jpg "), "{screen}");
}
#[test]
fn one_offset_scrolls_the_name_and_the_folder_together() {
let items = [file("@@abc\\Music\\Album\\abcdefghijklmnopqrstuvwxyz")];
let screen = render_rows(&items, 4);
assert!(screen.contains("…efghijklmn"), "{screen}");
assert!(screen.contains("…c\\Musi"), "{screen}");
assert!(screen.contains("…efghijklmn …c\\Musi"), "{screen}");
}
#[test]
fn the_end_offset_is_the_longer_columns() {
use super::name_end_offset;
use ratatui::layout::Rect;
// 88 cells wide: 11 for the name, 7 for the folder.
let area = Rect::new(0, 0, 88, 6);
assert_eq!(name_end_offset("short.mp3", area), 0);
assert_eq!(name_end_offset(&"x".repeat(40), area), 30);
let area = ratatui::layout::Rect::new(0, 0, 88, 6);
let folder = "f".repeat(20);
assert_eq!(super::name_end_offset(&format!("{folder}\\a"), area), 14);
let name = "x".repeat(40);
assert_eq!(
name_end_offset(&format!("{}\\short.mp3", "f".repeat(20)), area),
14,
"the folder overflows its narrower column"
);
assert_eq!(
name_end_offset(
&format!("{}\\{}", "f".repeat(20), "x".repeat(40)),
area
),
30,
"whichever runs further"
super::name_end_offset(&format!("{folder}\\{name}"), area),
30
);
}
+3 -11
View File
@@ -6,14 +6,6 @@ import { TuiPane } from './chrome'
import type { ResultRow, SearchRow, TransferRow, TransferStatus } from './data'
const RESULT_COLS = '30px 3fr 2fr 74px 92px 78px 74px 42px'
/** A shared path split at its last separator, as the TUI's two columns show it. */
const splitPath = (path: string): { folder: string; name: string } => {
const at = Math.max(path.lastIndexOf('\\'), path.lastIndexOf('/'))
return at < 0
? { folder: '', name: path }
: { folder: path.slice(0, at), name: path.slice(at + 1) }
}
const TRANSFER_COLS = '48px 1fr 96px 168px 74px'
interface SearchesPaneProps {
@@ -152,7 +144,7 @@ export const ResultsPane = ({
<div className="flex-1 overflow-y-auto">
{rows.map((row, i) => {
const { glyph, tone } = resultGlyph(row)
const { folder, name } = splitPath(row.name)
const parts = row.name.split('\\')
const isSelected = selected === i
return (
<button
@@ -177,10 +169,10 @@ export const ResultsPane = ({
isSelected ? 'text-[var(--color-paper)]' : 'text-[var(--color-paper-dim)]',
)}
>
{name}
{parts.at(-1)}
</span>
<span className="overflow-hidden text-ellipsis whitespace-nowrap text-[var(--color-dust)]">
{folder}
{parts.slice(0, -1).join('\\')}
</span>
<span className="text-right text-[var(--color-tape)]">{row.size}</span>
<span className="text-[var(--color-signal)]">{row.user}</span>