diff --git a/components/utilities/files/FileBrowserPanel.tsx b/components/utilities/files/FileBrowserPanel.tsx index 4c5db41a..c0084c7b 100644 --- a/components/utilities/files/FileBrowserPanel.tsx +++ b/components/utilities/files/FileBrowserPanel.tsx @@ -11,18 +11,17 @@ type FileItem = { }; type ListResponse = -| { path: string; items: FileItem[] } // current API shape -| { path: string; entries: FileItem[] } // legacy fallback +| { path: string; items: FileItem[] } +| { path: string; entries: FileItem[] } | { items?: FileItem[]; entries?: FileItem[]; path?: string }; const BASE = -(process.env.NEXT_PUBLIC_FILE_API_BASE_URL || "").replace(/\/$/, "") || ""; // same-origin fallback +(process.env.NEXT_PUBLIC_FILE_API_BASE_URL || "").replace(/\/$/, "") || ""; function joinPath(a: string, b: string) { if (!a || a === "/") return b.startsWith("/") ? b : `/${b}`; return `${a.replace(/\/$/, "")}/${b.replace(/^\//, "")}`; } - function parentPath(path: string) { if (!path || path === "/") return "/"; const parts = path.replace(/\/+$/, "").split("/"); @@ -30,7 +29,6 @@ function parentPath(path: string) { const p = parts.join("/"); return p === "" ? "/" : p; } - function formatSize(bytes?: number) { if (bytes == null) return "—"; const units = ["B", "KB", "MB", "GB", "TB"]; @@ -54,12 +52,10 @@ export default function FileBrowserPanel() { const p = encodeURIComponent(path || "/"); return `${BASE}/api/files/list?path=${p}`; }, [path]); - const urlDownload = useCallback((p: string) => { const qp = encodeURIComponent(p || "/"); return `${BASE}/api/files/download?path=${qp}`; }, []); - const urlRaw = useCallback((p: string) => { const qp = encodeURIComponent(p || "/"); return `${BASE}/api/files/raw?path=${qp}`; @@ -72,7 +68,7 @@ export default function FileBrowserPanel() { try { const res = await fetch(urlList, { method: "GET", - credentials: "include", // ensure cookies flow past middleware + credentials: "include", headers: { Accept: "application/json" }, cache: "no-store", }); @@ -83,7 +79,6 @@ export default function FileBrowserPanel() { const json: ListResponse = await res.json(); const arr = (json as any).items || (json as any).entries || []; if (!Array.isArray(arr)) throw new Error("Malformed list response"); - // Sort: dirs first, then files, alpha arr.sort((a: FileItem, b: FileItem) => { if (a.type !== b.type) return a.type === "dir" ? -1 : 1; return a.name.localeCompare(b.name); @@ -105,17 +100,13 @@ export default function FileBrowserPanel() { if (it.type === "dir") { setPath((p) => joinPath(p, it.name)); } else { - // try to preview; if the file is not previewable, user can still download setPreviewHref(urlRaw(joinPath(path, it.name))); } }; - const onUp = () => setPath((p) => parentPath(p)); const onHome = () => setPath("/"); - const onDownload = (it: FileItem) => { const href = urlDownload(joinPath(path, it.name)); - // create an anchor to download const a = document.createElement("a"); a.href = href; a.download = it.name; @@ -126,7 +117,6 @@ export default function FileBrowserPanel() { return (
- {/* toolbar */}
- {/* path crumb */}
{path}
- {/* grid: table + preview */} -
+ {/* Wider table, same preview: left has min 760px; table can scroll horizontally if cramped */} +
+ {/* TABLE */}
-
+
+
+
Name
Type
Size
@@ -180,7 +172,7 @@ export default function FileBrowserPanel() { items.map((it) => (
+
+
- {/* preview */} + {/* PREVIEW (unchanged size) */}
Preview
{previewHref ? (
- {/* let the browser try its best; PDFs/images will render inline */}