Configurable list columns via header right-click
Native checkable menu on the column headers toggles Size, Modified, Kind, Rights, Owner and Group (persisted, both panels). Owner/group come from the ls-style SFTP longname remotely and cached uid/gid lookups locally (POSIX; undefined on Windows). All columns sort. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,42 @@ function typeOf(st: Stats): EntryType {
|
||||
return st.isDirectory() ? "dir" : "file";
|
||||
}
|
||||
|
||||
/**
|
||||
* uid/gid -> names, cached per process (a listing usually has 1-3 owners).
|
||||
*
|
||||
* PORTABILITY: POSIX-only lookups (`id -nu` works on macOS and Linux; group
|
||||
* names use dscacheutil on macOS, getent elsewhere). On Windows these return
|
||||
* undefined and the Owner/Group columns show "—".
|
||||
*/
|
||||
const idNameCache = new Map<string, string | undefined>();
|
||||
|
||||
function resolveId(kind: "user" | "group", id: number): string | undefined {
|
||||
if (process.platform === "win32") return undefined;
|
||||
const cacheKey = `${kind}:${id}`;
|
||||
if (idNameCache.has(cacheKey)) return idNameCache.get(cacheKey);
|
||||
let name: string | undefined;
|
||||
try {
|
||||
const cmd =
|
||||
kind === "user"
|
||||
? ["id", "-nu", String(id)]
|
||||
: process.platform === "darwin"
|
||||
? ["dscacheutil", "-q", "group", "-a", "gid", String(id)]
|
||||
: ["getent", "group", String(id)];
|
||||
const proc = Bun.spawnSync(cmd, { stdout: "pipe", stderr: "ignore" });
|
||||
if (proc.success) {
|
||||
const out = proc.stdout.toString();
|
||||
if (kind === "user") name = out.trim() || undefined;
|
||||
else if (process.platform === "darwin") name = /^name:\s*(.+)$/m.exec(out)?.[1]?.trim();
|
||||
else name = out.split(":")[0]?.trim() || undefined;
|
||||
}
|
||||
} catch {
|
||||
// lookup tool missing — fall through to the numeric id
|
||||
}
|
||||
name ??= String(id);
|
||||
idNameCache.set(cacheKey, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
function toEntry(name: string, full: string, st: Stats): FileEntry {
|
||||
return {
|
||||
name,
|
||||
@@ -19,6 +55,8 @@ function toEntry(name: string, full: string, st: Stats): FileEntry {
|
||||
size: st.size,
|
||||
mtime: st.mtimeMs,
|
||||
mode: st.mode & 0o7777,
|
||||
owner: resolveId("user", st.uid),
|
||||
group: resolveId("group", st.gid),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user