Type-to-filter in file panels

Typing while the list is focused builds a filter query: a bar appears under
the column headers (search icon, query, live match count, Esc hint, clear
button) and the listing narrows to substring matches — '..' always stays.
Backspace edits the query, Escape clears it, cursor parks on the first match,
arrows/Enter keep working (the list never loses focus), and Space keeps its
select meaning until a query exists. Filter resets on directory change and
survives refresh; operations act only on visible (filtered) selection.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jokin
2026-07-11 22:05:23 +02:00
co-authored by Claude Fable 5
parent 41f9e14cad
commit 9737cad6de
4 changed files with 74 additions and 1 deletions
+15 -1
View File
@@ -24,10 +24,16 @@ export class PanelState {
readonly sortAsc = signal(true);
readonly cursor = signal(0);
readonly selected = signal<ReadonlySet<string>>(new Set());
/** Type-to-filter query; entries not matching are hidden (".." always stays). */
readonly filter = signal('');
/** Directories first, then the active sort column. ".." (parent) is pinned on top. */
readonly sorted = computed(() => {
const list = [...this.entries()];
const query = this.filter().toLowerCase();
let list = [...this.entries()];
if (query) {
list = list.filter((e) => e.name.toLowerCase().includes(query));
}
const key = this.sortKey();
const dir = this.sortAsc() ? 1 : -1;
list.sort((a, b) => {
@@ -69,6 +75,7 @@ export class PanelState {
this.entries.set(res.entries);
this.cursor.set(0);
this.selected.set(new Set());
this.filter.set('');
} finally {
this.loading.set(false);
}
@@ -110,6 +117,13 @@ export class PanelState {
void this.load(`${this.cwd()}/..`);
}
/** Update the filter and park the cursor on the first real match. */
setFilter(query: string): void {
this.filter.set(query);
const first = this.sorted().findIndex((e) => e.name !== '..');
this.cursor.set(first >= 0 ? first : 0);
}
toggleSort(key: SortKey): void {
if (this.sortKey() === key) {
this.sortAsc.update((v) => !v);