show local folders using core/disks
This commit is contained in:
@@ -334,6 +334,7 @@ const FilePanel = forwardRef<
|
||||
<RemoteSidebar
|
||||
position="left"
|
||||
selectedRemote={nav.selectedRemote}
|
||||
cwd={nav.cwd}
|
||||
onRemoteSelect={nav.selectRemote}
|
||||
allowedKeys={allowedKeys}
|
||||
remotes={nav.remotes}
|
||||
@@ -410,6 +411,7 @@ const FilePanel = forwardRef<
|
||||
<RemoteSidebar
|
||||
position="right"
|
||||
selectedRemote={nav.selectedRemote}
|
||||
cwd={nav.cwd}
|
||||
onRemoteSelect={nav.selectRemote}
|
||||
allowedKeys={allowedKeys}
|
||||
remotes={nav.remotes}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Button, ScrollShadow, Tooltip, cn } from '@heroui/react'
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { platform } from '@tauri-apps/plugin-os'
|
||||
import { LaptopIcon, StarIcon } from 'lucide-react'
|
||||
import { StarIcon } from 'lucide-react'
|
||||
import { useMemo } from 'react'
|
||||
import rclone from '../../../lib/rclone/client.ts'
|
||||
import type { AllowedKey, RemoteString } from './types'
|
||||
import { getDiskIcon, getDiskLabel, shouldShowDisk } from './utils'
|
||||
|
||||
function RemoteButton({
|
||||
remote,
|
||||
@@ -51,13 +53,15 @@ function RemoteButton({
|
||||
export default function RemoteSidebar({
|
||||
position,
|
||||
selectedRemote,
|
||||
cwd,
|
||||
onRemoteSelect,
|
||||
allowedKeys,
|
||||
remotes,
|
||||
}: {
|
||||
position: 'left' | 'right'
|
||||
selectedRemote: RemoteString
|
||||
onRemoteSelect: (remote: string | 'UI_LOCAL_FS' | 'UI_FAVORITES') => void
|
||||
cwd?: string
|
||||
onRemoteSelect: (remote: string | 'UI_LOCAL_FS' | 'UI_FAVORITES', initialPath?: string) => void
|
||||
allowedKeys: AllowedKey[]
|
||||
remotes: string[]
|
||||
}) {
|
||||
@@ -65,6 +69,24 @@ export default function RemoteSidebar({
|
||||
const canShowLocal = allowedKeys.includes('LOCAL_FS')
|
||||
const canShowRemotes = allowedKeys.includes('REMOTES')
|
||||
|
||||
const disksQuery = useQuery({
|
||||
queryKey: ['core', 'disks'],
|
||||
queryFn: () => rclone('/core/disks'),
|
||||
staleTime: 1000 * 60 * 5,
|
||||
enabled: canShowLocal,
|
||||
})
|
||||
|
||||
const disks = useMemo(
|
||||
() => (disksQuery.data?.disks ?? []).filter(shouldShowDisk),
|
||||
[disksQuery.data]
|
||||
)
|
||||
|
||||
const activeDisk = useMemo(() => {
|
||||
if (selectedRemote !== 'UI_LOCAL_FS' || !cwd) return null
|
||||
const sorted = [...disks].sort((a, b) => b.length - a.length)
|
||||
return sorted.find((disk) => cwd.startsWith(disk)) ?? null
|
||||
}, [selectedRemote, cwd, disks])
|
||||
|
||||
const orderClass = position === 'right' ? 'order-last' : 'order-first'
|
||||
|
||||
return (
|
||||
@@ -94,24 +116,30 @@ export default function RemoteSidebar({
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{canShowLocal && (
|
||||
<Tooltip
|
||||
content="Local Filesystem"
|
||||
placement={position === 'left' ? 'right' : 'left'}
|
||||
color="foreground"
|
||||
size="lg"
|
||||
>
|
||||
<Button
|
||||
isIconOnly={true}
|
||||
className="shrink-0"
|
||||
size="lg"
|
||||
onPress={() => onRemoteSelect('UI_LOCAL_FS')}
|
||||
variant={selectedRemote === 'UI_LOCAL_FS' ? 'faded' : 'light'}
|
||||
>
|
||||
<LaptopIcon className="size-6" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)}
|
||||
{canShowLocal &&
|
||||
disks.map((disk) => {
|
||||
const { icon: DiskIcon, className: iconColor } = getDiskIcon(disk)
|
||||
const label = getDiskLabel(disk)
|
||||
return (
|
||||
<Tooltip
|
||||
key={disk}
|
||||
content={label}
|
||||
placement={position === 'left' ? 'right' : 'left'}
|
||||
color="foreground"
|
||||
size="lg"
|
||||
>
|
||||
<Button
|
||||
isIconOnly={true}
|
||||
className="shrink-0"
|
||||
size="lg"
|
||||
onPress={() => onRemoteSelect('UI_LOCAL_FS', disk)}
|
||||
variant={activeDisk === disk ? 'faded' : 'light'}
|
||||
>
|
||||
<DiskIcon className={cn('size-6', iconColor)} />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)
|
||||
})}
|
||||
{canShowRemotes &&
|
||||
remotes.map((remote) => (
|
||||
<RemoteButton
|
||||
|
||||
@@ -237,18 +237,18 @@ export default function useFileNavigation({
|
||||
)
|
||||
|
||||
const selectRemote = useCallback(
|
||||
async (remote: string | 'UI_LOCAL_FS' | 'UI_FAVORITES') => {
|
||||
async (remote: string | 'UI_LOCAL_FS' | 'UI_FAVORITES', initialPath?: string) => {
|
||||
cleanupSelectionForRemote(remote)
|
||||
if (remote === 'UI_LOCAL_FS') {
|
||||
const home = await homeDir()
|
||||
const startPath = initialPath ?? (await homeDir())
|
||||
startTransition(() => {
|
||||
setSelectedRemote(remote)
|
||||
setCwd(home)
|
||||
setCwd(startPath)
|
||||
})
|
||||
} else {
|
||||
startTransition(() => {
|
||||
setSelectedRemote(remote)
|
||||
setCwd('')
|
||||
setCwd(initialPath ?? '')
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
import { dirname, join, sep } from '@tauri-apps/api/path'
|
||||
import { readDir } from '@tauri-apps/plugin-fs'
|
||||
import type { LucideIcon } from 'lucide-react'
|
||||
import {
|
||||
DownloadIcon,
|
||||
FileTextIcon,
|
||||
HardDriveIcon,
|
||||
HouseIcon,
|
||||
MonitorIcon,
|
||||
UsbIcon,
|
||||
} from 'lucide-react'
|
||||
import { createRef } from 'react'
|
||||
import rclone from '../../../lib/rclone/client.ts'
|
||||
import type { SelectItem } from './types'
|
||||
@@ -177,6 +186,41 @@ export function getFileExtension(filename: string): string {
|
||||
return filename.slice(lastDot + 1).toLowerCase()
|
||||
}
|
||||
|
||||
export function getDiskLabel(disk: string): string {
|
||||
const last = disk.split(/[/\\]/).filter(Boolean).pop()
|
||||
return last ?? disk
|
||||
}
|
||||
|
||||
const SHOWN_DISKS = new Set(['desktop', 'documents', 'downloads'])
|
||||
|
||||
export function shouldShowDisk(disk: string): boolean {
|
||||
if (disk === '/' || /^[A-Z]:[\\/]?$/i.test(disk)) return true
|
||||
const last = disk.split(/[/\\]/).filter(Boolean).pop()?.toLowerCase()
|
||||
if (last && SHOWN_DISKS.has(last)) return true
|
||||
// Home folder: parent is a known users directory
|
||||
if (/[\\/](?:Users|home)[\\/][^/\\]+\/?$/i.test(disk)) return true
|
||||
// USB / external volumes
|
||||
if (/[\\/](?:media|Volumes|mnt)[\\/]/i.test(disk)) return true
|
||||
return false
|
||||
}
|
||||
|
||||
export function getDiskIcon(disk: string): { icon: LucideIcon; className: string } {
|
||||
const last = disk.split(/[/\\]/).filter(Boolean).pop()?.toLowerCase()
|
||||
switch (last) {
|
||||
case 'desktop':
|
||||
return { icon: MonitorIcon, className: 'text-sky-400' }
|
||||
case 'documents':
|
||||
return { icon: FileTextIcon, className: 'text-blue-400' }
|
||||
case 'downloads':
|
||||
return { icon: DownloadIcon, className: 'text-green-400' }
|
||||
}
|
||||
if (disk === '/' || /^[A-Z]:[\\/]?$/i.test(disk))
|
||||
return { icon: HardDriveIcon, className: 'text-zinc-400' }
|
||||
if (/[\\/](?:media|Volumes|mnt)[\\/]/i.test(disk))
|
||||
return { icon: UsbIcon, className: 'text-orange-400' }
|
||||
return { icon: HouseIcon, className: 'text-amber-400' }
|
||||
}
|
||||
|
||||
export function formatModTime(modTime: string | undefined): string {
|
||||
if (!modTime) return '—'
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user