import { type FormEvent, useMemo, useState } from "react"; import { useQueryClient } from "@tanstack/react-query"; import { ChevronRight, FileIcon, FilesIcon, Folder, FolderOpen, FolderPlus, Home, Pencil, Trash2 } from "lucide-react"; import { useNavigate, useSearch } from "@tanstack/react-router"; import { $api } from "@/client/lib/api"; import type { FileNode } from "@/client/lib/types"; import { Button } from "@/client/components/ui/button"; import { ConfirmDialog } from "@/client/components/ui/confirm-dialog"; import { Dialog } from "@/client/components/ui/dialog"; import { EmptyState } from "@/client/components/empty-state"; import { ErrorBanner } from "@/client/components/error-banner"; import { Input } from "@/client/components/ui/input"; import { Label } from "@/client/components/ui/label"; import { PageHeader } from "@/client/components/ui/page-header"; import { Skeleton } from "@/client/components/loading-panel"; import { formatBytes, formatDate } from "@/client/lib/format"; import { paths } from "@/client/lib/paths"; import { toast } from "@/client/lib/toast"; function normalizeDirPath(input: string): string { let value = input.trim(); if (!value) value = "/files/"; if (!value.startsWith("/files")) value = "/files" + (value.startsWith("/") ? value : `/${value}`); if (!value.endsWith("/")) value += "/"; return value; } function parentOf(dir: string): string | null { if (dir === "/files/" || dir === "/files") return null; const trimmed = dir.endsWith("/") ? dir.slice(0, -1) : dir; const idx = trimmed.lastIndexOf("/"); return idx <= "/files".length - 1 ? "/files/" : `${trimmed.slice(0, idx)}/`; } function buildBreadcrumbs(dir: string): { label: string; path: string }[] { const segments = dir.replace(/^\/+/, "").replace(/\/+$/, "").split("/"); const crumbs: { label: string; path: string }[] = []; let acc = ""; for (const segment of segments) { acc += `/${segment}`; crumbs.push({ label: segment, path: `${acc}/` }); } return crumbs; } export function FilesPage() { const search = useSearch({ from: paths.files }); const navigate = useNavigate(); const dir = normalizeDirPath(search.path); const listing = $api.useQuery("get", "/api/v1/files/list", { params: { query: { path: dir } } }); const [mkdirOpen, setMkdirOpen] = useState(false); const [renameTarget, setRenameTarget] = useState(null); const breadcrumbs = useMemo(() => buildBreadcrumbs(dir), [dir]); const parent = parentOf(dir); const goTo = (path: string) => navigate({ to: paths.files, search: { path } }); return (
); } function FileTable({ entries, parent, onNavigate, onRename, currentDir, }: { entries: FileNode[]; parent: string | null; onNavigate: (path: string) => void; onRename: (node: FileNode) => void; currentDir: string; }) { if (entries.length === 0 && !parent) { return (