This commit is contained in:
Timothy Jaeryang Baek
2026-05-09 01:31:42 +09:00
parent b72019db39
commit 6bdc2ffa79
4 changed files with 102 additions and 4 deletions
+34 -4
View File
@@ -92,6 +92,35 @@
let loading = false;
let error: string | null = null;
// ── Sort state ──────────────────────────────────────────────────────
type SortMode = 'name' | 'date';
let sortBy: SortMode = 'name';
let sortAsc = true;
const sortEntries = (items: FileEntry[]): FileEntry[] => {
return [...items].sort((a, b) => {
// Directories always first
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
if (sortBy === 'date') {
const aTime = a.modified ?? 0;
const bTime = b.modified ?? 0;
return sortAsc ? aTime - bTime : bTime - aTime;
}
const cmp = a.name.localeCompare(b.name);
return sortAsc ? cmp : -cmp;
});
};
const toggleSort = (mode: SortMode) => {
if (sortBy === mode) {
sortAsc = !sortAsc;
} else {
sortBy = mode;
sortAsc = mode === 'name'; // name defaults asc, date defaults asc (oldest first)
}
entries = sortEntries(entries);
};
// ── Navigation history ──────────────────────────────────────────────
type NavEntry = { path: string; file: string | null };
let navHistory: NavEntry[] = [];
@@ -341,10 +370,7 @@
'Failed to load directory. Check your Terminal connection in Settings → Integrations.';
entries = [];
} else {
entries = result.sort((a, b) => {
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
return a.name.localeCompare(b.name);
});
entries = sortEntries(result);
}
};
@@ -918,6 +944,8 @@
{loading}
{canGoBack}
{canGoForward}
{sortBy}
{sortAsc}
onGoBack={goBack}
onGoForward={goForward}
onNavigate={loadDir}
@@ -934,6 +962,7 @@
onUploadFiles={handleUploadFiles}
onDownloadDir={() => downloadFile(currentPath)}
onMove={handleMove}
onSort={toggleSort}
>
{#if fileImageUrl !== null || (fileOfficeSlides !== null && fileOfficeSlides.length > 0)}
<Tooltip content={$i18n.t('Reset view')}>
@@ -1355,6 +1384,7 @@
onRename={handleRename}
onSelect={handleSelect}
onLongPress={enterSelectionMode}
showDate={sortBy === 'date'}
/>
{/each}
</ul>
@@ -30,6 +30,17 @@
export let selectedPaths: Set<string> = new Set();
export let onSelect: (entry: FileEntry, event: MouseEvent) => void = () => {};
export let onLongPress: () => void = () => {};
export let showDate: boolean = false;
const formatRelativeTime = (epoch: number): string => {
const diff = Math.floor(Date.now() / 1000) - epoch;
if (diff < 60) return 'just now';
if (diff < 3600) return `${Math.floor(diff / 60)}m ago`;
if (diff < 86400) return `${Math.floor(diff / 3600)}h ago`;
if (diff < 2592000) return `${Math.floor(diff / 86400)}d ago`;
if (diff < 31536000) return `${Math.floor(diff / 2592000)}mo ago`;
return `${Math.floor(diff / 31536000)}y ago`;
};
let dragOverFolder = false;
@@ -271,7 +282,12 @@
</span>
{/if}
{#if entry.type === 'file' && entry.size !== undefined && !renaming}
{#if showDate && entry.modified}
<span class="text-[10px] text-gray-400 shrink-0">{formatRelativeTime(entry.modified)}</span>
{/if}
<span class="text-xs text-gray-400 shrink-0">{formatFileSize(entry.size)}</span>
{:else if entry.type === 'directory' && showDate && entry.modified && !renaming}
<span class="text-[10px] text-gray-400 shrink-0">{formatRelativeTime(entry.modified)}</span>
{/if}
</button>
@@ -6,6 +6,7 @@
import FilePlusAlt from '../../icons/FilePlusAlt.svelte';
import Spinner from '../../common/Spinner.svelte';
import Tooltip from '../../common/Tooltip.svelte';
import Dropdown from '$lib/components/common/Dropdown.svelte';
const i18n = getContext('i18n');
@@ -21,6 +22,11 @@
export let onDownloadDir: () => void = () => {};
export let onMove: (source: string, destFolder: string) => void = () => {};
// Sort controls
export let sortBy: 'name' | 'date' = 'name';
export let sortAsc: boolean = true;
export let onSort: (mode: 'name' | 'date') => void = () => {};
// Back / forward navigation
export let canGoBack = false;
export let canGoForward = false;
@@ -161,6 +167,49 @@
</Tooltip>
{#if !selectedFile}
<Dropdown align="end" sideOffset={4}>
<Tooltip content={$i18n.t('Sort')}>
<button
class="shrink-0 p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400"
aria-label={$i18n.t('Sort')}
>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3.5">
<path d="M2 3.75A.75.75 0 0 1 2.75 3h11.5a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 3.75ZM2 7.5a.75.75 0 0 1 .75-.75h7.508a.75.75 0 0 1 0 1.5H2.75A.75.75 0 0 1 2 7.5ZM14 7a.75.75 0 0 1 .75.75v6.69l1.72-1.72a.75.75 0 1 1 1.06 1.06l-3 3a.75.75 0 0 1-1.06 0l-3-3a.75.75 0 1 1 1.06-1.06l1.72 1.72V7.75A.75.75 0 0 1 14 7ZM2 11.25a.75.75 0 0 1 .75-.75h4.562a.75.75 0 0 1 0 1.5H2.75a.75.75 0 0 1-.75-.75Z" />
</svg>
</button>
</Tooltip>
<div slot="content">
<div
class="min-w-[150px] rounded-2xl p-1 z-[9999999] bg-white dark:bg-gray-850 dark:text-white shadow-lg border border-gray-100 dark:border-gray-800"
>
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
on:click={() => onSort('name')}
>
<span class="flex-1 text-left">{$i18n.t('Name')}</span>
{#if sortBy === 'name'}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-3 text-gray-500 dark:text-gray-400 transition-transform {sortAsc ? '' : 'rotate-180'}">
<path fill-rule="evenodd" d="M11.78 9.78a.75.75 0 0 1-1.06 0L8 7.06 5.28 9.78a.75.75 0 0 1-1.06-1.06l3.25-3.25a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06Z" clip-rule="evenodd" />
</svg>
{/if}
</button>
<button
type="button"
class="select-none flex rounded-xl py-1.5 px-3 w-full hover:bg-gray-50 dark:hover:bg-gray-800 transition items-center gap-2 text-sm"
on:click={() => onSort('date')}
>
<span class="flex-1 text-left">{$i18n.t('Date Modified')}</span>
{#if sortBy === 'date'}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-3 text-gray-500 dark:text-gray-400 transition-transform {sortAsc ? '' : 'rotate-180'}">
<path fill-rule="evenodd" d="M11.78 9.78a.75.75 0 0 1-1.06 0L8 7.06 5.28 9.78a.75.75 0 0 1-1.06-1.06l3.25-3.25a.75.75 0 0 1 1.06 0l3.25 3.25a.75.75 0 0 1 0 1.06Z" clip-rule="evenodd" />
</svg>
{/if}
</button>
</div>
</div>
</Dropdown>
<Tooltip content={$i18n.t('New Folder')}>
<button
class="shrink-0 p-1 rounded hover:bg-gray-100 dark:hover:bg-gray-800 transition text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-400"
@@ -497,6 +497,7 @@
"Danger Zone": "",
"Dark": "",
"Data Controls": "",
"Date Modified": "",
"Database": "",
"Datalab Marker API": "",
"Day": "",
@@ -1900,6 +1901,8 @@
"Sonar Reasoning Pro": "",
"Sort": "",
"Sort by": "",
"Sort by date": "",
"Sort by name": "",
"Sougou Search API sID": "",
"Sougou Search API SK": "",
"Source": "",