feat: refine sidebar UI and update download categories

- Restructured layout so the sidebar acts as an edge-to-edge curved layer with a shadow
- Replaced old download categories with Musics, Movies, Compressed, Documents, Pictures, Applications, and Other
- Updated Settings > Locations tab to handle the new categories
- Added extensive predefined file extensions for smart auto-categorization of downloads
This commit is contained in:
NimBold
2026-06-13 00:02:07 +03:30
parent 610eda203e
commit dc9289fece
10 changed files with 137 additions and 48 deletions
@@ -8,12 +8,12 @@ import { invoke } from '@tauri-apps/api/core';
function determineCategory(fileName: string): DownloadCategory {
const ext = fileName.split('.').pop()?.toLowerCase() || '';
if (['mp4', 'mkv', 'webm', 'avi', 'mov', 'flv'].includes(ext)) return 'Video';
if (['mp3', 'm4a', 'wav', 'flac', 'ogg', 'aac'].includes(ext)) return 'Audio';
if (['mp4', 'mkv', 'avi', 'mov', 'wmv', 'flv', 'webm', 'm4v'].includes(ext)) return 'Movies';
if (['mp3', 'wav', 'aac', 'flac', 'ogg', 'm4a', 'wma'].includes(ext)) return 'Musics';
if (['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'rtf'].includes(ext)) return 'Documents';
if (['exe', 'dmg', 'pkg', 'app', 'apk', 'deb', 'rpm'].includes(ext)) return 'Apps';
if (['jpg', 'jpeg', 'png', 'gif', 'svg', 'webp', 'bmp', 'tiff'].includes(ext)) return 'Images';
if (['zip', 'rar', '7z', 'tar', 'gz', 'bz2', 'xz'].includes(ext)) return 'Archives';
if (['exe', 'dmg', 'apk', 'app', 'pkg', 'deb', 'rpm', 'msi', 'iso', 'bin', 'run'].includes(ext)) return 'Applications';
if (['jpg', 'jpeg', 'png', 'gif', 'webp', 'bmp', 'tiff', 'svg'].includes(ext)) return 'Pictures';
if (['zip', 'rar', '7z', 'tar', 'gz', 'xz', 'bz2'].includes(ext)) return 'Compressed';
return 'Other';
}
+8 -12
View File
@@ -99,18 +99,19 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter }) => {
const getCategoryIcon = (category: string) => {
switch(category) {
case 'Musics': return <Music size={16} className="text-pink-400" />;
case 'Movies': return <Film size={16} className="text-red-400" />;
case 'Documents': return <FileText size={16} className="text-blue-400" />;
case 'Images': return <ImageIcon size={16} className="text-purple-400" />;
case 'Audio': return <Music size={16} className="text-pink-400" />;
case 'Video': return <Film size={16} className="text-red-400" />;
case 'Apps': return <Box size={16} className="text-orange-400" />;
case 'Archives': return <Archive size={16} className="text-yellow-400" />;
case 'Applications': return <Box size={16} className="text-indigo-400" />;
case 'Pictures': return <ImageIcon size={16} className="text-purple-400" />;
case 'Compressed': return <Archive size={16} className="text-amber-600" />;
case 'Other': return <FileQuestion size={16} className="text-gray-400" />;
default: return <FileQuestion size={16} className="text-gray-400" />;
}
}
return (
<div className="flex-1 flex flex-col bg-main-bg h-full relative">
<div className="flex-1 flex flex-col bg-transparent h-full relative">
<div className="glass-panel shrink-0 border-b border-border-color/60">
<WindowDragRegion className={!isSidebarVisible ? 'pl-24' : ''} />
@@ -163,7 +164,7 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter }) => {
</div>
{/* Table */}
<div className="flex-1 overflow-auto bg-main-bg">
<div className="flex-1 overflow-auto bg-transparent">
<table className="w-full border-collapse text-left">
<thead className="sticky top-0 bg-main-bg z-0 border-b border-border-color">
<tr className="text-text-muted/60 text-[10px] font-bold tracking-widest uppercase">
@@ -269,11 +270,6 @@ export const DownloadTable: React.FC<DownloadTableProps> = ({ filter }) => {
</table>
</div>
{/* Status Bar */}
<div className="px-6 py-2 border-t border-border-color text-[11px] font-medium text-text-muted bg-sidebar-bg/50 backdrop-blur-md">
{downloads.length} Item{downloads.length !== 1 ? 's' : ''}
</div>
{/* Floating Context Menu */}
{contextMenu && contextItem && (
<div
+11 -12
View File
@@ -95,21 +95,20 @@ export default function SettingsView() {
const handleBrowseBulk = async () => {
try {
const selected = await open({
const base = await open({
directory: true,
multiple: false
});
if (selected && typeof selected === 'string') {
// Automatically populate all category folders
const cleanBase = selected.endsWith('/') ? selected.slice(0, -1) : selected;
settings.setCategoryDirectory('Video', `${cleanBase}/Video`);
settings.setCategoryDirectory('Audio', `${cleanBase}/Audio`);
if (base && typeof base === 'string') {
const cleanBase = base.replace(/\/$/, '');
settings.setCategoryDirectory('Musics', `${cleanBase}/Musics`);
settings.setCategoryDirectory('Movies', `${cleanBase}/Movies`);
settings.setCategoryDirectory('Compressed', `${cleanBase}/Compressed`);
settings.setCategoryDirectory('Documents', `${cleanBase}/Documents`);
settings.setCategoryDirectory('Apps', `${cleanBase}/Apps`);
settings.setCategoryDirectory('Images', `${cleanBase}/Images`);
settings.setCategoryDirectory('Archives', `${cleanBase}/Archives`);
settings.setCategoryDirectory('Pictures', `${cleanBase}/Pictures`);
settings.setCategoryDirectory('Applications', `${cleanBase}/Applications`);
settings.setCategoryDirectory('Other', `${cleanBase}/Other`);
showToast("Created subfolders for all categories");
showToast("Updated all categories to use base folder");
}
} catch (e) {
console.error("Failed to browse base path:", e);
@@ -492,13 +491,13 @@ export default function SettingsView() {
</div>
</div>
{Object.keys(settings.downloadDirectories || {}).map((category) => (
{['Musics', 'Movies', 'Compressed', 'Documents', 'Pictures', 'Applications', 'Other'].map((category) => (
<div key={category} className="grid grid-cols-[150px_1fr] items-center gap-4 text-[13px]">
<label className="text-text-secondary capitalize">{category} folder:</label>
<div className="flex gap-2">
<input
type="text"
value={(settings.downloadDirectories || {})[category]}
value={(settings.downloadDirectories || {})[category] || ''}
onChange={(e) => settings.setCategoryDirectory(category, e.target.value)}
className="flex-1 bg-bg-input border border-border-modal rounded-md px-3 py-1 text-xs text-text-primary font-mono"
/>
+6 -6
View File
@@ -168,7 +168,7 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
};
return (
<aside className="w-[220px] min-w-[190px] max-w-[260px] bg-sidebar-bg border-r border-border-color flex flex-col relative shrink-0">
<aside className="w-full h-full flex flex-col relative shrink-0">
<WindowDragRegion />
<div className="overflow-y-auto flex-1 px-2 pb-3">
<section className="mb-4">
@@ -181,12 +181,12 @@ export const Sidebar: React.FC<SidebarProps> = (props) => {
<section className="mb-4">
<div className="text-[11px] font-semibold text-text-muted px-2.5 mb-1.5">Folders</div>
<NavItem icon={Film} label="Video" filter="Video" />
<NavItem icon={Music} label="Audio" filter="Audio" />
<NavItem icon={Music} label="Musics" filter="Musics" />
<NavItem icon={Film} label="Movies" filter="Movies" />
<NavItem icon={Archive} label="Compressed" filter="Compressed" />
<NavItem icon={FileText} label="Documents" filter="Documents" />
<NavItem icon={Box} label="Apps" filter="Apps" />
<NavItem icon={ImageIcon} label="Images" filter="Images" />
<NavItem icon={Archive} label="Archives" filter="Archives" />
<NavItem icon={ImageIcon} label="Pictures" filter="Pictures" />
<NavItem icon={Box} label="Applications" filter="Applications" />
<NavItem icon={FileQuestion} label="Other" filter="Other" />
</section>