import { useState, useEffect } from "react"; import { FileBrowserWidget } from "@/modules/storage"; import { AclEditor } from "@/modules/storage/AclEditor"; import { vfsIndex, vfsClearIndex, subscribeToVfsIndexStream } from "@/modules/storage/client-vfs"; import { Card } from "@/components/ui/card"; import { Separator } from "@/components/ui/separator"; import { T, translate } from "@/i18n"; import { Button } from "@/components/ui/button"; import { RefreshCw, Database, Trash2 } from "lucide-react"; import { toast } from "sonner"; import { useAuth } from "@/hooks/useAuth"; import { Progress } from "@/components/ui/progress"; import { Checkbox } from "@/components/ui/checkbox"; import { Label } from "@/components/ui/label"; import { Input } from "@/components/ui/input"; export default function StorageManager() { // Default to 'root' mount, but we could allow selecting mounts if there are multiple. // For now assuming 'root' is the main VFS. const [mount, setMount] = useState("home"); const [currentPath, setCurrentPath] = useState("/"); const [selectedPath, setSelectedPath] = useState(null); const [manualPath, setManualPath] = useState("/"); const [indexing, setIndexing] = useState(false); const [indexProgress, setIndexProgress] = useState(0); const [indexCountMsg, setIndexCountMsg] = useState(""); const [fullText, setFullText] = useState(false); const { session } = useAuth(); // The ACL editor shows permissions for the manual path. // Sync external file browser selections into the manual input. useEffect(() => { setManualPath(selectedPath || currentPath); }, [selectedPath, currentPath]); const targetPath = manualPath; useEffect(() => { if (!indexing) return; let eventSource: EventSource | undefined; let isMounted = true; subscribeToVfsIndexStream(mount, targetPath, (progress, msg) => { if (!isMounted) return; setIndexProgress(progress); setIndexCountMsg(msg); }, session?.access_token).then(es => { if (!isMounted) { es.close(); } else { eventSource = es; } }); return () => { isMounted = false; if (eventSource) eventSource.close(); }; }, [indexing, mount, targetPath, session]); const handleClearIndex = async () => { if (!confirm("Are you sure you want to clear the index for this path? This will not delete actual files.")) return; try { setIndexing(true); const data = await vfsClearIndex(mount, targetPath); toast.success(translate("Index cleared"), { description: data.message }); } catch (err: any) { toast.error(translate("Failed to clear index"), { description: err.message }); } finally { setIndexing(false); } }; const handleIndex = async () => { try { setIndexing(true); setIndexProgress(0); setIndexCountMsg("Preparing indexing batch..."); const data = await vfsIndex(mount, targetPath, fullText); toast.success(translate("Indexing completed"), { description: data.message }); } catch (err: any) { toast.error(translate("Failed to index mount"), { description: err.message }); } finally { // keep it somewhat visible for a second if extremely fast setTimeout(() => { setIndexing(false); setIndexProgress(0); setIndexCountMsg(""); }, 1000); } }; return (
{/* Left Pane: File Browser */}

File Browser

{ setMount(m); setCurrentPath('/'); setSelectedPath(null); }} onPathChange={(p) => { setCurrentPath(p); setSelectedPath(null); // Clear selection when navigating }} onSelect={setSelectedPath} viewMode="list" mode="simple" canChangeMount={true} allowFileViewer={false} allowDownload={true} allowPreview={false} allowFileUpload={true} allowFileDelete={true} allowFileMove={true} allowFileRename={true} allowFolderCreate={true} allowFolderDelete={true} allowFolderMove={true} allowFolderRename={true} showToolbar={true} index={false} glob="*.*" sortBy="name" />
{/* Right Pane: ACL Editor */}

Permissions & Tasks

Selected Path
{mount}:
setManualPath(e.target.value)} className="font-mono" />
Database Synchronization
setFullText(checked as boolean)} disabled={indexing} />
{indexing && (
{indexCountMsg || `${indexProgress}%`}
)}
); }