Fix appearance persistence and add drag over folder highlighting
All checks were successful
Automated Container Build / build-and-push (push) Successful in 32s

This commit is contained in:
Elijah 2026-05-22 18:07:20 -07:00
parent d532f8d064
commit 425598c278

View file

@ -89,9 +89,28 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
const [movingFiles, setMovingFiles] = useState<string[]>([]); const [movingFiles, setMovingFiles] = useState<string[]>([]);
const [moveModalPath, setMoveModalPath] = useState('.'); const [moveModalPath, setMoveModalPath] = useState('.');
const [moveFolders, setMoveFolders] = useState<FileItem[]>([]); const [moveFolders, setMoveFolders] = useState<FileItem[]>([]);
const [dragOverFolder, setDragOverFolder] = useState<string | null>(null);
const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined); const searchTimeoutRef = useRef<NodeJS.Timeout | undefined>(undefined);
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
api.getSettings().then((data) => {
const theme = data?.settings?.theme || data?.theme;
if (theme) {
if (theme === 'light') {
document.documentElement.setAttribute('data-theme', 'light');
document.documentElement.classList.remove('dark');
document.documentElement.style.setProperty('color-scheme', 'light');
} else {
document.documentElement.setAttribute('data-theme', 'dark');
document.documentElement.classList.add('dark');
document.documentElement.style.setProperty('color-scheme', 'dark');
}
localStorage.setItem('theme', theme);
}
}).catch(console.error);
}, []);
// Load files on path change // Load files on path change
const loadFiles = useCallback(async (path?: string) => { const loadFiles = useCallback(async (path?: string) => {
const targetPath = path ?? currentPath; const targetPath = path ?? currentPath;
@ -339,10 +358,20 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
if (!file.is_dir) return; if (!file.is_dir) return;
if (draggedFile === file.path) return; if (draggedFile === file.path) return;
e.preventDefault(); e.preventDefault();
if (dragOverFolder !== file.path) {
setDragOverFolder(file.path);
}
}
function handleItemDragLeave(e: React.DragEvent, file: FileItem) {
if (dragOverFolder === file.path) {
setDragOverFolder(null);
}
} }
async function handleItemDrop(e: React.DragEvent, file: FileItem) { async function handleItemDrop(e: React.DragEvent, file: FileItem) {
if (!file.is_dir) return; if (!file.is_dir) return;
setDragOverFolder(null);
const sourcePath = e.dataTransfer.getData('text/plain'); const sourcePath = e.dataTransfer.getData('text/plain');
if (!sourcePath || sourcePath === file.path) return; if (!sourcePath || sourcePath === file.path) return;
@ -829,6 +858,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
draggable draggable
onDragStart={(e: any) => handleItemDragStart(e, file.path)} onDragStart={(e: any) => handleItemDragStart(e, file.path)}
onDragOver={(e: any) => handleItemDragOver(e, file)} onDragOver={(e: any) => handleItemDragOver(e, file)}
onDragLeave={(e: any) => handleItemDragLeave(e, file)}
onDrop={(e: any) => handleItemDrop(e, file)} onDrop={(e: any) => handleItemDrop(e, file)}
initial={{ opacity: 0, scale: 0.95 }} initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }} animate={{ opacity: 1, scale: 1 }}
@ -837,8 +867,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
onContextMenu={(e) => handleContextMenu(e, file)} onContextMenu={(e) => handleContextMenu(e, file)}
className={`group relative p-4 rounded-xl cursor-pointer transition-all duration-150 border-2 ${draggedFile === file.path ? 'opacity-50' : ''}`} className={`group relative p-4 rounded-xl cursor-pointer transition-all duration-150 border-2 ${draggedFile === file.path ? 'opacity-50' : ''}`}
style={{ style={{
backgroundColor: selectedFiles.has(file.path) ? 'var(--color-accent-subtle)' : 'var(--color-bg-elevated)', backgroundColor: selectedFiles.has(file.path) || dragOverFolder === file.path ? 'var(--color-accent-subtle)' : 'var(--color-bg-elevated)',
border: `1px solid ${selectedFiles.has(file.path) ? 'var(--color-accent)' : 'var(--color-border-subtle)'}`, border: `1px solid ${selectedFiles.has(file.path) || dragOverFolder === file.path ? 'var(--color-accent)' : 'var(--color-border-subtle)'}`,
}} }}
> >
<div <div
@ -907,6 +937,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
draggable draggable
onDragStart={(e: any) => handleItemDragStart(e, file.path)} onDragStart={(e: any) => handleItemDragStart(e, file.path)}
onDragOver={(e: any) => handleItemDragOver(e, file)} onDragOver={(e: any) => handleItemDragOver(e, file)}
onDragLeave={(e: any) => handleItemDragLeave(e, file)}
onDrop={(e: any) => handleItemDrop(e, file)} onDrop={(e: any) => handleItemDrop(e, file)}
initial={{ opacity: 0, y: 10 }} initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
@ -915,8 +946,8 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
onContextMenu={(e) => handleContextMenu(e, file)} onContextMenu={(e) => handleContextMenu(e, file)}
className={`group relative p-4 rounded-xl cursor-pointer transition-all duration-150 ${draggedFile === file.path ? 'opacity-50' : ''}`} className={`group relative p-4 rounded-xl cursor-pointer transition-all duration-150 ${draggedFile === file.path ? 'opacity-50' : ''}`}
style={{ style={{
backgroundColor: selectedFiles.has(file.path) ? 'var(--color-accent-subtle)' : 'var(--color-bg-elevated)', backgroundColor: selectedFiles.has(file.path) || dragOverFolder === file.path ? 'var(--color-accent-subtle)' : 'var(--color-bg-elevated)',
border: `1px solid ${selectedFiles.has(file.path) ? 'var(--color-accent)' : 'var(--color-border-subtle)'}`, border: `1px solid ${selectedFiles.has(file.path) || dragOverFolder === file.path ? 'var(--color-accent)' : 'var(--color-border-subtle)'}`,
}} }}
> >
<div <div
@ -986,6 +1017,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
draggable draggable
onDragStart={(e: any) => handleItemDragStart(e, file.path)} onDragStart={(e: any) => handleItemDragStart(e, file.path)}
onDragOver={(e: any) => handleItemDragOver(e, file)} onDragOver={(e: any) => handleItemDragOver(e, file)}
onDragLeave={(e: any) => handleItemDragLeave(e, file)}
onDrop={(e: any) => handleItemDrop(e, file)} onDrop={(e: any) => handleItemDrop(e, file)}
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
@ -993,7 +1025,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
onClick={(e) => handleFileClick(file, e)} onClick={(e) => handleFileClick(file, e)}
onContextMenu={(e) => handleContextMenu(e, file)} onContextMenu={(e) => handleContextMenu(e, file)}
className={`grid grid-cols-[1fr_100px_140px_40px] gap-4 items-center px-4 py-2.5 rounded-lg cursor-pointer transition-all duration-100 group ${draggedFile === file.path ? 'opacity-50' : ''}`} className={`grid grid-cols-[1fr_100px_140px_40px] gap-4 items-center px-4 py-2.5 rounded-lg cursor-pointer transition-all duration-100 group ${draggedFile === file.path ? 'opacity-50' : ''}`}
style={{ backgroundColor: selectedFiles.has(file.path) ? 'var(--color-accent-subtle)' : 'transparent' }} style={{ backgroundColor: selectedFiles.has(file.path) || dragOverFolder === file.path ? 'var(--color-accent-subtle)' : 'transparent' }}
> >
<div className="flex items-center gap-3 min-w-0"> <div className="flex items-center gap-3 min-w-0">
<input <input
@ -1052,6 +1084,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
draggable draggable
onDragStart={(e: any) => handleItemDragStart(e, file.path)} onDragStart={(e: any) => handleItemDragStart(e, file.path)}
onDragOver={(e: any) => handleItemDragOver(e, file)} onDragOver={(e: any) => handleItemDragOver(e, file)}
onDragLeave={(e: any) => handleItemDragLeave(e, file)}
onDrop={(e: any) => handleItemDrop(e, file)} onDrop={(e: any) => handleItemDrop(e, file)}
initial={{ opacity: 0 }} initial={{ opacity: 0 }}
animate={{ opacity: 1 }} animate={{ opacity: 1 }}
@ -1059,7 +1092,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
onClick={(e) => handleFileClick(file, e)} onClick={(e) => handleFileClick(file, e)}
onContextMenu={(e) => handleContextMenu(e, file)} onContextMenu={(e) => handleContextMenu(e, file)}
className={`grid grid-cols-[1fr_100px_140px_40px] gap-4 items-center px-4 py-2.5 rounded-lg cursor-pointer transition-all duration-100 group ${draggedFile === file.path ? 'opacity-50' : ''}`} className={`grid grid-cols-[1fr_100px_140px_40px] gap-4 items-center px-4 py-2.5 rounded-lg cursor-pointer transition-all duration-100 group ${draggedFile === file.path ? 'opacity-50' : ''}`}
style={{ backgroundColor: selectedFiles.has(file.path) ? 'var(--color-accent-subtle)' : 'transparent' }} style={{ backgroundColor: selectedFiles.has(file.path) || dragOverFolder === file.path ? 'var(--color-accent-subtle)' : 'transparent' }}
> >
<div className="flex items-center gap-3 min-w-0"> <div className="flex items-center gap-3 min-w-0">
<input <input