feat(MediaBrowser, MediaManager): enhance file type handling and improve icon representation

This commit is contained in:
Jaime Idolpx 2026-06-08 13:47:26 -04:00
parent b70c98d69a
commit d2440c5ba1
2 changed files with 82 additions and 54 deletions

View File

@ -1,20 +1,30 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import { import {
Folder, ArrowLeft,
BookOpen,
Braces,
CassetteTape,
Check,
ChevronRight,
Code2,
Cpu,
Disc,
Save,
File, File,
FileText, FileText,
HardDrive, Folder,
Image as ImageIcon,
ChevronRight,
Home,
RefreshCw,
Upload,
FolderPlus, FolderPlus,
ArrowLeft, HardDrive,
Home,
Image as ImageIcon,
Loader2, Loader2,
MoreVertical, MoreVertical,
Check, Music,
Package,
RefreshCw,
SlidersHorizontal,
Trash2, Trash2,
Upload,
} from 'lucide-react'; } from 'lucide-react';
import { import {
createFolder, createFolder,
@ -37,17 +47,37 @@ import {
DialogDescription, DialogDescription,
} from './ui/dialog'; } from './ui/dialog';
const TEXT_EXTS = new Set(['txt','cfg','ini','bas','asm','seq','rel','prg','log','csv','s','lst','md','markdown','json','xml','svg','html','htm']); const TEXT_EXTS = new Set(['txt','cfg','ini','bas','asm','seq','rel','prg','log','csv','s','lst']);
const IMAGE_EXTS = new Set(['png','jpg','jpeg','gif','bmp','webp']); const MD_EXTS = new Set(['md','markdown']);
const DISK_EXTS = new Set(['d64','d71','d81','d82','g64','g71','t64','tap','crt','nib']); const JSON_EXTS = new Set(['json']);
const XML_EXTS = new Set(['xml','svg','html','htm','rss','atom','xsl']);
const IMAGE_EXTS = new Set(['png','jpg','jpeg','gif','bmp','webp']);
const AUDIO_EXTS = new Set(['sid','psid','mus','vgm']);
const ROM_EXTS = new Set(['bin','rom','crt']);
const TAPE_EXTS = new Set(['tap','t64','tcrt']);
const DISK_EXTS = new Set(['d41','d64','d71','d80','d81','d82','d8b','dfi','g64','g71','g81','p64','p71','p81','nib']);
const DISC_EXTS = new Set(['iso','img','cue']);
const HD_EXTS = new Set(['d1m','d2m','d4m','d90','dhd','hdd']);
const ARCHIVE_EXTS = new Set(['zip','7z','tar','gz','bz2','xz','rar','arj','lzh','ace','z','lha','cab','lbr','arc','ark','lnx']);
const CONFIG_EXTS = new Set(['config']);
function EntryIcon({ entry }: { entry: EntryInfo }) { function EntryIcon({ entry }: { entry: EntryInfo }) {
if (entry.type === 'folder') return <Folder className="w-5 h-5 text-blue-500 flex-shrink-0" />; if (entry.type === 'folder') return <Folder className="w-5 h-5 text-blue-500 flex-shrink-0" />;
const ext = entry.name.split('.').pop()?.toLowerCase() ?? ''; const ext = entry.name.split('.').pop()?.toLowerCase() ?? '';
if (IMAGE_EXTS.has(ext)) return <ImageIcon className="w-5 h-5 text-purple-500 flex-shrink-0" />; if (IMAGE_EXTS.has(ext)) return <ImageIcon className="w-5 h-5 text-purple-500 flex-shrink-0" />;
if (DISK_EXTS.has(ext)) return <HardDrive className="w-5 h-5 text-amber-500 flex-shrink-0" />; if (DISK_EXTS.has(ext)) return <Save className="w-5 h-5 text-amber-500 flex-shrink-0" />;
if (TEXT_EXTS.has(ext)) return <FileText className="w-5 h-5 text-green-600 flex-shrink-0" />; if (HD_EXTS.has(ext)) return <HardDrive className="w-5 h-5 text-orange-500 flex-shrink-0" />;
return <File className="w-5 h-5 text-neutral-400 flex-shrink-0" />; if (DISC_EXTS.has(ext)) return <Disc className="w-5 h-5 text-sky-500 flex-shrink-0" />;
if (TAPE_EXTS.has(ext)) return <CassetteTape className="w-5 h-5 text-rose-400 flex-shrink-0" />;
if (ROM_EXTS.has(ext)) return <Cpu className="w-5 h-5 text-red-500 flex-shrink-0" />;
if (AUDIO_EXTS.has(ext)) return <Music className="w-5 h-5 text-teal-500 flex-shrink-0" />;
if (ARCHIVE_EXTS.has(ext)) return <Package className="w-5 h-5 text-yellow-600 flex-shrink-0" />;
if (CONFIG_EXTS.has(ext)) return <SlidersHorizontal className="w-5 h-5 text-slate-400 flex-shrink-0" />;
if (JSON_EXTS.has(ext)) return <Braces className="w-5 h-5 text-yellow-500 flex-shrink-0" />;
if (XML_EXTS.has(ext)) return <Code2 className="w-5 h-5 text-cyan-500 flex-shrink-0" />;
if (MD_EXTS.has(ext)) return <BookOpen className="w-5 h-5 text-sky-400 flex-shrink-0" />;
if (TEXT_EXTS.has(ext)) return <FileText className="w-5 h-5 text-green-600 flex-shrink-0" />;
return <File className="w-5 h-5 text-neutral-400 flex-shrink-0" />;
} }
interface MediaBrowserProps { interface MediaBrowserProps {

View File

@ -4,6 +4,7 @@ import {
ArrowLeft, ArrowLeft,
BookOpen, BookOpen,
Braces, Braces,
CassetteTape,
Check, Check,
CheckSquare, CheckSquare,
ChevronLeft, ChevronLeft,
@ -11,6 +12,8 @@ import {
ClipboardPaste, ClipboardPaste,
Code2, Code2,
Copy, Copy,
Cpu,
Disc,
Download, Download,
Eye, Eye,
File, File,
@ -25,6 +28,8 @@ import {
Loader2, Loader2,
MoreVertical, MoreVertical,
Move, Move,
Music,
Package,
SlidersHorizontal, SlidersHorizontal,
Pencil, Pencil,
RefreshCw, RefreshCw,
@ -70,40 +75,23 @@ import {
type SortKey = 'name' | 'size' | 'date'; type SortKey = 'name' | 'size' | 'date';
type Clipboard = { op: 'copy' | 'move'; paths: string[] }; type Clipboard = { op: 'copy' | 'move'; paths: string[] };
type FileCategory = 'text' | 'image' | 'disk' | 'binary'; type ViewMode = 'text' | 'markdown' | 'json' | 'xml' | 'hex' | 'image' | 'config';
type ViewMode = 'text' | 'markdown' | 'json' | 'xml' | 'hex' | 'image' | 'config';
// ─── Extension sets ────────────────────────────────────────────────────────── // ─── Extension sets ──────────────────────────────────────────────────────────
const TEXT_EXTS = new Set(['txt', 'cfg', 'ini', 'bas', 'asm', 'seq', 'rel', 'prg', 'log', 'csv', 's', 'lst']); const TEXT_EXTS = new Set(['txt', 'cfg', 'ini', 'bas', 'asm', 'seq', 'rel', 'prg', 'log', 'csv', 's', 'lst']);
const MD_EXTS = new Set(['md', 'markdown']); const MD_EXTS = new Set(['md', 'markdown']);
const JSON_EXTS = new Set(['json']); const JSON_EXTS = new Set(['json']);
const XML_EXTS = new Set(['xml', 'svg', 'html', 'htm', 'rss', 'atom', 'xsl']); const XML_EXTS = new Set(['xml', 'svg', 'html', 'htm', 'rss', 'atom', 'xsl']);
const IMAGE_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp']); const IMAGE_EXTS = new Set(['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp']);
const AUDIO_EXTS = new Set(['sid', 'psid', 'mus', 'vgm']); const AUDIO_EXTS = new Set(['sid', 'psid', 'mus', 'vgm']);
const ROM_EXTS = new Set(['bin', 'rom', 'crt']); const ROM_EXTS = new Set(['bin', 'rom', 'crt']);
const TAPE_EXTS = new Set(['tap', 't64', 'tcrt']); const TAPE_EXTS = new Set(['tap', 'htap', 't64', 'tcrt']);
const DISK_EXTS = new Set(['d41', 'd64', 'd71', 'd80', 'd81', 'd82', 'd90', 'd8b', 'dfi', 'g64', 'g71', 'g81', 'p64', 'p71', 'p81', 'nib']); const DISK_EXTS = new Set(['d41', 'd64', 'd71', 'd80', 'd81', 'd82', 'g64', 'g71', 'g81', 'p64', 'p71', 'p81', 'nib']);
const DISC_EXTS = new Set(['iso', 'img', 'cue']); const DISC_EXTS = new Set(['iso', 'img', 'cue']);
const HD_EXTS = new Set(['d1m', 'd2m', 'd4m', 'dhd', 'hdd']); const HD_EXTS = new Set(['d1m', 'd2m', 'd4m', 'd90', 'dhd', 'hdd']);
const ARCHIVE_EXTS = new Set(['zip', '7z', 'tar', 'gz', 'bz2', 'xz', 'rar', 'arj', 'lzh', 'ace', 'z', 'lha', 'cab', 'lbr', 'arc', 'ark', 'lnx']); const ARCHIVE_EXTS = new Set(['zip', '7z', 'tar', 'gz', 'bz2', 'xz', 'rar', 'arj', 'lzh', 'ace', 'z', 'lha', 'cab', 'lbr', 'arc', 'ark', 'lnx', 'bbt', 'd8b', 'dfi']);
const CONFIG_EXTS = new Set(['config']); const CONFIG_EXTS = new Set(['config']);
function fileCategory(entry: EntryInfo): FileCategory {
if (entry.type === 'folder') return 'binary';
const ext = entry.name.split('.').pop()?.toLowerCase() ?? '';
if (IMAGE_EXTS.has(ext)) return 'image';
if (DISK_EXTS.has(ext)) return 'disk';
if (HD_EXTS.has(ext)) return 'disk';
if (ROM_EXTS.has(ext)) return 'disk';
if (TAPE_EXTS.has(ext)) return 'disk';
if (AUDIO_EXTS.has(ext)) return 'disk';
if (ARCHIVE_EXTS.has(ext)) return 'disk';
if (DISC_EXTS.has(ext)) return 'disk';
if (CONFIG_EXTS.has(ext)) return 'text';
if (TEXT_EXTS.has(ext) || MD_EXTS.has(ext) || JSON_EXTS.has(ext) || XML_EXTS.has(ext)) return 'text';
return 'binary';
}
function defaultViewMode(entry: EntryInfo): ViewMode { function defaultViewMode(entry: EntryInfo): ViewMode {
const ext = entry.name.split('.').pop()?.toLowerCase() ?? ''; const ext = entry.name.split('.').pop()?.toLowerCase() ?? '';
@ -258,12 +246,22 @@ function MarkdownEditor({ text, onSave }: { text: string; onSave?: (s: string) =
// ─── Entry icon ─────────────────────────────────────────────────────────────── // ─── Entry icon ───────────────────────────────────────────────────────────────
function EntryIcon({ entry }: { entry: EntryInfo }) { function EntryIcon({ entry }: { entry: EntryInfo }) {
if (entry.type === 'folder') return <Folder className="w-5 h-5 text-blue-500 flex-shrink-0" />; if (entry.type === 'folder') return <Folder className="w-5 h-5 text-blue-500 flex-shrink-0" />;
const cat = fileCategory(entry); const ext = entry.name.split('.').pop()?.toLowerCase() ?? '';
if (cat === 'image') return <ImageIcon className="w-5 h-5 text-purple-500 flex-shrink-0" />; if (IMAGE_EXTS.has(ext)) return <ImageIcon className="w-5 h-5 text-purple-500 flex-shrink-0" />;
if (cat === 'disk') return <HardDrive className="w-5 h-5 text-amber-500 flex-shrink-0" />; if (DISK_EXTS.has(ext)) return <Save className="w-5 h-5 text-amber-500 flex-shrink-0" />;
if (cat === 'text') return <FileText className="w-5 h-5 text-green-600 flex-shrink-0" />; if (HD_EXTS.has(ext)) return <HardDrive className="w-5 h-5 text-orange-500 flex-shrink-0" />;
return <File className="w-5 h-5 text-neutral-400 flex-shrink-0" />; if (DISC_EXTS.has(ext)) return <Disc className="w-5 h-5 text-sky-500 flex-shrink-0" />;
if (TAPE_EXTS.has(ext)) return <CassetteTape className="w-5 h-5 text-rose-400 flex-shrink-0" />;
if (ROM_EXTS.has(ext)) return <Cpu className="w-5 h-5 text-red-500 flex-shrink-0" />;
if (AUDIO_EXTS.has(ext)) return <Music className="w-5 h-5 text-teal-500 flex-shrink-0" />;
if (ARCHIVE_EXTS.has(ext)) return <Package className="w-5 h-5 text-yellow-600 flex-shrink-0" />;
if (CONFIG_EXTS.has(ext)) return <SlidersHorizontal className="w-5 h-5 text-slate-400 flex-shrink-0" />;
if (JSON_EXTS.has(ext)) return <Braces className="w-5 h-5 text-yellow-500 flex-shrink-0" />;
if (XML_EXTS.has(ext)) return <Code2 className="w-5 h-5 text-cyan-500 flex-shrink-0" />;
if (MD_EXTS.has(ext)) return <BookOpen className="w-5 h-5 text-sky-400 flex-shrink-0" />;
if (TEXT_EXTS.has(ext)) return <FileText className="w-5 h-5 text-green-600 flex-shrink-0" />;
return <File className="w-5 h-5 text-neutral-400 flex-shrink-0" />;
} }
// ─── ActionsModal ───────────────────────────────────────────────────────────── // ─── ActionsModal ─────────────────────────────────────────────────────────────