bug fixes galore

This commit is contained in:
FTCHD
2026-05-10 22:37:26 +03:00
parent d749844073
commit c1516bda65
28 changed files with 199 additions and 238 deletions
+1 -1
View File
@@ -110,7 +110,7 @@ export function groupByCategory(
const category = getFlagCategory(k, allFlags) const category = getFlagCategory(k, allFlags)
if (!category) continue if (!category) continue
if (category.category.startsWith('serve.')) { if (category.category.startsWith('serve.')) {
const serveType = category.category.slice(5) as (typeof SERVE_TYPES)[number] const serveType = category.category.slice(6) as (typeof SERVE_TYPES)[number]
collectedFlags.serve[serveType] = { collectedFlags.serve[serveType] = {
...collectedFlags.serve[serveType], ...collectedFlags.serve[serveType],
[k]: v, [k]: v,
+2
View File
@@ -6,6 +6,8 @@ const RE_LOCAL_WINDOWS_PATH = /^:local:([a-zA-Z]:\/?.*)$/
const RE_PATH_SEPARATOR = /[/\\]/ const RE_PATH_SEPARATOR = /[/\\]/
export function formatBytes(bytes: number) { export function formatBytes(bytes: number) {
if (!Number.isFinite(bytes)) return '0 B'
if (bytes < 1024) { if (bytes < 1024) {
return `${Math.round(bytes)} B` return `${Math.round(bytes)} B`
} }
+10 -12
View File
@@ -50,18 +50,16 @@ export function useFlags() {
const serveFlags = SERVE_TYPES.reduce( const serveFlags = SERVE_TYPES.reduce(
(acc, type) => { (acc, type) => {
acc[type] = acc[type] = (allFlags?.[type] || [])
allFlags?.[type] || .map((flag: any) => ({
[] ...flag,
.map((flag: any) => ({ FieldName: flag.Name,
...flag, DefaultStr:
FieldName: flag.Name, flag.Name === 'addr'
DefaultStr: ? flag.DefaultStr.replace('[', '').replace(']', '')
flag.Name === 'addr' : flag.DefaultStr,
? flag.DefaultStr.replace('[', '').replace(']', '') }))
: flag.DefaultStr, .sort((a: any, b: any) => a.Name.localeCompare(b.Name))
}))
.sort((a: any, b: any) => a.Name.localeCompare(b.Name))
return acc return acc
}, },
{} as Record<(typeof SERVE_TYPES)[number], any[]> {} as Record<(typeof SERVE_TYPES)[number], any[]>
+3 -3
View File
@@ -892,7 +892,7 @@ export async function startBisync({
{ {
retries: 3, retries: 3,
} }
).catch(null) ).catch(() => null)
console.log('jobStatus', JSON.stringify(jobStatus, null, 2)) console.log('jobStatus', JSON.stringify(jobStatus, null, 2))
@@ -988,7 +988,7 @@ export async function startSync({
{ {
retries: 3, retries: 3,
} }
).catch(null) ).catch(() => null)
console.log('jobStatus', JSON.stringify(jobStatus, null, 2)) console.log('jobStatus', JSON.stringify(jobStatus, null, 2))
@@ -1229,7 +1229,7 @@ export async function startBatch(inputs: ({ _path: string } & Record<string, any
{ {
retries: 3, retries: 3,
} }
).catch(null) ).catch(() => null)
console.log('[startBatch] jobStatus', { console.log('[startBatch] jobStatus', {
jobid: r.jobid, jobid: r.jobid,
+4 -6
View File
@@ -231,15 +231,13 @@ export function parseRcloneVersion(output: string) {
} }
export function shouldUpdateRclone(versionData: { yours: string; latest: string } | null) { export function shouldUpdateRclone(versionData: { yours: string; latest: string } | null) {
if (!versionData?.yours) return false
if (!versionData) { if (!versionData) {
console.warn('[shouldUpdateRclone] received no version data:', versionData) console.warn('[shouldUpdateRclone] received no version data:', versionData)
return false return false
} }
const currentVersion = versionData.yours const currentVersion = versionData?.yours
const latestVersion = versionData.latest const latestVersion = versionData?.latest
if (!currentVersion || !latestVersion) { if (!currentVersion || !latestVersion) {
console.warn('[shouldUpdateRclone] could not parse version output:', versionData) console.warn('[shouldUpdateRclone] could not parse version output:', versionData)
@@ -249,8 +247,8 @@ export function shouldUpdateRclone(versionData: { yours: string; latest: string
console.log('[shouldUpdateRclone] current version:', currentVersion) console.log('[shouldUpdateRclone] current version:', currentVersion)
console.log('[shouldUpdateRclone] latest version:', latestVersion) console.log('[shouldUpdateRclone] latest version:', latestVersion)
if (useHostStore.getState().lastSkippedVersion === currentVersion) { if (useHostStore.getState().lastSkippedVersion === latestVersion) {
console.log('[shouldUpdateRclone] current version is in the lastSkippedVersion') console.log('[shouldUpdateRclone] latest version is in the lastSkippedVersion')
return false return false
} }
+5 -2
View File
@@ -636,6 +636,11 @@ async function resumeTasks() {
isEnabled: task.isEnabled, isEnabled: task.isEnabled,
}) })
if (!task.isEnabled) {
console.log('[resumeTasks] task', task.id, 'is disabled, skipping')
continue
}
if (task.isRunning) { if (task.isRunning) {
console.log('[resumeTasks] task', task.id, 'was marked as running, resetting state') console.log('[resumeTasks] task', task.id, 'was marked as running, resetting state')
useHostStore.getState().updateScheduledTask(task.id, { useHostStore.getState().updateScheduledTask(task.id, {
@@ -866,8 +871,6 @@ async function handleTask(task: ScheduledTask) {
console.error('[handleTask] task', task.id, 'failed with error:', err) console.error('[handleTask] task', task.id, 'failed with error:', err)
console.error('[handleTask] task args were:', JSON.stringify(task.args, null, 2)) console.error('[handleTask] task args were:', JSON.stringify(task.args, null, 2))
useHostStore.getState().updateScheduledTask(task.id, { useHostStore.getState().updateScheduledTask(task.id, {
isRunning: false,
currentRunId: undefined,
lastRunError: err instanceof Error ? err.message : 'Unknown error', lastRunError: err instanceof Error ? err.message : 'Unknown error',
}) })
} finally { } finally {
+6 -1
View File
@@ -2,7 +2,7 @@ import { Button, Input, Select, SelectItem } from '@heroui/react'
import cronstrue from 'cronstrue' import cronstrue from 'cronstrue'
import { ClockIcon, XIcon } from 'lucide-react' import { ClockIcon, XIcon } from 'lucide-react'
import type React from 'react' import type React from 'react'
import { startTransition, useCallback, useEffect, useMemo, useState } from 'react' import { startTransition, useCallback, useEffect, useMemo, useRef, useState } from 'react'
interface CronEditorProps { interface CronEditorProps {
expression: string | null expression: string | null
@@ -51,7 +51,12 @@ export default function CronEditor({ expression, onChange }: CronEditorProps) {
[cronExpression] [cronExpression]
) )
const hasMounted = useRef(false)
useEffect(() => { useEffect(() => {
if (!hasMounted.current) {
hasMounted.current = true
return
}
onChange(cronExpression) onChange(cronExpression)
}, [cronExpression, onChange]) }, [cronExpression, onChange])
+5 -4
View File
@@ -264,10 +264,11 @@ export function PathField({
if (!item) { if (!item) {
return return
} }
item.type === 'folder' && !item.path.endsWith('/') && !item.path.endsWith('\\') setPath(
? `${item.path}${sep()}` item.type === 'folder' && !item.path.endsWith('/') && !item.path.endsWith('\\')
: item.path ? `${item.path}${sep()}`
setPath(item.path) : item.path
)
}} }}
isOpen={isOpen} isOpen={isOpen}
initialPaths={path ? [path] : []} initialPaths={path ? [path] : []}
+4 -3
View File
@@ -271,10 +271,11 @@ export default function RemoteCreateDrawer({
data-focus-visible="false" data-focus-visible="false"
onPress={() => { onPress={() => {
setTimeout(() => { setTimeout(() => {
const { name, type, ...parameters } = config
createRemoteMutation.mutate({ createRemoteMutation.mutate({
name: config.name, name,
type: config.type, type,
parameters: config, parameters,
}) })
}, 10) }, 10)
}} }}
+1 -1
View File
@@ -186,7 +186,7 @@ export default function TemplateAddDrawer({
const parsedNumber = Number(v) const parsedNumber = Number(v)
if (!isNaN(parsedNumber)) v = parsedNumber if (!isNaN(parsedNumber)) v = parsedNumber
if (value.includes(',')) v = value.split(',') if (value?.includes(',')) v = value.split(',')
flagGroups[flag.trim()] = v flagGroups[flag.trim()] = v
} }
@@ -78,11 +78,14 @@ export default function PreviewDrawer({
return return
} }
const abortController = new AbortController()
setIsLoadingText(true) setIsLoadingText(true)
setTextError(null) setTextError(null)
fetch(previewUrl, { fetch(previewUrl, {
headers: auth ? { Authorization: `Basic ${auth}` } : undefined, headers: auth ? { Authorization: `Basic ${auth}` } : undefined,
signal: abortController.signal,
}) })
.then((res) => { .then((res) => {
if (!res.ok) throw new Error(`HTTP ${res.status}`) if (!res.ok) throw new Error(`HTTP ${res.status}`)
@@ -93,9 +96,12 @@ export default function PreviewDrawer({
setIsLoadingText(false) setIsLoadingText(false)
}) })
.catch((err: Error) => { .catch((err: Error) => {
if (abortController.signal.aborted) return
setTextError(err.message) setTextError(err.message)
setIsLoadingText(false) setIsLoadingText(false)
}) })
return () => abortController.abort()
}, [item, fileType, previewUrl, auth, isTooLarge]) }, [item, fileType, previewUrl, auth, isTooLarge])
const handleDownload = useCallback(() => { const handleDownload = useCallback(() => {
+6 -6
View File
@@ -468,15 +468,15 @@ export default function Bisync() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.copy) if (groupedOptions.copy)
setBisyncOptions({ ...bisyncOptions, ...groupedOptions.copy }) setBisyncOptionsJsonString(JSON.stringify({ ...bisyncOptions, ...groupedOptions.copy }, null, 2))
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.copy) setBisyncOptions(groupedOptions.copy) if (groupedOptions.copy) setBisyncOptionsJsonString(JSON.stringify(groupedOptions.copy, null, 2))
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
+68 -103
View File
@@ -67,20 +67,6 @@ export default function Browser() {
const [trackedJobIds, setTrackedJobIds] = useState<Set<number>>(new Set()) const [trackedJobIds, setTrackedJobIds] = useState<Set<number>>(new Set())
const handleJobStarted = useCallback((jobId: number) => { const handleJobStarted = useCallback((jobId: number) => {
// #region agent log
fetch('http://127.0.0.1:7250/ingest/2834749a-e80b-4040-ad02-08316d12b97e', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
location: 'Commander.tsx:handleJobStarted',
message: 'handleJobStarted called',
data: { jobId },
timestamp: Date.now(),
sessionId: 'debug-session',
hypothesisId: 'G',
}),
}).catch(() => {})
// #endregion
setTrackedJobIds((prev) => new Set([...prev, jobId])) setTrackedJobIds((prev) => new Set([...prev, jobId]))
}, []) }, [])
@@ -206,91 +192,85 @@ export default function Browser() {
setContextMenu(null) setContextMenu(null)
}, []) }, [])
const handleDelete = useCallback( const handleDelete = useCallback(async (entry: Entry) => {
async (entry: Entry) => { const confirmed = await ask(`Are you sure you want to delete "${entry.name}"?`, {
const confirmed = await ask( title: 'Confirm Delete',
`Are you sure you want to delete "${entry.name}"?`, kind: 'warning',
{ title: 'Confirm Delete', kind: 'warning' } })
) if (!confirmed) return
if (!confirmed) return
try { try {
const source = entry.fullPath + (entry.isDir ? '/' : '') const source = entry.fullPath + (entry.isDir ? '/' : '')
const info = getFsInfo(source) const info = getFsInfo(source)
const endpoint = entry.isDir ? '/operations/purge' : '/operations/deletefile' const endpoint = entry.isDir ? '/operations/purge' : '/operations/deletefile'
await rclone(endpoint as any, { await rclone(endpoint as any, {
params: {
query: {
fs: info.root,
remote: info.filePath,
},
},
})
leftPanelRef.current?.refresh()
rightPanelRef.current?.refresh()
} catch (error) {
await message(error instanceof Error ? error.message : 'Delete failed', {
title: 'Error',
kind: 'error',
})
}
}, [])
const handleRename = useCallback(async (entry: Entry) => {
const newName = await invoke<string | null>('prompt', {
title: 'Rename',
message: `Enter a new name for "${entry.name}"`,
default: entry.name,
sensitive: false,
})
if (!newName || newName === entry.name) return
try {
const info = getFsInfo(entry.fullPath)
const parentDir = info.filePath.includes('/')
? info.filePath.slice(0, info.filePath.lastIndexOf('/') + 1)
: ''
const dstRemote = `${parentDir}${newName}`
if (entry.isDir) {
await rclone('/sync/move' as any, {
params: { params: {
query: { query: {
fs: info.root, srcFs: `${info.root}${info.filePath}/`,
remote: info.filePath, dstFs: `${info.root}${dstRemote}/`,
deleteEmptySrcDirs: true,
}, },
}, },
}) })
} else {
leftPanelRef.current?.refresh() await rclone('/operations/movefile' as any, {
rightPanelRef.current?.refresh() params: {
} catch (error) { query: {
await message(error instanceof Error ? error.message : 'Delete failed', { srcFs: info.root,
title: 'Error', srcRemote: info.filePath,
kind: 'error', dstFs: info.root,
dstRemote,
},
},
}) })
} }
},
[]
)
const handleRename = useCallback( leftPanelRef.current?.refresh()
async (entry: Entry) => { rightPanelRef.current?.refresh()
const newName = await invoke<string | null>('prompt', { } catch (error) {
title: 'Rename', await message(error instanceof Error ? error.message : 'Rename failed', {
message: `Enter a new name for "${entry.name}"`, title: 'Error',
default: entry.name, kind: 'error',
sensitive: false,
}) })
if (!newName || newName === entry.name) return }
}, [])
try {
const info = getFsInfo(entry.fullPath)
const parentDir = info.filePath.includes('/')
? info.filePath.slice(0, info.filePath.lastIndexOf('/') + 1)
: ''
const dstRemote = `${parentDir}${newName}`
if (entry.isDir) {
await rclone('/sync/move' as any, {
params: {
query: {
srcFs: `${info.root}${info.filePath}/`,
dstFs: `${info.root}${dstRemote}/`,
deleteEmptySrcDirs: true,
},
},
})
} else {
await rclone('/operations/movefile' as any, {
params: {
query: {
srcFs: info.root,
srcRemote: info.filePath,
dstFs: info.root,
dstRemote,
},
},
})
}
leftPanelRef.current?.refresh()
rightPanelRef.current?.refresh()
} catch (error) {
await message(error instanceof Error ? error.message : 'Rename failed', {
title: 'Error',
kind: 'error',
})
}
},
[]
)
const handleCreateFolder = useCallback( const handleCreateFolder = useCallback(
async (panelSide: 'left' | 'right') => { async (panelSide: 'left' | 'right') => {
@@ -519,21 +499,6 @@ function TransfersBar({ trackedJobIds }: { trackedJobIds: Set<number> }) {
const [isExpanded, setIsExpanded] = useState(false) const [isExpanded, setIsExpanded] = useState(false)
const jobIds = Array.from(trackedJobIds) const jobIds = Array.from(trackedJobIds)
// #region agent log
fetch('http://127.0.0.1:7250/ingest/2834749a-e80b-4040-ad02-08316d12b97e', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
location: 'Commander.tsx:TransfersBar:render',
message: 'TransfersBar rendering',
data: { jobIdsCount: jobIds.length, jobIds },
timestamp: Date.now(),
sessionId: 'debug-session',
hypothesisId: 'G,H',
}),
}).catch(() => {})
// #endregion
const itemsQuery = useQuery({ const itemsQuery = useQuery({
queryKey: ['transfers', 'items', jobIds], queryKey: ['transfers', 'items', jobIds],
queryFn: async () => { queryFn: async () => {
+10 -10
View File
@@ -266,7 +266,7 @@ export default function Copy() {
if (startCopyMutation.isPending) return 'STARTING...' if (startCopyMutation.isPending) return 'STARTING...'
if (!sources || sources.length === 0) return 'Please select a source path' if (!sources || sources.length === 0) return 'Please select a source path'
if (!dest) return 'Please select a destination path' if (!dest) return 'Please select a destination path'
if (sources[0] === dest) return 'Source and destination cannot be the same' if (sources.some((s) => s === dest)) return 'Source and destination cannot be the same'
if (jsonError) return 'Invalid JSON for ' + jsonError.toUpperCase() + ' options' if (jsonError) return 'Invalid JSON for ' + jsonError.toUpperCase() + ' options'
if (cronExpression) return 'START AND SCHEDULE COPY' if (cronExpression) return 'START AND SCHEDULE COPY'
return 'START COPY' return 'START COPY'
@@ -274,7 +274,7 @@ export default function Copy() {
const buttonIcon = useMemo(() => { const buttonIcon = useMemo(() => {
if (startCopyMutation.isPending) return if (startCopyMutation.isPending) return
if (!sources || sources.length === 0 || !dest || sources[0] === dest) if (!sources || sources.length === 0 || !dest || sources.some((s) => s === dest))
return <FoldersIcon className="w-5 h-5" /> return <FoldersIcon className="w-5 h-5" />
if (jsonError) return <AlertOctagonIcon className="w-5 h-5" /> if (jsonError) return <AlertOctagonIcon className="w-5 h-5" />
return <PlayIcon className="w-5 h-5 fill-current" /> return <PlayIcon className="w-5 h-5 fill-current" />
@@ -411,15 +411,15 @@ export default function Copy() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.copy) if (groupedOptions.copy)
setCopyOptions({ ...copyOptions, ...groupedOptions.copy }) setCopyOptionsJsonString(JSON.stringify({ ...copyOptions, ...groupedOptions.copy }, null, 2))
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.copy) setCopyOptions(groupedOptions.copy) if (groupedOptions.copy) setCopyOptionsJsonString(JSON.stringify(groupedOptions.copy, null, 2))
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
@@ -565,7 +565,7 @@ export default function Copy() {
!sources || !sources ||
sources.length === 0 || sources.length === 0 ||
!dest || !dest ||
sources[0] === dest sources.some((s) => s === dest)
} }
isLoading={startCopyMutation.isPending} isLoading={startCopyMutation.isPending}
endContent={buttonIcon} endContent={buttonIcon}
@@ -597,7 +597,7 @@ export default function Copy() {
!sources || !sources ||
sources.length === 0 || sources.length === 0 ||
!dest || !dest ||
sources[0] === dest sources.some((s) => s === dest)
) { ) {
return return
} }
+4 -4
View File
@@ -343,12 +343,12 @@ export default function Delete() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
+5 -1
View File
@@ -207,7 +207,11 @@ export default function Download() {
setIsFetchingDownloadData(false) setIsFetchingDownloadData(false)
}) })
}) })
.catch() .catch(() => {
startTransition(() => {
setIsFetchingDownloadData(false)
})
})
return () => { return () => {
abortController.abort() abortController.abort()
+8 -8
View File
@@ -308,18 +308,18 @@ export default function Mount() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.mount) if (groupedOptions.mount)
setMountOptions({ ...mountOptions, ...groupedOptions.mount }) setMountOptionsJsonString(JSON.stringify({ ...mountOptions, ...groupedOptions.mount }, null, 2))
if (groupedOptions.vfs) if (groupedOptions.vfs)
setVfsOptions({ ...vfsOptions, ...groupedOptions.vfs }) setVfsOptionsJsonString(JSON.stringify({ ...vfsOptions, ...groupedOptions.vfs }, null, 2))
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.mount) setMountOptions(groupedOptions.mount) if (groupedOptions.mount) setMountOptionsJsonString(JSON.stringify(groupedOptions.mount, null, 2))
if (groupedOptions.vfs) setVfsOptions(groupedOptions.vfs) if (groupedOptions.vfs) setVfsOptionsJsonString(JSON.stringify(groupedOptions.vfs, null, 2))
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
+10 -10
View File
@@ -293,7 +293,7 @@ export default function Move() {
if (startMoveMutation.isPending) return 'STARTING...' if (startMoveMutation.isPending) return 'STARTING...'
if (!sources || sources.length === 0) return 'Please select a source path' if (!sources || sources.length === 0) return 'Please select a source path'
if (!dest) return 'Please select a destination path' if (!dest) return 'Please select a destination path'
if (sources[0] === dest) return 'Source and destination cannot be the same' if (sources.some((s) => s === dest)) return 'Source and destination cannot be the same'
if (jsonError) return 'Invalid JSON for ' + jsonError.toUpperCase() + ' options' if (jsonError) return 'Invalid JSON for ' + jsonError.toUpperCase() + ' options'
if (cronExpression) return 'START AND SCHEDULE MOVE' if (cronExpression) return 'START AND SCHEDULE MOVE'
return 'START MOVE' return 'START MOVE'
@@ -301,7 +301,7 @@ export default function Move() {
const buttonIcon = useMemo(() => { const buttonIcon = useMemo(() => {
if (startMoveMutation.isPending) return if (startMoveMutation.isPending) return
if (!sources || sources.length === 0 || !dest || sources[0] === dest) if (!sources || sources.length === 0 || !dest || sources.some((s) => s === dest))
return <FoldersIcon className="w-5 h-5" /> return <FoldersIcon className="w-5 h-5" />
if (jsonError) return <AlertOctagonIcon className="w-5 h-5" /> if (jsonError) return <AlertOctagonIcon className="w-5 h-5" />
return <PlayIcon className="w-5 h-5 fill-current" /> return <PlayIcon className="w-5 h-5 fill-current" />
@@ -433,15 +433,15 @@ export default function Move() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.copy) if (groupedOptions.copy)
setMoveOptions({ ...moveOptions, ...groupedOptions.copy }) setMoveOptionsJsonString(JSON.stringify({ ...moveOptions, ...groupedOptions.copy }, null, 2))
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.copy) setMoveOptions(groupedOptions.copy) if (groupedOptions.copy) setMoveOptionsJsonString(JSON.stringify(groupedOptions.copy, null, 2))
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
@@ -587,7 +587,7 @@ export default function Move() {
!sources || !sources ||
sources.length === 0 || sources.length === 0 ||
!dest || !dest ||
sources[0] === dest sources.some((s) => s === dest)
} }
isLoading={startMoveMutation.isPending} isLoading={startMoveMutation.isPending}
endContent={buttonIcon} endContent={buttonIcon}
@@ -619,7 +619,7 @@ export default function Move() {
!sources || !sources ||
sources.length === 0 || sources.length === 0 ||
!dest || !dest ||
sources[0] === dest sources.some((s) => s === dest)
) { ) {
return return
} }
+2 -2
View File
@@ -230,9 +230,9 @@ export default function Purge() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else if (groupedOptions.config) } else if (groupedOptions.config)
setConfigOptions(groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
}) })
}} }}
getOptions={() => ({ getOptions={() => ({
+4 -2
View File
@@ -69,8 +69,10 @@ function TaskCard({
} }
}, [task.name, isEditingName]) }, [task.name, isEditingName])
const parsed = useMemo(() => CronExpressionParser.parse(task.cron), [task.cron]) const nextRun = useMemo(() => {
const nextRun = useMemo(() => (parsed.hasNext() ? parsed.next().toDate() : null), [parsed]) const parsed = CronExpressionParser.parse(task.cron)
return parsed.hasNext() ? parsed.next().toDate() : null
}, [task.cron])
const source = useMemo( const source = useMemo(
() => ('source' in task.args ? task.args.source : task.args.sources[0]), () => ('source' in task.args ? task.args.source : task.args.sources[0]),
+8 -11
View File
@@ -290,22 +290,19 @@ export default function Serve() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.serve && type) if (groupedOptions.serve && type)
setServeOptions({ setServeOptionsJsonString(JSON.stringify({ ...serveOptions, ...groupedOptions.serve[type] }, null, 2))
...serveOptions,
...groupedOptions.serve[type],
})
if (groupedOptions.vfs) if (groupedOptions.vfs)
setVfsOptions({ ...vfsOptions, ...groupedOptions.vfs }) setVfsOptionsJsonString(JSON.stringify({ ...vfsOptions, ...groupedOptions.vfs }, null, 2))
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.serve && type) if (groupedOptions.serve && type)
setServeOptions(groupedOptions.serve[type]) setServeOptionsJsonString(JSON.stringify(groupedOptions.serve[type], null, 2))
if (groupedOptions.vfs) setVfsOptions(groupedOptions.vfs) if (groupedOptions.vfs) setVfsOptionsJsonString(JSON.stringify(groupedOptions.vfs, null, 2))
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
+16 -24
View File
@@ -109,22 +109,18 @@ export default function LicenseSection() {
try { try {
await validateLicense(licenseKeyInput) await validateLicense(licenseKeyInput)
} catch (e) { } catch (e) {
if (e instanceof Error) { await message(
await message(e.message, { e instanceof Error ? e.message : 'An error occurred. Please try again.',
{
title: 'Error', title: 'Error',
kind: 'error', kind: 'error',
okLabel: 'Ok', okLabel: 'Ok',
}) }
return )
} return
} finally {
await message('An error occurred. Please try again.', { setIsActivating(false)
title: 'Error',
kind: 'error',
okLabel: 'Ok',
})
} }
setIsActivating(false)
await message('Your license has been successfully activated.', { await message('Your license has been successfully activated.', {
title: 'Congrats!', title: 'Congrats!',
@@ -165,22 +161,18 @@ export default function LicenseSection() {
try { try {
await revokeMachineLicense(licenseKeyInput) await revokeMachineLicense(licenseKeyInput)
} catch (e) { } catch (e) {
if (e instanceof Error) { await message(
await message(e.message, { e instanceof Error ? e.message : 'An error occurred. Please try again.',
{
title: 'Error', title: 'Error',
kind: 'error', kind: 'error',
okLabel: 'Ok', okLabel: 'Ok',
}) }
return )
} return
} finally {
await message('An error occurred. Please try again.', { setIsRevoking(false)
title: 'Error',
kind: 'error',
okLabel: 'Ok',
})
} }
setIsRevoking(false)
await message('Your license has been successfully deactivated.', { await message('Your license has been successfully deactivated.', {
title: 'License deactivated', title: 'License deactivated',
-2
View File
@@ -365,8 +365,6 @@ function RemoteCard({
enabled: supportsAbout, enabled: supportsAbout,
}) })
console.log('about', remote, type, JSON.stringify(remoteAboutData, null, 2))
const imageUrl = useMemo( const imageUrl = useMemo(
() => () =>
provider && !type ? `/icons/providers/${provider}.png` : `/icons/backends/${type}.png`, provider && !type ? `/icons/providers/${provider}.png` : `/icons/backends/${type}.png`,
+6 -6
View File
@@ -416,15 +416,15 @@ export default function Sync() {
startTransition(() => { startTransition(() => {
if (shouldMerge) { if (shouldMerge) {
if (groupedOptions.sync) if (groupedOptions.sync)
setSyncOptions({ ...syncOptions, ...groupedOptions.sync }) setSyncOptionsJsonString(JSON.stringify({ ...syncOptions, ...groupedOptions.sync }, null, 2))
if (groupedOptions.filter) if (groupedOptions.filter)
setFilterOptions({ ...filterOptions, ...groupedOptions.filter }) setFilterOptionsJsonString(JSON.stringify({ ...filterOptions, ...groupedOptions.filter }, null, 2))
if (groupedOptions.config) if (groupedOptions.config)
setConfigOptions({ ...configOptions, ...groupedOptions.config }) setConfigOptionsJsonString(JSON.stringify({ ...configOptions, ...groupedOptions.config }, null, 2))
} else { } else {
if (groupedOptions.sync) setSyncOptions(groupedOptions.sync) if (groupedOptions.sync) setSyncOptionsJsonString(JSON.stringify(groupedOptions.sync, null, 2))
if (groupedOptions.filter) setFilterOptions(groupedOptions.filter) if (groupedOptions.filter) setFilterOptionsJsonString(JSON.stringify(groupedOptions.filter, null, 2))
if (groupedOptions.config) setConfigOptions(groupedOptions.config) if (groupedOptions.config) setConfigOptionsJsonString(JSON.stringify(groupedOptions.config, null, 2))
} }
}) })
}} }}
-11
View File
@@ -59,17 +59,6 @@ export default function Transfers() {
[transfersQuery.data] [transfersQuery.data]
) )
console.log('[Transfers] query state', {
isLoading: transfersQuery.isLoading,
isRefetching: transfersQuery.isRefetching,
isError: transfersQuery.isError,
error: transfersQuery.error,
activeCount: transfers.active.length,
inactiveCount: transfers.inactive.length,
// checkingJobs: transfers.active.filter((j) => j.isChecking).length,
})
console.log('[Transfers] data', JSON.stringify(transfers, null, 2))
if (transfersQuery.isLoading) { if (transfersQuery.isLoading) {
return ( return (
<div className="flex flex-col items-center justify-center h-screen"> <div className="flex flex-col items-center justify-center h-screen">
+3 -3
View File
@@ -34,9 +34,9 @@ export async function initHostStore(hostId: string) {
} }
try { try {
disposeKeyChange = activeStore.onKeyChange('host-store', async () => { disposeKeyChange = await activeStore.onKeyChange('host-store', async () => {
await useHostStore.persist.rehydrate() await useHostStore.persist.rehydrate()
}) as unknown as () => void })
} catch (err) { } catch (err) {
console.error('[HostStore] failed to register onKeyChange listener', err) console.error('[HostStore] failed to register onKeyChange listener', err)
} }
@@ -49,7 +49,7 @@ const getStorage = (): StateStorage => ({
getItem: async (name: string): Promise<string | null> => { getItem: async (name: string): Promise<string | null> => {
if (!activeStore) return null if (!activeStore) return null
// console.log('[HostStore] getItem', { name, host: activeHostId }) // console.log('[HostStore] getItem', { name, host: activeHostId })
return (await activeStore.get(name)) || null return (await activeStore.get(name)) ?? null
}, },
setItem: async (name: string, value: string): Promise<void> => { setItem: async (name: string, value: string): Promise<void> => {
if (!activeStore) return if (!activeStore) return
+1 -1
View File
@@ -142,7 +142,7 @@ interface PersistedStateV2 {
const getStorage = (store: LazyStore): StateStorage => ({ const getStorage = (store: LazyStore): StateStorage => ({
getItem: async (name: string): Promise<string | null> => { getItem: async (name: string): Promise<string | null> => {
console.log('getItem', { name }) console.log('getItem', { name })
return (await store.get(name)) || null return (await store.get(name)) ?? null
}, },
setItem: async (name: string, value: string): Promise<void> => { setItem: async (name: string, value: string): Promise<void> => {
console.log('setItem', { name, value }) console.log('setItem', { name, value })
+1 -1
View File
@@ -21,5 +21,5 @@
"noUnusedParameters": true, "noUnusedParameters": true,
"noFallthroughCasesInSwitch": true "noFallthroughCasesInSwitch": true
}, },
"include": ["src", "lib", "reset.d.ts", "toolbar"] "include": ["src", "lib", "store", "types", "toolbar", "main.ts", "reset.d.ts"]
} }