diff --git a/frontend/src/components/FileExplorer.tsx b/frontend/src/components/FileExplorer.tsx index 135fd3a..bf750e6 100644 --- a/frontend/src/components/FileExplorer.tsx +++ b/frontend/src/components/FileExplorer.tsx @@ -46,6 +46,20 @@ interface UploadProgress { error?: string; } +function getFileDisplayName(name: string, isDir: boolean) { + if (isDir) return name; + const dotIndex = name.lastIndexOf('.'); + if (dotIndex > 0) return name.substring(0, dotIndex); + return name; +} + +function getFileExtension(name: string, isDir: boolean) { + if (isDir) return ''; + const dotIndex = name.lastIndexOf('.'); + if (dotIndex > 0) return name.substring(dotIndex); + return ''; +} + export default function FileExplorer({ onLogout }: FileExplorerProps) { const [currentPath, setCurrentPath] = useState(() => { if (typeof window !== 'undefined') { @@ -324,6 +338,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { } function handleFileClick(file: FileItem, e: React.MouseEvent) { + if (renaming === file.path) return; if (e.shiftKey) { // Multi-select with shift setSelectedFiles(prev => { @@ -406,9 +421,20 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { } } - async function handleRename(path: string) { - if (!newName.trim()) return; - await api.rename(path, newName); + async function handleRename(path: string, originalName: string, isDir: boolean) { + if (!newName.trim()) { + setRenaming(null); + setNewName(''); + return; + } + const ext = getFileExtension(originalName, isDir); + const finalName = newName.trim() + ext; + if (finalName === originalName) { + setRenaming(null); + setNewName(''); + return; + } + await api.rename(path, finalName); setRenaming(null); setNewName(''); loadFiles(); @@ -785,7 +811,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) {
{getFileIcon(file)}
- {file.name} + {getFileDisplayName(file.name, file.is_dir)} )) )} @@ -1141,10 +1167,11 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { type="text" value={newName} onChange={(e) => setNewName(e.target.value)} - onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path); if (e.key === 'Escape') setRenaming(null); }} - onBlur={() => handleRename(file.path)} + onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path, file.name, file.is_dir); if (e.key === 'Escape') setRenaming(null); }} + onBlur={() => handleRename(file.path, file.name, file.is_dir)} autoFocus onFocus={(e) => e.target.select()} + onClick={(e) => e.stopPropagation()} className="w-full text-xs text-center bg-transparent outline-none" style={{ color: 'var(--color-text-primary)', @@ -1155,9 +1182,9 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { - {file.name} + {getFileDisplayName(file.name, file.is_dir)} )} @@ -1221,15 +1248,16 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { type="text" value={newName} onChange={(e) => setNewName(e.target.value)} - onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path); if (e.key === 'Escape') setRenaming(null); }} - onBlur={() => handleRename(file.path)} + onKeyDown={(e) => { if (e.key === 'Enter') handleRename(file.path, file.name, file.is_dir); if (e.key === 'Escape') setRenaming(null); }} + onBlur={() => handleRename(file.path, file.name, file.is_dir)} autoFocus onFocus={(e) => e.target.select()} + onClick={(e) => e.stopPropagation()} className="flex-1 text-sm bg-transparent outline-none" style={{ color: 'var(--color-text-primary)', borderBottom: '1px solid var(--color-accent)' }} /> ) : ( - {file.name} + {getFileDisplayName(file.name, file.is_dir)} )} {file.is_pinned && } @@ -1376,7 +1404,7 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { label="Rename" onClick={() => { setRenaming(contextMenu.file.path); - setNewName(contextMenu.file.name); + setNewName(getFileDisplayName(contextMenu.file.name, contextMenu.file.is_dir)); setContextMenu(null); }} /> @@ -1490,9 +1518,9 @@ export default function FileExplorer({ onLogout }: FileExplorerProps) { - {upload.name} + {getFileDisplayName(upload.name, false)} {upload.status === 'complete' ? '✓' : upload.status === 'error' ? '✗' : `${Math.round(upload.progress)}%`} diff --git a/frontend/src/components/FilePreview.tsx b/frontend/src/components/FilePreview.tsx index 28caa5c..c5ba0a8 100644 --- a/frontend/src/components/FilePreview.tsx +++ b/frontend/src/components/FilePreview.tsx @@ -18,6 +18,12 @@ interface FilePreviewProps { onClose: () => void; } +function getFileDisplayName(name: string) { + const dotIndex = name.lastIndexOf('.'); + if (dotIndex > 0) return name.substring(0, dotIndex); + return name; +} + export default function FilePreview({ file, onClose }: FilePreviewProps) { const [content, setContent] = useState(null); const [loading, setLoading] = useState(false); @@ -87,8 +93,8 @@ export default function FilePreview({ file, onClose }: FilePreviewProps) {
{getIcon(previewType)} - - {file.name} + + {getFileDisplayName(file.name)} {formatSize(file.size)} @@ -139,7 +145,7 @@ export default function FilePreview({ file, onClose }: FilePreviewProps) { {previewType === 'audio' && (
-

{file.name}

+

{getFileDisplayName(file.name)}

)}