+
+
+
+
+
+
+
+
+ Sort by
+ setSort(v as Sort)}>
+ Name
+ Size
+ Last changed
+
+
+
+ {canCreate && (
+
+
+
+
+
+ actions.onNew("note", dir)}>
+ Note
+
+ actions.onNew("folder", dir)}>
+ Folder
+
+
+
+ )}
+
+
+
+
+
+
+ setFilter(e.target.value)} placeholder="Filter, or search every file and note" className="h-8 pl-8 text-[13px]" aria-label="Filter and search" />
+ {filter && (
+
+ )}
+
+
+
+ {checkedList.length > 0 && (
+
+ {plural(checkedList.length, "item")} selected
+ {checkedBytes > 0 && {formatBytes(checkedBytes)}}
+
+
+
+
+
+
+ )}
+
+
+ {pickable.length > 1 && (
+
+ setChecked(v === true ? new Set(pickable.map((e) => e.path)) : new Set())}
+ />
+ Name
+ Size
+ Changed
+
+
+ )}
+ {listing.isLoading ? (
+
+ {Array.from({ length: 7 }, (_, i) => (
+
+ ))}
+
+ ) : listing.isError ? (
+
{errorMessage(listing.error)}
+ ) : !entries.length ? (
+
{needle ? "Nothing here matches." : "This folder is empty."}
+ ) : (
+
+ {entries.map((e) => {
+ const isDir = e.type === "dir";
+ const size = sizes.get(e.path);
+ const bytes = size?.bytes ?? (isDir ? null : e.size);
+ const locked = isLocked(e.path);
+ const active = selected === e.path;
+ const on = checked.has(e.path);
+ const modified = size?.modifiedAt ?? e.modifiedAt;
+ return (
+ -
+
+ {locked ? (
+
+ ) : (
+ toggle(e.path, v === true)} className={cn(!on && !checkedList.length && "opacity-0 group-hover/row:opacity-100 focus-visible:opacity-100 pointer-coarse:opacity-100")} />
+ )}
+
+
+
+ {bytes === null ? (
+
+ ) : (
+
+
+ {size?.partial ? "≥ " : ""}
+ {formatBytes(bytes)}
+
+
+
+
+
+ )}
+
+
+ {modified ? formatDistanceToNowStrict(new Date(modified)) : ""}
+
+ (isDir ? onDir(e.path) : onSelect({ path: e.path, size: e.size }))} />
+
+ );
+ })}
+
+ )}
+
+ {search.length >= 2 && (
+
+ In all files
+ {found.isLoading ? (
+
+ ) : !otherHits.length ? (
+ No other file or note mentions “{search}”.
+ ) : (
+
+ {otherHits.map((h) => (
+ -
+
+
+ ))}
+
+ )}
+
+ )}
+
+
+
+ {needle ? `${entries.length} of ${plural(listing.data?.length ?? 0, "item")}` : plural(entries.length, "item")}
+ {total > 0 && !needle && · {formatBytes(total)}}
+ {sizesQ.isFetching && counting…}
+
+
+ );
+}
+
+function RowMenu({ path, isDir, locked, frozen, actions, onOpen }: { path: string; isDir: boolean; locked: boolean; frozen: boolean; actions: BrowseActions; onOpen: () => void }) {
+ return (
+