update nextui > heroui, tailwind, vite, onboard, comments, fixes

This commit is contained in:
FTCHD
2025-08-05 05:11:29 +03:00
parent de698051db
commit 1bec1378f2
25 changed files with 4828 additions and 3326 deletions
-1
View File
@@ -1,5 +1,4 @@
export function formatBytes(bytes: number) { export function formatBytes(bytes: number) {
// format bytes in to a readable format like (MB, GB, etc), depending on how big the number is
if (bytes < 1024) { if (bytes < 1024) {
return `${bytes} B` return `${bytes} B`
} }
+18 -4
View File
@@ -3,16 +3,20 @@ import { fetch } from '@tauri-apps/plugin-http'
import { usePersistedStore } from './store' import { usePersistedStore } from './store'
export async function validateLicense(licenseKey: string) { export async function validateLicense(licenseKey: string) {
console.log('[validateLicense]')
let id let id
try { try {
id = await invoke('get_uid') id = await invoke('get_uid')
} catch (e) { } catch (e) {
console.error('[validateLicense] failed to build unique identifier')
console.error(JSON.stringify(e)) console.error(JSON.stringify(e))
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
if (!id) { if (!id) {
console.error('[validateLicense] missing unique identifier')
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
@@ -25,34 +29,41 @@ export async function validateLicense(licenseKey: string) {
}) })
.then((r) => r.json()) .then((r) => r.json())
.catch((e) => { .catch((e) => {
console.error('[validateLicense] failed to validate license')
console.error(JSON.stringify(e)) console.error(JSON.stringify(e))
throw new Error('Failed to validate license. Are you connected to the internet?') throw new Error('Failed to validate license. Are you connected to the internet?')
}) })
// console.log(JSON.stringify(validationResponse))
if (validationResponse.error) { if (validationResponse.error) {
console.error('[validateLicense] failed to validate license')
throw new Error(validationResponse.error) throw new Error(validationResponse.error)
} }
if (!validationResponse.valid) { if (!validationResponse.valid) {
console.error('[validateLicense] invalid license key')
throw new Error('Invalid license key. Please check your license key and try again.') throw new Error('Invalid license key. Please check your license key and try again.')
} }
usePersistedStore.setState({ licenseKey, licenseValid: true }) usePersistedStore.setState({ licenseKey, licenseValid: true })
console.log('[validateLicense] license validated')
} }
export async function revokeLicense(licenseKey: string) { export async function revokeLicense(licenseKey: string) {
console.log('[revokeLicense]')
let id let id
try { try {
id = await invoke('get_uid') id = await invoke('get_uid')
} catch (e) { } catch (e) {
console.error('[revokeLicense] failed to build unique identifier')
console.error(JSON.stringify(e)) console.error(JSON.stringify(e))
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
if (!id) { if (!id) {
console.error('[revokeLicense] missing unique identifier')
throw new Error('Failed to build unique identifier. Please try again later.') throw new Error('Failed to build unique identifier. Please try again later.')
} }
@@ -65,19 +76,22 @@ export async function revokeLicense(licenseKey: string) {
}) })
.then((r) => r.json()) .then((r) => r.json())
.catch((e) => { .catch((e) => {
console.error('[revokeLicense] failed to revoke license, fetch failed')
console.error(JSON.stringify(e)) console.error(JSON.stringify(e))
throw new Error('Failed to revoke license. Are you connected to the internet?') throw new Error('Failed to revoke license. Are you connected to the internet?')
}) })
// console.log(JSON.stringify(revocationResponse))
if (revocationResponse.error) { if (revocationResponse.error) {
console.error('[revokeLicense] failed to revoke license, has error response')
throw new Error(revocationResponse.error) throw new Error(revocationResponse.error)
} }
if (!revocationResponse.revoked) { if (!revocationResponse.revoked) {
console.error('[revokeLicense] failed to revoke license, missing revoked response')
throw new Error('Failed to revoke license. Please check your license key and try again.') throw new Error('Failed to revoke license. Please check your license key and try again.')
} }
usePersistedStore.setState({ licenseKey: undefined, licenseValid: false }) usePersistedStore.setState({ licenseKey: undefined, licenseValid: false })
console.log('[revokeLicense] license revoked')
} }
-1
View File
@@ -14,7 +14,6 @@ import { usePersistedStore, useStore } from './store'
import { getLoadingTray, getMainTray, rebuildTrayMenu } from './tray' import { getLoadingTray, getMainTray, rebuildTrayMenu } from './tray'
import { lockWindows, openFullWindow, openWindow, unlockWindows } from './window' import { lockWindows, openFullWindow, openWindow, unlockWindows } from './window'
// Function to rebuild and update the menu
export async function buildMenu() { export async function buildMenu() {
const storeState = useStore.getState() const storeState = useStore.getState()
+43 -18
View File
@@ -24,14 +24,17 @@ function getAuthHeader() {
/* DATA */ /* DATA */
export async function listRemotes() { export async function listRemotes() {
console.log('[listRemotes] CALLED')
const r = await fetch('http://localhost:5572/config/listremotes', { const r = await fetch('http://localhost:5572/config/listremotes', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
}).then((res) => res.json() as Promise<{ remotes: string[] }>) })
// .catch((e) => { .catch((e) => {
// console.log("error", e); console.log('error', e)
// throw e; throw e
// }); })
.then((res) => res.json() as Promise<{ remotes: string[] }>)
if (typeof r?.remotes === 'undefined') { if (typeof r?.remotes === 'undefined') {
throw new Error('Failed to fetch remotes') throw new Error('Failed to fetch remotes')
@@ -41,16 +44,14 @@ export async function listRemotes() {
} }
export async function getRemote(remote: string) { export async function getRemote(remote: string) {
console.log('[getRemote]', remote)
const r = await fetch(`http://localhost:5572/config/get?name=${remote}`, { const r = await fetch(`http://localhost:5572/config/get?name=${remote}`, {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
}).then( }).then(
(res) => res.json() as Promise<{ type: string } & Record<string, string | number | boolean>> (res) => res.json() as Promise<{ type: string } & Record<string, string | number | boolean>>
) )
// .catch((e) => {
// console.log("error", e);
// throw e;
// });
// console.log(JSON.stringify(r, null, 2)) // console.log(JSON.stringify(r, null, 2))
@@ -61,7 +62,7 @@ export async function updateRemote(
remote: string, remote: string,
parameters: Record<string, string | number | boolean> parameters: Record<string, string | number | boolean>
) { ) {
console.log('updateRemote', remote, parameters) console.log('[updateRemote]', remote, parameters)
const options = new URLSearchParams() const options = new URLSearchParams()
options.set('name', remote) options.set('name', remote)
@@ -80,7 +81,7 @@ export async function createRemote(
type: string, type: string,
parameters: Record<string, string | number | boolean> parameters: Record<string, string | number | boolean>
) { ) {
console.log('createRemote', name, type, parameters) console.log('[createRemote]', name, type, parameters)
const options = new URLSearchParams() const options = new URLSearchParams()
options.set('name', name) options.set('name', name)
@@ -96,6 +97,8 @@ export async function createRemote(
} }
export async function deleteRemote(remote: string) { export async function deleteRemote(remote: string) {
console.log('[deleteRemote]', remote)
await fetch(`http://localhost:5572/config/delete?name=${remote}`, { await fetch(`http://localhost:5572/config/delete?name=${remote}`, {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -105,6 +108,8 @@ export async function deleteRemote(remote: string) {
} }
export async function getMountPoints() { export async function getMountPoints() {
console.log('[getMountPoints]')
const r = await fetch('http://localhost:5572/mount/listmounts', { const r = await fetch('http://localhost:5572/mount/listmounts', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -115,8 +120,6 @@ export async function getMountPoints() {
}> }>
) )
// console.log('Mount points:', r)
if (!Array.isArray(r?.mountPoints)) { if (!Array.isArray(r?.mountPoints)) {
throw new Error('Failed to get mount points') throw new Error('Failed to get mount points')
} }
@@ -125,6 +128,8 @@ export async function getMountPoints() {
} }
export async function getBackends() { export async function getBackends() {
console.log('[getBackends]')
const providers = await fetch('http://localhost:5572/config/providers', { const providers = await fetch('http://localhost:5572/config/providers', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -149,6 +154,8 @@ export interface ListOptions {
} }
export async function listPath(remote: string, path: string = '', options: ListOptions = {}) { export async function listPath(remote: string, path: string = '', options: ListOptions = {}) {
console.log('[listPath]', remote, path, options)
const params = new URLSearchParams() const params = new URLSearchParams()
params.set('fs', `${remote}:`) params.set('fs', `${remote}:`)
params.set('remote', path) params.set('remote', path)
@@ -190,9 +197,11 @@ export async function listPath(remote: string, path: string = '', options: ListO
return response?.list || [] return response?.list || []
} }
/* JOBS */
/* JOBS */
export async function listJobs() { export async function listJobs() {
console.log('[listJobs]')
const allStats = await fetch('http://localhost:5572/core/stats', { const allStats = await fetch('http://localhost:5572/core/stats', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -273,6 +282,8 @@ export async function listJobs() {
} }
export async function stopJob(jobId: number) { export async function stopJob(jobId: number) {
console.log('[stopJob]', jobId)
await fetch(`http://localhost:5572/job/stopgroup?group=job/${jobId}`, { await fetch(`http://localhost:5572/job/stopgroup?group=job/${jobId}`, {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -291,6 +302,8 @@ export async function mountRemote({
mountOptions?: Record<string, string | number | boolean> mountOptions?: Record<string, string | number | boolean>
vfsOptions?: Record<string, string | number | boolean> vfsOptions?: Record<string, string | number | boolean>
}) { }) {
console.log('[mountRemote]', remotePath, mountPoint)
const options = new URLSearchParams() const options = new URLSearchParams()
options.set('fs', remotePath) options.set('fs', remotePath)
options.set('mountPoint', mountPoint) options.set('mountPoint', mountPoint)
@@ -325,6 +338,8 @@ export async function unmountRemote({
}: { }: {
mountPoint: string mountPoint: string
}) { }) {
console.log('[unmountRemote]', mountPoint)
const options = new URLSearchParams() const options = new URLSearchParams()
options.set('mountPoint', mountPoint) options.set('mountPoint', mountPoint)
@@ -338,8 +353,6 @@ export async function unmountRemote({
throw e throw e
}) })
// console.log('unmountRemote', JSON.stringify(r, null, 2))
if ('error' in r) { if ('error' in r) {
throw new Error(r.error) throw new Error(r.error)
} }
@@ -348,6 +361,8 @@ export async function unmountRemote({
} }
export async function unmountAllRemotes() { export async function unmountAllRemotes() {
console.log('[unmountAllRemotes]')
const r = await fetch('http://localhost:5572/mount/unmountall', { const r = await fetch('http://localhost:5572/mount/unmountall', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -358,8 +373,6 @@ export async function unmountAllRemotes() {
throw e throw e
}) })
console.log('unmountAllRemotes', r)
if ('error' in r) { if ('error' in r) {
throw new Error(r.error) throw new Error(r.error)
} }
@@ -452,6 +465,8 @@ export async function startSync({
/* FLAGS */ /* FLAGS */
export async function getGlobalFlags() { export async function getGlobalFlags() {
console.log('[getGlobalFlags]')
const r = await fetch('http://localhost:5572/options/get', { const r = await fetch('http://localhost:5572/options/get', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -461,6 +476,8 @@ export async function getGlobalFlags() {
} }
export async function getCopyFlags() { export async function getCopyFlags() {
console.log('[getCopyFlags]')
const r = await fetch('http://localhost:5572/options/info', { const r = await fetch('http://localhost:5572/options/info', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -476,6 +493,8 @@ export async function getCopyFlags() {
} }
export async function getSyncFlags() { export async function getSyncFlags() {
console.log('[getSyncFlags]')
const r = await fetch('http://localhost:5572/options/info', { const r = await fetch('http://localhost:5572/options/info', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -494,6 +513,8 @@ export async function getSyncFlags() {
} }
export async function getFilterFlags() { export async function getFilterFlags() {
console.log('[getFilterFlags]')
const r = await fetch('http://localhost:5572/options/info', { const r = await fetch('http://localhost:5572/options/info', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -508,6 +529,8 @@ export async function getFilterFlags() {
} }
export async function getVfsFlags() { export async function getVfsFlags() {
console.log('[getVfsFlags]')
const r = await fetch('http://localhost:5572/options/info', { const r = await fetch('http://localhost:5572/options/info', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
@@ -523,6 +546,8 @@ export async function getVfsFlags() {
} }
export async function getMountFlags() { export async function getMountFlags() {
console.log('[getMountFlags]')
const r = await fetch('http://localhost:5572/options/info', { const r = await fetch('http://localhost:5572/options/info', {
method: 'POST', method: 'POST',
headers: getAuthHeader(), headers: getAuthHeader(),
+39 -30
View File
@@ -39,9 +39,10 @@ export async function initRclone() {
* @returns {Promise<boolean>} True if rclone is installed and working * @returns {Promise<boolean>} True if rclone is installed and working
*/ */
export async function isSystemRcloneInstalled() { export async function isSystemRcloneInstalled() {
console.log('[isSystemRcloneInstalled]')
try { try {
const output = await Command.create('rclone-system').execute() const output = await Command.create('rclone-system').execute()
// console.log('[isSystemRcloneInstalled] output', output)
return ( return (
output.stdout.includes('Available commands') || output.stdout.includes('Available commands') ||
output.stderr.includes('Available commands') output.stderr.includes('Available commands')
@@ -56,6 +57,8 @@ export async function isSystemRcloneInstalled() {
* @returns {Promise<boolean>} True if downloaded rclone is present and working * @returns {Promise<boolean>} True if downloaded rclone is present and working
*/ */
export async function isInternalRcloneInstalled() { export async function isInternalRcloneInstalled() {
console.log('[isInternalRcloneInstalled]')
try { try {
const output = await Command.create('rclone-internal').execute() const output = await Command.create('rclone-internal').execute()
// console.log('[isInternalRcloneInstalled] output', output) // console.log('[isInternalRcloneInstalled] output', output)
@@ -74,18 +77,21 @@ export async function isInternalRcloneInstalled() {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
export async function provisionRclone() { export async function provisionRclone() {
console.log('[provisionRclone]')
const currentVersionString = await fetch('https://downloads.rclone.org/version.txt').then( const currentVersionString = await fetch('https://downloads.rclone.org/version.txt').then(
(res) => res.text() (res) => res.text()
) )
console.log('currentVersionString', currentVersionString) console.log('[provisionRclone] currentVersionString', currentVersionString)
const currentVersion = currentVersionString.split('v')?.[1]?.trim() const currentVersion = currentVersionString.split('v')?.[1]?.trim()
if (!currentVersion) { if (!currentVersion) {
console.error('Failed to get latest version') console.error('[provisionRclone] failed to get latest version')
await message('Failed to get latest rclone version, please try again later.')
return return
} }
console.log('currentVersion', currentVersion) console.log('[provisionRclone] currentVersion', currentVersion)
const currentPlatform = platform() const currentPlatform = platform()
console.log('currentPlatform', currentPlatform) console.log('currentPlatform', currentPlatform)
@@ -97,29 +103,31 @@ export async function provisionRclone() {
if (tempDirPath.endsWith('/') || tempDirPath.endsWith('\\')) { if (tempDirPath.endsWith('/') || tempDirPath.endsWith('\\')) {
tempDirPath = tempDirPath.slice(0, -1) tempDirPath = tempDirPath.slice(0, -1)
} }
console.log('tempDirPath', tempDirPath) console.log('[provisionRclone] tempDirPath', tempDirPath)
const arch = (await invoke('get_arch')) as 'arm64' | 'amd64' | '386' | 'unknown' const arch = (await invoke('get_arch')) as 'arm64' | 'amd64' | '386' | 'unknown'
console.log('arch', arch) console.log('[provisionRclone] arch', arch)
if (arch === 'unknown') { if (arch === 'unknown') {
throw new Error('Failed to get architecture, please try again later.') console.error('[provisionRclone] failed to get architecture')
await message('Failed to get current arch, please try again later.')
return
} }
const downloadUrl = `https://downloads.rclone.org/v${currentVersion}/rclone-v${currentVersion}-${currentOs}-${arch}.zip` const downloadUrl = `https://downloads.rclone.org/v${currentVersion}/rclone-v${currentVersion}-${currentOs}-${arch}.zip`
console.log('downloadUrl', downloadUrl) console.log('[provisionRclone] downloadUrl', downloadUrl)
const downloadedFile = await fetch(downloadUrl).then((res) => res.arrayBuffer()) const downloadedFile = await fetch(downloadUrl).then((res) => res.arrayBuffer())
console.log('downloadedFile') console.log('[provisionRclone] downloadedFile')
let tempDirExists let tempDirExists
try { try {
tempDirExists = await exists('rclone', { tempDirExists = await exists('rclone', {
baseDir: BaseDirectory.Temp, baseDir: BaseDirectory.Temp,
}) })
console.log('tempDirExists', tempDirExists) console.log('[provisionRclone] tempDirExists', tempDirExists)
} catch (error) { } catch (error) {
console.error('Failed to check if rclone temp dir exists', error) console.error('[provisionRclone] failed to check if rclone temp dir exists', error)
} }
if (tempDirExists) { if (tempDirExists) {
@@ -128,9 +136,9 @@ export async function provisionRclone() {
recursive: true, recursive: true,
baseDir: BaseDirectory.Temp, baseDir: BaseDirectory.Temp,
}) })
console.log('removed rclone temp dir') console.log('[provisionRclone] removed rclone temp dir')
} catch (error) { } catch (error) {
console.error('Failed to remove rclone temp dir', error) console.error('[provisionRclone] failed to remove rclone temp dir', error)
await message('Failed to provision rclone.') await message('Failed to provision rclone.')
return return
} }
@@ -140,21 +148,21 @@ export async function provisionRclone() {
await mkdir('rclone', { await mkdir('rclone', {
baseDir: BaseDirectory.Temp, baseDir: BaseDirectory.Temp,
}) })
console.log('created rclone temp dir') console.log('[provisionRclone] created rclone temp dir')
} catch (error) { } catch (error) {
console.error('Failed to create rclone temp dir', error) console.error('[provisionRclone] failed to create rclone temp dir', error)
await message('Failed to provision rclone.') await message('Failed to provision rclone.')
return return
} }
const zipPath = `${tempDirPath}/rclone/rclone-v${currentVersion}-${currentOs}-${arch}.zip` const zipPath = `${tempDirPath}/rclone/rclone-v${currentVersion}-${currentOs}-${arch}.zip`
console.log('zipPath', zipPath) console.log('[provisionRclone] zipPath', zipPath)
try { try {
await writeFile(zipPath, new Uint8Array(downloadedFile)) await writeFile(zipPath, new Uint8Array(downloadedFile))
console.log('wrote zip file') console.log('[provisionRclone] wrote zip file')
} catch (error) { } catch (error) {
console.error('Failed to write zip file', error) console.error('[provisionRclone] failed to write zip file', error)
await message('Failed to provision rclone.') await message('Failed to provision rclone.')
return return
} }
@@ -164,48 +172,48 @@ export async function provisionRclone() {
zipPath, zipPath,
outputFolder: `${tempDirPath}/rclone/rclone-ui`, outputFolder: `${tempDirPath}/rclone/rclone-ui`,
}) })
console.log('Successfully unzipped file') console.log('[provisionRclone] successfully unzipped file')
} catch (error) { } catch (error) {
console.error('Failed to unzip file', error) console.error('[provisionRclone] failed to unzip file', error)
await message('Failed to provision rclone.') await message('Failed to provision rclone.')
return return
} }
const unarchivedPath = `${tempDirPath}/rclone/rclone-ui/rclone-v${currentVersion}-${currentOs}-${arch}` const unarchivedPath = `${tempDirPath}/rclone/rclone-ui/rclone-v${currentVersion}-${currentOs}-${arch}`
console.log('unarchivedPath', unarchivedPath) console.log('[provisionRclone] unarchivedPath', unarchivedPath)
const binaryName = currentPlatform === 'windows' ? 'rclone.exe' : 'rclone' const binaryName = currentPlatform === 'windows' ? 'rclone.exe' : 'rclone'
// "/" here looks to be working on windows // "/" here looks to be working on windows
const rcloneBinaryPath = unarchivedPath + '/' + binaryName const rcloneBinaryPath = unarchivedPath + '/' + binaryName
console.log('rcloneBinaryPath', rcloneBinaryPath) console.log('[provisionRclone] rcloneBinaryPath', rcloneBinaryPath)
try { try {
const binaryExists = await exists(rcloneBinaryPath) const binaryExists = await exists(rcloneBinaryPath)
console.log('rcloneBinaryPathExists', binaryExists) console.log('[provisionRclone] rcloneBinaryPathExists', binaryExists)
if (!binaryExists) { if (!binaryExists) {
throw new Error('Could not find rclone binary in zip') throw new Error('Could not find rclone binary in zip')
} }
} catch (error) { } catch (error) {
console.error('Failed to check if rclone binary exists', error) console.error('[provisionRclone] failed to check if rclone binary exists', error)
throw new Error('Could not find rclone binary in zip') await message('Failed to provision rclone.')
} }
const appLocalDataDirPath = await appLocalDataDir() const appLocalDataDirPath = await appLocalDataDir()
console.log('appLocalDataDirPath', appLocalDataDirPath) console.log('[provisionRclone] appLocalDataDirPath', appLocalDataDirPath)
const appLocalDataDirPathExists = await exists(appLocalDataDirPath) const appLocalDataDirPathExists = await exists(appLocalDataDirPath)
console.log('appLocalDataDirPathExists', appLocalDataDirPathExists) console.log('[provisionRclone] appLocalDataDirPathExists', appLocalDataDirPathExists)
if (!appLocalDataDirPathExists) { if (!appLocalDataDirPathExists) {
await mkdir(appLocalDataDirPath, { await mkdir(appLocalDataDirPath, {
recursive: true, recursive: true,
}) })
console.log('appLocalDataDirPath created') console.log('[provisionRclone] appLocalDataDirPath created')
} }
await copyFile(rcloneBinaryPath, `${appLocalDataDirPath}/${binaryName}`) await copyFile(rcloneBinaryPath, `${appLocalDataDirPath}/${binaryName}`)
console.log('copied rclone binary') console.log('[provisionRclone] copied rclone binary')
const hasInstalled = await isInternalRcloneInstalled() const hasInstalled = await isInternalRcloneInstalled()
@@ -213,5 +221,6 @@ export async function provisionRclone() {
throw new Error('Failed to install rclone') throw new Error('Failed to install rclone')
} }
console.log('rclone has been installed') console.log('[provisionRclone] rclone has been installed')
} }
+11 -6
View File
@@ -8,13 +8,15 @@ import { exit } from '@tauri-apps/plugin-process'
import { Command } from '@tauri-apps/plugin-shell' import { Command } from '@tauri-apps/plugin-shell'
export async function needsMountPlugin() { export async function needsMountPlugin() {
console.log('[needsMountPlugin]')
const currentPlatform = platform() const currentPlatform = platform()
if (currentPlatform === 'macos') { if (currentPlatform === 'macos') {
// check fuse-t or osxfuse // check fuse-t or osxfuse
const hasFuseT = await exists('/Library/Application Support/fuse-t') const hasFuseT = await exists('/Library/Application Support/fuse-t')
console.log('hasFuseT', hasFuseT) console.log('[needsMountPlugin] hasFuseT', hasFuseT)
const hasOsxFuse = await exists('/Library/Filesystems/macfuse.fs') const hasOsxFuse = await exists('/Library/Filesystems/macfuse.fs')
console.log('hasOsxFuse', hasOsxFuse) console.log('[needsMountPlugin] hasOsxFuse', hasOsxFuse)
return !hasFuseT && !hasOsxFuse return !hasFuseT && !hasOsxFuse
} }
if (currentPlatform === 'windows') { if (currentPlatform === 'windows') {
@@ -22,13 +24,15 @@ export async function needsMountPlugin() {
const hasWinFsp = const hasWinFsp =
(await exists('C:\\Program Files\\WinFsp')) || (await exists('C:\\Program Files\\WinFsp')) ||
(await exists('C:\\Program Files (x86)\\WinFsp')) (await exists('C:\\Program Files (x86)\\WinFsp'))
console.log('hasWinFsp', hasWinFsp) console.log('[needsMountPlugin] hasWinFsp', hasWinFsp)
return !hasWinFsp return !hasWinFsp
} }
return false return false
} }
export async function dialogGetMountPlugin() { export async function dialogGetMountPlugin() {
console.log('[dialogGetMountPlugin]')
const currentPlatform = platform() const currentPlatform = platform()
if (currentPlatform === 'macos') { if (currentPlatform === 'macos') {
// download fuse-t or osxfuse // download fuse-t or osxfuse
@@ -75,13 +79,12 @@ export async function dialogGetMountPlugin() {
} }
export async function unmount(mountPoint: string, force = false) { export async function unmount(mountPoint: string, force = false) {
console.log('[unmount]', mountPoint, force)
const command = Command.create('umount', [force ? '-f' : '', mountPoint]) const command = Command.create('umount', [force ? '-f' : '', mountPoint])
const output = await command.execute() const output = await command.execute()
// console.log('output')
// console.log(JSON.stringify(output, null, 2))
if (output.code !== 0) { if (output.code !== 0) {
if (output.stderr.toLowerCase().includes('busy')) { if (output.stderr.toLowerCase().includes('busy')) {
const answer = await ask('This resource is busy, do you wish to force unmount?', { const answer = await ask('This resource is busy, do you wish to force unmount?', {
@@ -97,9 +100,11 @@ export async function unmount(mountPoint: string, force = false) {
} }
if (output.stderr.toLowerCase().includes('not currently mounted')) { if (output.stderr.toLowerCase().includes('not currently mounted')) {
console.error('[unmount] not currently mounted')
return return
} }
console.error('[unmount] failed to unmount', output.stderr)
throw new Error(output.stderr) throw new Error(output.stderr)
} }
+17 -8
View File
@@ -24,19 +24,23 @@ export async function triggerTrayRebuild() {
}) })
} }
// Function to update the tray menu
export async function rebuildTrayMenu() { export async function rebuildTrayMenu() {
console.log('[rebuildTrayMenu]')
const tray = await getMainTray() const tray = await getMainTray()
if (!tray) { if (!tray) {
console.error('[rebuildTrayMenu] tray not found')
return return
} }
const newMenu = await buildMenu() const newMenu = await buildMenu()
await tray.setMenu(newMenu) await tray.setMenu(newMenu)
console.log('[rebuildTrayMenu] tray menu rebuilt')
} }
async function onTrayAction(event: TrayIconEvent) { async function onTrayAction(event: TrayIconEvent) {
if (event.type === 'Click') { if (event.type === 'Click') {
console.log('Tray clicked:', event) console.log('[onTrayAction] tray clicked:', event)
await resetMainWindow() await resetMainWindow()
} }
@@ -45,12 +49,12 @@ async function onTrayAction(event: TrayIconEvent) {
// Initialize the tray // Initialize the tray
export async function initTray(): Promise<void> { export async function initTray(): Promise<void> {
try { try {
console.log('initTray') console.log('[initTray]')
const menu = await buildMenu() const menu = await buildMenu()
console.log('built menu') console.log('[initTray] built menu')
await TrayIcon.getById('loading-tray').then((t) => t?.setVisible(false)) await TrayIcon.getById('loading-tray').then((t) => t?.setVisible(false))
console.log('set loading tray to false') console.log('[initTray] set loading tray to false')
await TrayIcon.new({ await TrayIcon.new({
id: 'main-tray', id: 'main-tray',
@@ -61,13 +65,18 @@ export async function initTray(): Promise<void> {
action: onTrayAction, action: onTrayAction,
}) })
} catch (error) { } catch (error) {
console.error('Failed to create tray') console.error('[initTray] failed to create tray')
console.error(error) console.error(error)
} }
} }
export async function initLoadingTray() { export async function initLoadingTray() {
if (platform() === 'linux') return console.log('[initLoadingTray]')
if (platform() === 'linux') {
console.log('[initLoadingTray] platform is linux, skipping')
return
}
const globeIconPath = await resolveResource('icons/favicon/frame_00_delay-0.1s.png') const globeIconPath = await resolveResource('icons/favicon/frame_00_delay-0.1s.png')
@@ -104,6 +113,6 @@ export async function initLoadingTray() {
`icons/favicon/frame_${currentIcon < 10 ? '0' : ''}${currentIcon}_delay-0.1s.png` `icons/favicon/frame_${currentIcon < 10 ? '0' : ''}${currentIcon}_delay-0.1s.png`
) )
await loadingTray?.setIcon(globeIconPath) await loadingTray?.setIcon(globeIconPath)
currentIcon = currentIcon + 1 currentIcon += 1
}, 200) }, 200)
} }
+10 -3
View File
@@ -21,6 +21,8 @@ export async function openFullWindow({
name: string name: string
url: string url: string
}) { }) {
console.log('[openFullWindow]')
const w = new WebviewWindow(name, { const w = new WebviewWindow(name, {
height: 0, height: 0,
width: 0, width: 0,
@@ -36,7 +38,10 @@ export async function openFullWindow({
const size = await currentMonitor().then((m) => m?.size) const size = await currentMonitor().then((m) => m?.size)
if (!size) return if (!size) {
console.error('[openFullWindow] no monitor found')
throw new Error('No monitor found')
}
if (platform() === 'windows') { if (platform() === 'windows') {
// windows merges the space for the taskbar // windows merges the space for the taskbar
@@ -55,14 +60,16 @@ export async function openFullWindow({
export async function openWindow({ export async function openWindow({
name, name,
url, url,
width = 740, width = 820,
height = 600, height = 700,
}: { }: {
name: string name: string
url: string url: string
width?: number width?: number
height?: number height?: number
}) { }) {
console.log('[openWindow]')
const isFirstWindow = useStore.getState().firstWindow const isFirstWindow = useStore.getState().firstWindow
const w = new WebviewWindow(name, { const w = new WebviewWindow(name, {
+22 -16
View File
@@ -86,9 +86,11 @@ async function validateInstance() {
} }
async function startRclone() { async function startRclone() {
console.log('[startRclone]')
try { try {
const remotes = await listRemotes() const remotes = await listRemotes()
console.log('rclone rcd already running') console.log('[startRclone] rclone rcd already running')
useStore.setState({ rcloneLoaded: true }) useStore.setState({ rcloneLoaded: true })
useStore.setState({ remotes: remotes }) useStore.setState({ remotes: remotes })
return return
@@ -99,7 +101,7 @@ async function startRclone() {
try { try {
rclone = await initRclone() rclone = await initRclone()
} catch (error) { } catch (error) {
await ask(error.message || 'Failed to provision rclone, please try again later.', { await ask(error.message || 'Failed to start rclone, please try again later.', {
title: 'Error', title: 'Error',
kind: 'error', kind: 'error',
okLabel: 'Exit', okLabel: 'Exit',
@@ -149,11 +151,12 @@ async function startRclone() {
console.log('error', event) console.log('error', event)
}) })
console.log('running rclone') console.log('[startRclone] starting rclone')
const childProcess = await command.spawn() const childProcess = await command.spawn()
console.log('[startRclone] running rclone')
getCurrentWindow().listen('close-app', async (e) => { getCurrentWindow().listen('close-app', async (e) => {
console.log('(main) window close-app requested') console.log('[startRclone] (main) window close-app requested')
if (rclone.system) { if (rclone.system) {
const answer = await ask('Unmount all remotes before exiting?', { const answer = await ask('Unmount all remotes before exiting?', {
@@ -169,13 +172,15 @@ async function startRclone() {
await childProcess.kill() await childProcess.kill()
}) })
console.log('[startRclone] set listener for close-app')
await new Promise((resolve) => setTimeout(resolve, 200)) await new Promise((resolve) => setTimeout(resolve, 200))
useStore.setState({ rcloneLoaded: true }) useStore.setState({ rcloneLoaded: true })
console.log('[startRclone] listing remotes')
const remotes = await listRemotes() const remotes = await listRemotes()
console.log('remotes', remotes) console.log('[startRclone] got remotes')
useStore.setState({ remotes: remotes }) useStore.setState({ remotes: remotes })
// console.log('childProcess', JSON.stringify(childProcess)) // prints `pid` // console.log('childProcess', JSON.stringify(childProcess)) // prints `pid`
@@ -210,6 +215,17 @@ async function startupMounts() {
} }
} }
async function onboardUser() {
const firstOpen = usePersistedStore.getState().isFirstOpen
if (firstOpen) {
await message('Rclone has initialized, you can now find it in the tray menu!', {
title: 'Welcome to Rclone UI',
kind: 'info',
okLabel: 'Got it',
})
usePersistedStore.setState({ isFirstOpen: false })
}
}
getCurrentWindow().listen('tauri://close-requested', async (e) => { getCurrentWindow().listen('tauri://close-requested', async (e) => {
console.log('(main) window close requested') console.log('(main) window close requested')
}) })
@@ -235,17 +251,7 @@ initLoadingTray()
.then(() => waitForHydration()) .then(() => waitForHydration())
.then(() => validateInstance()) .then(() => validateInstance())
.then(() => startRclone()) .then(() => startRclone())
.then(async () => { .then(() => onboardUser())
const firstOpen = usePersistedStore.getState().isFirstOpen
if (firstOpen) {
await message('Rclone has initialized, you can now find it in the tray menu!', {
title: 'Welcome',
kind: 'info',
okLabel: 'Got it',
})
usePersistedStore.setState({ isFirstOpen: false })
}
})
.then(() => startupMounts()) .then(() => startupMounts())
.then(() => initTray()) .then(() => initTray())
.catch(console.error) .catch(console.error)
+3974 -2653
View File
File diff suppressed because it is too large Load Diff
+28 -25
View File
@@ -19,42 +19,45 @@
"build:wasm": "tauri build --target wasm32-unknown-unknown" "build:wasm": "tauri build --target wasm32-unknown-unknown"
}, },
"dependencies": { "dependencies": {
"@nextui-org/react": "^2.6.11", "@heroui/react": "^2.7.11",
"@tauri-apps/api": "~2.2.0", "@tauri-apps/api": "~2.7.0",
"@tauri-apps/plugin-autostart": "^2.2.0", "@tauri-apps/plugin-autostart": "^2.5.0",
"@tauri-apps/plugin-dialog": "^2.2.0", "@tauri-apps/plugin-dialog": "^2.3.2",
"@tauri-apps/plugin-fs": "^2.2.0", "@tauri-apps/plugin-fs": "^2.4.1",
"@tauri-apps/plugin-http": "^2.2.0", "@tauri-apps/plugin-http": "^2.5.1",
"@tauri-apps/plugin-log": "^2.2.0", "@tauri-apps/plugin-log": "^2.6.0",
"@tauri-apps/plugin-notification": "^2.2.1", "@tauri-apps/plugin-notification": "^2.3.0",
"@tauri-apps/plugin-opener": "^2.2.5", "@tauri-apps/plugin-opener": "^2.4.0",
"@tauri-apps/plugin-os": "^2.2.0", "@tauri-apps/plugin-os": "^2.3.0",
"@tauri-apps/plugin-process": "^2.2.0", "@tauri-apps/plugin-process": "^2.3.0",
"@tauri-apps/plugin-shell": "^2.2.0", "@tauri-apps/plugin-shell": "^2.3.0",
"@tauri-apps/plugin-store": "^2.2.0", "@tauri-apps/plugin-store": "^2.3.0",
"@tauri-apps/plugin-updater": "^2.4.0", "@tauri-apps/plugin-updater": "^2.9.0",
"@tauri-apps/plugin-window-state": "^2.2.0", "@tauri-apps/plugin-window-state": "^2.4.0",
"framer-motion": "^11.17.0", "cron-parser": "^5.3.0",
"lucide-react": "^0.471.0", "cronstrue": "^3.2.0",
"date-fns": "^4.1.0",
"framer-motion": "^11.18.2",
"lucide-react": "^0.536.0",
"p-retry": "^6.2.1", "p-retry": "^6.2.1",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.28.1", "react-router-dom": "^6.28.1",
"use-broadcast-ts": "^2.0.0", "use-broadcast-ts": "^2.0.1",
"zustand": "^5.0.3" "zustand": "^5.0.7"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@biomejs/biome": "^1.9.4",
"@tauri-apps/cli": "~2.2.5", "@tauri-apps/cli": "~2.7.1",
"@types/node": "^22.10.7", "@types/node": "^22.10.7",
"@types/react": "^18.3.1", "@types/react": "^18.3.1",
"@types/react-dom": "^18.3.1", "@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.4", "@vitejs/plugin-react": "^4.7.0",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.21",
"esbuild": "^0.24.2", "esbuild": "^0.25.8",
"postcss": "^8.4.49", "postcss": "^8.5.6",
"tailwindcss": "^3.4.17", "tailwindcss": "^3.4.17",
"typescript": "~5.6.2", "typescript": "~5.6.2",
"vite": "^6.0.3" "vite": "^6.3.5"
} }
} }
+609 -421
View File
File diff suppressed because it is too large Load Diff
+14 -14
View File
@@ -21,25 +21,25 @@ tauri-build = { version = "2.0.4", features = [] }
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
log = "0.4" log = "0.4"
tauri = { version = "2.2.0", features = [ "tray-icon", "image-ico", tauri = { version = "2.7.0", features = [ "tray-icon", "image-ico",
"image-png", "config-json5" ] } "image-png", "config-json5" ] }
tauri-plugin-log = "2.2.0" tauri-plugin-log = "2.6.0"
tauri-plugin-shell = "2.2.0" tauri-plugin-shell = "2.3.0"
tauri-plugin-dialog = "2.2.0" tauri-plugin-dialog = "2.3.2"
tauri-plugin-fs = "2.2.0" tauri-plugin-fs = "2.4.1"
tauri-plugin-opener = "2.2.3" tauri-plugin-opener = "2.4.0"
tauri-plugin-http = "2.2.0" tauri-plugin-http = "2.5.1"
tauri-plugin-store = "2.2.0" tauri-plugin-store = "2.3.0"
tauri-plugin-process = "2" tauri-plugin-process = "2.3.0"
tauri-plugin-notification = { version = "2.0.0", features = [ "windows7-compat" ] } tauri-plugin-notification = { version = "2.3.0", features = [ "windows7-compat" ] }
tauri-plugin-os = "2" tauri-plugin-os = "2.3.0"
fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" } fix-path-env = { git = "https://github.com/tauri-apps/fix-path-env-rs" }
zip = "0.6.6" zip = "0.6.6"
machine-uid = "0.5.3" machine-uid = "0.5.3"
tauri-plugin-sentry = "0.4.1" tauri-plugin-sentry = "0.4.1"
tauri-plugin-autostart = "2" tauri-plugin-autostart = "2.5.0"
tauri-plugin-single-instance = "2.2.0" tauri-plugin-single-instance = "2.3.2"
tauri-plugin-updater = "2" tauri-plugin-updater = "2.9.0"
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
cocoa = "0.26" cocoa = "0.26"
+1 -1
View File
@@ -1,4 +1,4 @@
import { Chip, Textarea, Tooltip } from '@nextui-org/react' import { Chip, Textarea, Tooltip } from '@heroui/react'
import { LockKeyholeIcon, LockOpenIcon, XIcon } from 'lucide-react' import { LockKeyholeIcon, LockOpenIcon, XIcon } from 'lucide-react'
import { useCallback, useEffect, useState } from 'react' import { useCallback, useEffect, useState } from 'react'
import { replaceSmartQuotes } from '../../lib/format' import { replaceSmartQuotes } from '../../lib/format'
+3 -2
View File
@@ -1,5 +1,6 @@
import { Autocomplete } from '@nextui-org/autocomplete' import { Autocomplete, Tooltip } from '@heroui/react'
import { AutocompleteItem, Button } from '@nextui-org/react' import { AutocompleteItem, Button } from '@heroui/react'
import { cn } from '@heroui/react'
import { open } from '@tauri-apps/plugin-dialog' import { open } from '@tauri-apps/plugin-dialog'
import { readDir } from '@tauri-apps/plugin-fs' import { readDir } from '@tauri-apps/plugin-fs'
import { platform } from '@tauri-apps/plugin-os' import { platform } from '@tauri-apps/plugin-os'
+5 -50
View File
@@ -1,4 +1,4 @@
import { Drawer, DrawerBody, DrawerFooter, DrawerHeader } from '@nextui-org/drawer' import { Drawer, DrawerBody, DrawerFooter, DrawerHeader } from '@heroui/react'
import { import {
Autocomplete, Autocomplete,
AutocompleteItem, AutocompleteItem,
@@ -8,7 +8,7 @@ import {
Input, Input,
Select, Select,
SelectItem, SelectItem,
} from '@nextui-org/react' } from '@heroui/react'
import { message } from '@tauri-apps/plugin-dialog' import { message } from '@tauri-apps/plugin-dialog'
import { ChevronDown, ChevronUp, RefreshCcwIcon } from 'lucide-react' import { ChevronDown, ChevronUp, RefreshCcwIcon } from 'lucide-react'
import { useEffect, useMemo, useState } from 'react' import { useEffect, useMemo, useState } from 'react'
@@ -73,20 +73,6 @@ export default function RemoteCreateDrawer({
</p> </p>
)} )}
</div> </div>
// <input
// type="checkbox"
// id={fieldId}
// name={option.Name}
// className="form-checkbox"
// defaultChecked={fieldValue === 'true'}
// onChange={(e) =>
// setConfig({ ...config, [option.Name]: e.target.checked })
// }
// autoComplete="off"
// autoCapitalize="off"
// autoCorrect="off"
// spellCheck={false}
// />
) )
case 'string': { case 'string': {
if (option.Examples && option.Examples.length > 0) { if (option.Examples && option.Examples.length > 0) {
@@ -127,7 +113,7 @@ export default function RemoteCreateDrawer({
autoComplete="off" autoComplete="off"
autoCapitalize="off" autoCapitalize="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
description={option.Help.split('\n').slice(1).join('\n')} description={option.Help.split('\n').slice(1).join('\n')}
isRequired={option.Required} isRequired={option.Required}
onValueChange={(value) => { onValueChange={(value) => {
@@ -136,37 +122,6 @@ export default function RemoteCreateDrawer({
}} }}
/> />
) )
// if (option.Examples && option.Examples.length > 0) {
// return (
// <select
// id={fieldId}
// name={option.Name}
// className="w-full p-2 border rounded dark:bg-gray-800"
// value={fieldValue}
// onChange={(e) =>
// setConfig({ ...config, [option.Name]: e.target.value })
// }
// >
// <option value="">Select {option.Name}</option>
// {option.Examples.map((example) => (
// <option key={example.Value} value={example.Value}>
// {example.Help || example.Value}
// </option>
// ))}
// </select>
// )
// }
// return (
// <input
// id={fieldId}
// name={option.Name}
// type={option.IsPassword ? 'password' : 'text'}
// value={fieldValue || ''}
// onChange={(e) =>
// setConfig({ ...config, [option.Name]: e.target.value })
// }
// />
// )
} }
default: default:
return null return null
@@ -275,7 +230,7 @@ export default function RemoteCreateDrawer({
autoComplete="off" autoComplete="off"
autoCapitalize="off" autoCapitalize="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
/> />
<Select <Select
@@ -290,7 +245,7 @@ export default function RemoteCreateDrawer({
isRequired={true} isRequired={true}
> >
{backends.map((backend) => ( {backends.map((backend) => (
<SelectItem key={backend.Name} value={backend.Name}> <SelectItem key={backend.Name}>
{backend.Description.includes('Compliant') {backend.Description.includes('Compliant')
? `${backend.Description.split('Compliant')[0]} Compliant` ? `${backend.Description.split('Compliant')[0]} Compliant`
: backend.Description || backend.Name} : backend.Description || backend.Name}
+6 -14
View File
@@ -1,6 +1,6 @@
import { Checkbox } from '@nextui-org/checkbox' import { Checkbox } from '@heroui/react'
import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader } from '@nextui-org/drawer' import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader } from '@heroui/react'
import { Accordion, AccordionItem, Avatar, Button, Input } from '@nextui-org/react' import { Accordion, AccordionItem, Avatar, Button, Input } from '@heroui/react'
import { homeDir } from '@tauri-apps/api/path' import { homeDir } from '@tauri-apps/api/path'
import { message, open } from '@tauri-apps/plugin-dialog' import { message, open } from '@tauri-apps/plugin-dialog'
import { import {
@@ -82,7 +82,6 @@ export default function RemoteDefaultsDrawer({
const handleSubmit = useCallback(async () => { const handleSubmit = useCallback(async () => {
if (!config) { if (!config) {
console.log('No config') console.log('No config')
// console.log(JSON.stringify(config, null, 2))
return return
} }
@@ -309,7 +308,7 @@ export default function RemoteDefaultsDrawer({
autoComplete="off" autoComplete="off"
autoCapitalize="off" autoCapitalize="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
onValueChange={(value) => { onValueChange={(value) => {
setConfig((prev) => ({ setConfig((prev) => ({
...prev, ...prev,
@@ -330,7 +329,7 @@ export default function RemoteDefaultsDrawer({
autoComplete="off" autoComplete="off"
autoCapitalize="off" autoCapitalize="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
startContent={ startContent={
<Button <Button
onPress={async () => { onPress={async () => {
@@ -340,6 +339,7 @@ export default function RemoteDefaultsDrawer({
directory: true, directory: true,
multiple: false, multiple: false,
defaultPath: await homeDir(), defaultPath: await homeDir(),
title: 'Select a mount point',
}) })
await unlockWindows() await unlockWindows()
if (selected) { if (selected) {
@@ -511,14 +511,6 @@ export default function RemoteDefaultsDrawer({
rows={20} rows={20}
/> />
</AccordionItem> </AccordionItem>
{/* <SyncSection
remoteName={remoteName}
syncOptionsJson={syncOptionsJson}
setSyncOptionsJson={setSyncOptionsJson}
globalOptions={
globalOptions['main' as keyof typeof globalOptions]
}
/> */}
</Accordion> </Accordion>
</DrawerBody> </DrawerBody>
<DrawerFooter> <DrawerFooter>
+5 -16
View File
@@ -1,13 +1,6 @@
import { Checkbox } from '@nextui-org/checkbox' import { Checkbox } from '@heroui/react'
import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader } from '@nextui-org/drawer' import { Drawer, DrawerBody, DrawerContent, DrawerFooter, DrawerHeader } from '@heroui/react'
import { import { Autocomplete, AutocompleteItem, Button, Input, Select, SelectItem } from '@heroui/react'
Autocomplete,
AutocompleteItem,
Button,
Input,
Select,
SelectItem,
} from '@nextui-org/react'
import { message } from '@tauri-apps/plugin-dialog' import { message } from '@tauri-apps/plugin-dialog'
import { ChevronDown, ChevronUp } from 'lucide-react' import { ChevronDown, ChevronUp } from 'lucide-react'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
@@ -115,7 +108,7 @@ export default function RemoteEditDrawer({
autoComplete="off" autoComplete="off"
autoCapitalize="off" autoCapitalize="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
description={option.Help.split('\n').slice(1).join('\n')} description={option.Help.split('\n').slice(1).join('\n')}
/> />
) )
@@ -204,14 +197,10 @@ export default function RemoteEditDrawer({
selectionMode="single" selectionMode="single"
placeholder="Select Type" placeholder="Select Type"
selectedKeys={[config.type]} selectedKeys={[config.type]}
// onChange={(e) => {
// console.log(e.target.value)
// setConfig({ ...config, type: e.target.value })
// }}
isDisabled={true} isDisabled={true}
> >
{backends.map((backend) => ( {backends.map((backend) => (
<SelectItem key={backend.Name} value={backend.Name}> <SelectItem key={backend.Name}>
{backend.Description.includes('Compliant') {backend.Description.includes('Compliant')
? `${backend.Description.split('Compliant')[0]} Compliant` ? `${backend.Description.split('Compliant')[0]} Compliant`
: backend.Description || backend.Name} : backend.Description || backend.Name}
+3 -3
View File
@@ -3,7 +3,7 @@ import ReactDOM from 'react-dom/client'
import { RouterProvider, createBrowserRouter } from 'react-router-dom' import { RouterProvider, createBrowserRouter } from 'react-router-dom'
import Home from './pages/Home' import Home from './pages/Home'
import './global.css' import './global.css'
import { NextUIProvider } from '@nextui-org/react' import { HeroUIProvider } from '@heroui/react'
import { debug, error, info, trace, warn } from '@tauri-apps/plugin-log' import { debug, error, info, trace, warn } from '@tauri-apps/plugin-log'
import Copy from './pages/Copy' import Copy from './pages/Copy'
import Jobs from './pages/Jobs' import Jobs from './pages/Jobs'
@@ -64,9 +64,9 @@ const router = createBrowserRouter([
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render( ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode> <React.StrictMode>
<NextUIProvider> <HeroUIProvider>
{/* <TauriWatcher /> */} {/* <TauriWatcher /> */}
<RouterProvider router={router} /> <RouterProvider router={router} />
</NextUIProvider> </HeroUIProvider>
</React.StrictMode> </React.StrictMode>
) )
+1 -1
View File
@@ -1,4 +1,4 @@
import { Accordion, AccordionItem, Avatar, Button } from '@nextui-org/react' import { Accordion, AccordionItem, Avatar, Button } from '@heroui/react'
import { message } from '@tauri-apps/plugin-dialog' import { message } from '@tauri-apps/plugin-dialog'
import { exists } from '@tauri-apps/plugin-fs' import { exists } from '@tauri-apps/plugin-fs'
import { AlertOctagonIcon, CopyIcon, FilterIcon, FoldersIcon, PlayIcon } from 'lucide-react' import { AlertOctagonIcon, CopyIcon, FilterIcon, FoldersIcon, PlayIcon } from 'lucide-react'
+3 -3
View File
@@ -1,5 +1,5 @@
import { Card, CardBody, CardFooter, CardHeader } from '@nextui-org/card' import { Card, CardBody, CardFooter, CardHeader } from '@heroui/react'
import { Button, Chip, Divider, Progress, Spinner } from '@nextui-org/react' import { Button, Chip, Divider, Progress, Spinner } from '@heroui/react'
import { listen } from '@tauri-apps/api/event' import { listen } from '@tauri-apps/api/event'
import { ask } from '@tauri-apps/plugin-dialog' import { ask } from '@tauri-apps/plugin-dialog'
import { Trash2Icon } from 'lucide-react' import { Trash2Icon } from 'lucide-react'
@@ -50,7 +50,7 @@ export default function Jobs() {
await fetchJobs() await fetchJobs()
}, 2000) }, 2000)
//! prevents jobs being refreshed after "X" was pressed on the window // prevents jobs being refreshed after closing the window
const unlisten = listen('tauri://close-requested', () => { const unlisten = listen('tauri://close-requested', () => {
clearInterval(interval) clearInterval(interval)
}) })
+3 -3
View File
@@ -1,4 +1,4 @@
import { Accordion, AccordionItem, Avatar, Button } from '@nextui-org/react' import { Accordion, AccordionItem, Avatar, Button } from '@heroui/react'
import { getCurrentWindow } from '@tauri-apps/api/window' import { getCurrentWindow } from '@tauri-apps/api/window'
import { message } from '@tauri-apps/plugin-dialog' import { message } from '@tauri-apps/plugin-dialog'
import { exists, mkdir, remove } from '@tauri-apps/plugin-fs' import { exists, mkdir, remove } from '@tauri-apps/plugin-fs'
@@ -201,7 +201,7 @@ export default function Mount() {
return ( return (
<div className="flex flex-col h-screen gap-10 pt-10"> <div className="flex flex-col h-screen gap-10 pt-10">
{/* Main Content */} {/* Main Content */}
<div className="flex flex-col flex-1 w-full max-w-xl gap-6 mx-auto"> <div className="flex flex-col flex-1 w-full max-w-3xl gap-6 mx-auto">
{/* Paths Display */} {/* Paths Display */}
<PathFinder <PathFinder
sourcePath={source} sourcePath={source}
@@ -240,7 +240,7 @@ export default function Mount() {
setOptionsJson={setMountOptionsJson} setOptionsJson={setMountOptionsJson}
globalOptions={globalOptions['mount' as keyof typeof globalOptions]} globalOptions={globalOptions['mount' as keyof typeof globalOptions]}
optionsFetcher={getMountFlags} optionsFetcher={getMountFlags}
rows={5} rows={7}
isLocked={mountOptionsLocked} isLocked={mountOptionsLocked}
setIsLocked={setMountOptionsLocked} setIsLocked={setMountOptionsLocked}
/> />
+8 -28
View File
@@ -1,4 +1,4 @@
import { Button, Card, CardBody, Checkbox, Chip, Input, Tab, Tabs } from '@nextui-org/react' import { Button, Card, CardBody, Checkbox, Chip, Input, Tab, Tabs } from '@heroui/react'
import { ask, message } from '@tauri-apps/plugin-dialog' import { ask, message } from '@tauri-apps/plugin-dialog'
import { openUrl } from '@tauri-apps/plugin-opener' import { openUrl } from '@tauri-apps/plugin-opener'
import { relaunch } from '@tauri-apps/plugin-process' import { relaunch } from '@tauri-apps/plugin-process'
@@ -38,7 +38,7 @@ function Settings() {
autoCapitalize="none" autoCapitalize="none"
autoComplete="off" autoComplete="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
type={passwordVisible ? 'text' : 'password'} type={passwordVisible ? 'text' : 'password'}
fullWidth={false} fullWidth={false}
size="lg" size="lg"
@@ -75,20 +75,22 @@ function Settings() {
} }
return ( return (
<div className="flex flex-col w-screen h-screen gap-0 overflow-hidden animate-fade-in"> <div className="relative flex flex-col w-screen h-screen gap-0 overflow-hidden">
<Tabs <Tabs
aria-label="Options" aria-label="Options"
isVertical={true} isVertical={true}
variant="light" variant="light"
destroyInactiveTabPanel={false} destroyInactiveTabPanel={false}
disableAnimation={true} disableAnimation={true}
className="flex-shrink-0 w-3/12 h-screen px-2 py-4 border-r border-neutral-700" className="flex-shrink-0 w-40 h-screen px-2 py-4 border-r border-neutral-700"
classNames={{ classNames={{
tabList: 'w-full gap-3', tabList: 'w-full gap-3',
tab: 'h-14',
}} }}
size="lg" size="lg"
defaultSelectedKey="general" defaultSelectedKey="general"
color="secondary" color="secondary"
radius="sm"
> >
<Tab <Tab
key="general" key="general"
@@ -276,7 +278,7 @@ function GeneralSection() {
autoCapitalize="none" autoCapitalize="none"
autoComplete="off" autoComplete="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
type={passwordVisible ? 'text' : 'password'} type={passwordVisible ? 'text' : 'password'}
endContent={ endContent={
passwordInput && ( passwordInput && (
@@ -454,7 +456,7 @@ function LicenseSection() {
autoCapitalize="none" autoCapitalize="none"
autoComplete="off" autoComplete="off"
autoCorrect="off" autoCorrect="off"
spellCheck={false} spellCheck="false"
endContent={ endContent={
licenseValid && <CheckIcon className="w-5 h-5 text-green-500" /> licenseValid && <CheckIcon className="w-5 h-5 text-green-500" />
} }
@@ -806,25 +808,3 @@ function BaseHeader({ title, endContent }: { title: string; endContent?: React.R
} }
export default Settings export default Settings
// {/* <Button
// onPress={async () => {
// const enabled = await isEnabled()
// if (enabled) {
// await disable()
// } else {
// await enable()
// }
// }}
// >
// Start on boot
// </Button>
// <Button
// onPress={async () => {
// const enabled = await isEnabled()
// alert(enabled ? 'Enabled' : 'Disabled')
// }}
// >
// Get status
// </Button> */}
+2 -2
View File
@@ -1,4 +1,4 @@
import { Accordion, AccordionItem, Avatar, Button } from '@nextui-org/react' import { Accordion, AccordionItem, Avatar, Button } from '@heroui/react'
import { message } from '@tauri-apps/plugin-dialog' import { message } from '@tauri-apps/plugin-dialog'
import { exists } from '@tauri-apps/plugin-fs' import { exists } from '@tauri-apps/plugin-fs'
import { AlertOctagonIcon, FilterIcon, FolderSyncIcon, FoldersIcon, PlayIcon } from 'lucide-react' import { AlertOctagonIcon, FilterIcon, FolderSyncIcon, FoldersIcon, PlayIcon } from 'lucide-react'
@@ -188,7 +188,7 @@ export default function Sync() {
return ( return (
<div className="flex flex-col h-screen gap-10 pt-10"> <div className="flex flex-col h-screen gap-10 pt-10">
{/* Main Content */} {/* Main Content */}
<div className="flex flex-col flex-1 w-full max-w-xl gap-6 mx-auto"> <div className="flex flex-col flex-1 w-full max-w-3xl gap-6 mx-auto">
{/* Paths Display */} {/* Paths Display */}
<PathFinder <PathFinder
sourcePath={source} sourcePath={source}
+3 -3
View File
@@ -1,14 +1,14 @@
const { nextui } = require('@nextui-org/react') const { heroui } = require('@heroui/react')
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
export default { export default {
content: [ content: [
'./index.html', './index.html',
'./src/**/*.{js,ts,jsx,tsx}', './src/**/*.{js,ts,jsx,tsx}',
'./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}', './node_modules/@heroui/theme/dist/**/*.{js,ts,jsx,tsx}',
], ],
theme: { theme: {
extend: {}, extend: {},
}, },
plugins: [nextui()], plugins: [heroui()],
} }