mirror of
https://gh.catmak.name/https://github.com/mihomo-party-org/mihomo-party
synced 2025-12-27 13:10:30 +08:00
Compare commits
5 Commits
b272634c11
...
9f46ccf99a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9f46ccf99a | ||
|
|
96552778e6 | ||
|
|
55860af9b3 | ||
|
|
f67d4150f1 | ||
|
|
605351a498 |
46
package.json
46
package.json
@ -32,22 +32,22 @@
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.2",
|
||||
"@electron-toolkit/utils": "^4.0.0",
|
||||
"@heroui/react": "^2.8.4",
|
||||
"@heroui/react": "^2.8.5",
|
||||
"@mihomo-party/sysproxy": "^2.0.8",
|
||||
"@mihomo-party/sysproxy-darwin-arm64": "^2.0.8",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"adm-zip": "^0.5.16",
|
||||
"axios": "^1.12.0",
|
||||
"chart.js": "^4.5.0",
|
||||
"axios": "^1.12.2",
|
||||
"chart.js": "^4.5.1",
|
||||
"chokidar": "^4.0.3",
|
||||
"croner": "^9.1.0",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"express": "^5.1.0",
|
||||
"i18next": "^25.5.2",
|
||||
"i18next": "^25.6.0",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"react-chartjs-2": "^5.3.0",
|
||||
"react-i18next": "^15.7.3",
|
||||
"react-i18next": "^15.7.4",
|
||||
"webdav": "^5.8.0",
|
||||
"ws": "^8.18.3",
|
||||
"yaml": "^2.8.1"
|
||||
@ -59,49 +59,49 @@
|
||||
"@electron-toolkit/eslint-config-prettier": "^3.0.0",
|
||||
"@electron-toolkit/eslint-config-ts": "^3.1.0",
|
||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@tailwindcss/vite": "^4.1.15",
|
||||
"@types/adm-zip": "^0.5.7",
|
||||
"@types/express": "^5.0.3",
|
||||
"@types/node": "^24.3.1",
|
||||
"@types/node": "^24.9.1",
|
||||
"@types/pubsub-js": "^1.8.6",
|
||||
"@types/react": "^19.1.12",
|
||||
"@types/react-dom": "^19.1.9",
|
||||
"@types/react": "^19.2.2",
|
||||
"@types/react-dom": "^19.2.2",
|
||||
"@types/ws": "^8.18.1",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"cron-validator": "^1.4.0",
|
||||
"driver.js": "^1.3.6",
|
||||
"electron": "^37.5.0",
|
||||
"electron": "^37.7.0",
|
||||
"electron-builder": "26.0.12",
|
||||
"electron-vite": "^4.0.0",
|
||||
"electron-vite": "^4.0.1",
|
||||
"electron-window-state": "^5.0.3",
|
||||
"eslint": "9.32.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"form-data": "^4.0.4",
|
||||
"framer-motion": "12.23.12",
|
||||
"lodash": "^4.17.21",
|
||||
"meta-json-schema": "^1.19.13",
|
||||
"meta-json-schema": "^1.19.14",
|
||||
"monaco-yaml": "^5.4.0",
|
||||
"nanoid": "^5.1.5",
|
||||
"nanoid": "^5.1.6",
|
||||
"next-themes": "^0.4.6",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.2",
|
||||
"pubsub-js": "^1.9.5",
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"react": "^19.2.0",
|
||||
"react-dom": "^19.2.0",
|
||||
"react-error-boundary": "^6.0.0",
|
||||
"react-icons": "^5.5.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"react-monaco-editor": "^0.59.0",
|
||||
"react-router-dom": "^7.8.2",
|
||||
"react-virtuoso": "^4.14.0",
|
||||
"react-router-dom": "^7.9.4",
|
||||
"react-virtuoso": "^4.14.1",
|
||||
"swr": "^2.3.6",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"tar": "^7.4.3",
|
||||
"tsx": "^4.20.5",
|
||||
"tailwindcss": "^4.1.15",
|
||||
"tar": "^7.5.1",
|
||||
"tsx": "^4.20.6",
|
||||
"types-pac": "^1.0.3",
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "^7.1.5",
|
||||
"typescript": "^5.9.3",
|
||||
"vite": "^7.1.11",
|
||||
"vite-plugin-monaco-editor": "^1.1.0"
|
||||
},
|
||||
"packageManager": "pnpm@10.15.1"
|
||||
"packageManager": "pnpm@10.19.0"
|
||||
}
|
||||
|
||||
5051
pnpm-lock.yaml
generated
5051
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@ -14,6 +14,9 @@ import {
|
||||
} from '../utils/dirs'
|
||||
import { systemLogger } from '../utils/logger'
|
||||
import { Cron } from 'croner'
|
||||
import { dialog } from 'electron'
|
||||
import { existsSync } from 'fs'
|
||||
import i18next from 'i18next'
|
||||
|
||||
let backupCronJob: Cron | null = null
|
||||
|
||||
@ -196,3 +199,75 @@ export async function reinitScheduler(): Promise<void> {
|
||||
await initWebdavBackupScheduler()
|
||||
await systemLogger.info('WebDAV backup scheduler reinitialized successfully')
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出本地备份
|
||||
*/
|
||||
export async function exportLocalBackup(): Promise<boolean> {
|
||||
const zip = new AdmZip()
|
||||
if (existsSync(appConfigPath())) {
|
||||
zip.addLocalFile(appConfigPath())
|
||||
}
|
||||
if (existsSync(controledMihomoConfigPath())) {
|
||||
zip.addLocalFile(controledMihomoConfigPath())
|
||||
}
|
||||
if (existsSync(profileConfigPath())) {
|
||||
zip.addLocalFile(profileConfigPath())
|
||||
}
|
||||
if (existsSync(overrideConfigPath())) {
|
||||
zip.addLocalFile(overrideConfigPath())
|
||||
}
|
||||
if (existsSync(themesDir())) {
|
||||
zip.addLocalFolder(themesDir(), 'themes')
|
||||
}
|
||||
if (existsSync(profilesDir())) {
|
||||
zip.addLocalFolder(profilesDir(), 'profiles')
|
||||
}
|
||||
if (existsSync(overrideDir())) {
|
||||
zip.addLocalFolder(overrideDir(), 'override')
|
||||
}
|
||||
if (existsSync(subStoreDir())) {
|
||||
zip.addLocalFolder(subStoreDir(), 'substore')
|
||||
}
|
||||
|
||||
const date = new Date()
|
||||
const zipFileName = `clash-party-backup-${dayjs(date).format('YYYY-MM-DD_HH-mm-ss')}.zip`
|
||||
const result = await dialog.showSaveDialog({
|
||||
title: i18next.t('localBackup.export.title'),
|
||||
defaultPath: zipFileName,
|
||||
filters: [
|
||||
{ name: 'ZIP Files', extensions: ['zip'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
]
|
||||
})
|
||||
|
||||
if (!result.canceled && result.filePath) {
|
||||
zip.writeZip(result.filePath)
|
||||
await systemLogger.info(`Local backup exported to: ${result.filePath}`)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 导入本地备份
|
||||
*/
|
||||
export async function importLocalBackup(): Promise<boolean> {
|
||||
const result = await dialog.showOpenDialog({
|
||||
title: i18next.t('localBackup.import.title'),
|
||||
filters: [
|
||||
{ name: 'ZIP Files', extensions: ['zip'] },
|
||||
{ name: 'All Files', extensions: ['*'] }
|
||||
],
|
||||
properties: ['openFile']
|
||||
})
|
||||
|
||||
if (!result.canceled && result.filePaths.length > 0) {
|
||||
const filePath = result.filePaths[0]
|
||||
const zip = new AdmZip(filePath)
|
||||
zip.extractAllTo(dataDir(), true)
|
||||
await systemLogger.info(`Local backup imported from: ${filePath}`)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@ -356,10 +356,10 @@ export async function createTray(): Promise<void> {
|
||||
image.setTemplateImage(true)
|
||||
tray?.setImage(image)
|
||||
})
|
||||
tray?.addListener('right-click', async () => {
|
||||
tray?.addListener('click', async () => {
|
||||
triggerMainWindow()
|
||||
})
|
||||
tray?.addListener('click', async () => {
|
||||
tray?.addListener('right-click', async () => {
|
||||
await updateTrayMenu()
|
||||
})
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ import {
|
||||
setupFirewall
|
||||
} from '../sys/misc'
|
||||
import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory'
|
||||
import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore } from '../resolve/backup'
|
||||
import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore, exportLocalBackup, importLocalBackup } from '../resolve/backup'
|
||||
import { getInterfaces } from '../sys/interface'
|
||||
import { closeTrayIcon, copyEnv, showTrayIcon, updateTrayIcon, updateTrayIconImmediate } from '../resolve/tray'
|
||||
import { registerShortcut } from '../resolve/shortcut'
|
||||
@ -250,6 +250,8 @@ export function registerIpcMainHandlers(): void {
|
||||
ipcMain.handle('listWebdavBackups', ipcErrorWrapper(listWebdavBackups))
|
||||
ipcMain.handle('webdavDelete', (_e, filename) => ipcErrorWrapper(webdavDelete)(filename))
|
||||
ipcMain.handle('reinitWebdavBackupScheduler', ipcErrorWrapper(reinitScheduler))
|
||||
ipcMain.handle('exportLocalBackup', () => ipcErrorWrapper(exportLocalBackup)())
|
||||
ipcMain.handle('importLocalBackup', () => ipcErrorWrapper(importLocalBackup)())
|
||||
ipcMain.handle('registerShortcut', (_e, oldShortcut, newShortcut, action) =>
|
||||
ipcErrorWrapper(registerShortcut)(oldShortcut, newShortcut, action)
|
||||
)
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
export const defaultConfig: IAppConfig = {
|
||||
core: 'mihomo',
|
||||
enableSmartCore: true,
|
||||
enableSmartCore: false,
|
||||
enableSmartOverride: true,
|
||||
smartCoreUseLightGBM: false,
|
||||
smartCoreCollectData: false,
|
||||
|
||||
@ -130,7 +130,7 @@ const EditInfoModal: React.FC<Props> = (props) => {
|
||||
}
|
||||
}
|
||||
}}
|
||||
placeholder="例如:30 或 '0 * * * *'"
|
||||
placeholder={t('profiles.editInfo.intervalPlaceholder')}
|
||||
/>
|
||||
|
||||
{/* 动态提示信息 */}
|
||||
@ -142,13 +142,13 @@ const EditInfoModal: React.FC<Props> = (props) => {
|
||||
: '#6b7280'
|
||||
}}>
|
||||
{typeof values.interval === 'number' ? (
|
||||
'以分钟为单位的定时间隔'
|
||||
t('profiles.editInfo.intervalMinutes')
|
||||
) : /^\d+$/.test(values.interval?.toString() || '') ? (
|
||||
'以分钟为单位的定时间隔'
|
||||
t('profiles.editInfo.intervalMinutes')
|
||||
) : isValidCron(values.interval?.toString() || '', { seconds: false }) ? (
|
||||
'有效的Cron表达式'
|
||||
t('profiles.editInfo.intervalCron')
|
||||
) : (
|
||||
'请输入数字或合法的Cron表达式(如:0 * * * *)'
|
||||
t('profiles.editInfo.intervalHint')
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
81
src/renderer/src/components/settings/local-backup-config.tsx
Normal file
81
src/renderer/src/components/settings/local-backup-config.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import React, { useState } from 'react'
|
||||
import SettingCard from '../base/base-setting-card'
|
||||
import SettingItem from '../base/base-setting-item'
|
||||
import { Button, useDisclosure } from '@heroui/react'
|
||||
import { exportLocalBackup, importLocalBackup } from '@renderer/utils/ipc'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import BaseConfirmModal from '../base/base-confirm-modal'
|
||||
|
||||
const LocalBackupConfig: React.FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const { isOpen, onOpen, onClose } = useDisclosure()
|
||||
const [importing, setImporting] = useState(false)
|
||||
const [exporting, setExporting] = useState(false)
|
||||
|
||||
const handleExport = async (): Promise<void> => {
|
||||
setExporting(true)
|
||||
try {
|
||||
const success = await exportLocalBackup()
|
||||
if (success) {
|
||||
new window.Notification(t('localBackup.notification.exportSuccess.title'), {
|
||||
body: t('localBackup.notification.exportSuccess.body')
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
alert(e)
|
||||
} finally {
|
||||
setExporting(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleImport = async (): Promise<void> => {
|
||||
setImporting(true)
|
||||
try {
|
||||
const success = await importLocalBackup()
|
||||
if (success) {
|
||||
new window.Notification(t('localBackup.notification.importSuccess.title'), {
|
||||
body: t('localBackup.notification.importSuccess.body')
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
alert(t('common.error.importFailed', { error: e }))
|
||||
} finally {
|
||||
setImporting(false)
|
||||
onClose()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseConfirmModal
|
||||
isOpen={isOpen}
|
||||
onCancel={onClose}
|
||||
onConfirm={handleImport}
|
||||
title={t('localBackup.import.confirm.title')}
|
||||
content={t('localBackup.import.confirm.body')}
|
||||
/>
|
||||
<SettingCard title={t('localBackup.title')}>
|
||||
<SettingItem title={t('localBackup.export.title')} divider>
|
||||
<Button
|
||||
isLoading={exporting}
|
||||
size="sm"
|
||||
onPress={handleExport}
|
||||
>
|
||||
{t('localBackup.export.button')}
|
||||
</Button>
|
||||
</SettingItem>
|
||||
<SettingItem title={t('localBackup.import.title')}>
|
||||
<Button
|
||||
isLoading={importing}
|
||||
size="sm"
|
||||
onPress={onOpen}
|
||||
>
|
||||
{t('localBackup.import.button')}
|
||||
</Button>
|
||||
</SettingItem>
|
||||
</SettingCard>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LocalBackupConfig
|
||||
@ -239,6 +239,17 @@
|
||||
"webdav.restore.noBackups": "No backups available",
|
||||
"webdav.notification.backupSuccess.title": "Backup Successful",
|
||||
"webdav.notification.backupSuccess.body": "Backup file has been uploaded to WebDAV",
|
||||
"localBackup.title": "Backup & Restore",
|
||||
"localBackup.export.title": "Export Local Backup",
|
||||
"localBackup.export.button": "Export Backup",
|
||||
"localBackup.import.title": "Import Local Backup",
|
||||
"localBackup.import.button": "Import Backup",
|
||||
"localBackup.import.confirm.title": "Confirm Import",
|
||||
"localBackup.import.confirm.body": "Importing local backup will overwrite all current configurations. Are you sure you want to continue?",
|
||||
"localBackup.notification.exportSuccess.title": "Export Successful",
|
||||
"localBackup.notification.exportSuccess.body": "Local backup has been exported to the specified location",
|
||||
"localBackup.notification.importSuccess.title": "Import Successful",
|
||||
"localBackup.notification.importSuccess.body": "Local backup has been successfully imported",
|
||||
"shortcuts.title": "Keyboard Shortcuts",
|
||||
"shortcuts.toggleWindow": "Toggle Window",
|
||||
"shortcuts.toggleFloatingWindow": "Toggle Floating Window",
|
||||
@ -425,6 +436,11 @@
|
||||
"profiles.editInfo.url": "Subscription URL",
|
||||
"profiles.editInfo.useProxy": "Use Proxy to Update",
|
||||
"profiles.editInfo.interval": "Upd. Interval",
|
||||
"profiles.editInfo.intervalPlaceholder": "e.g.: 30 or '0 * * * *'",
|
||||
"profiles.editInfo.intervalInvalid": "Invalid",
|
||||
"profiles.editInfo.intervalMinutes": "Fixed interval in minutes",
|
||||
"profiles.editInfo.intervalCron": "Valid Cron expression",
|
||||
"profiles.editInfo.intervalHint": "Please enter a number or a valid Cron expression (e.g.: 0 * * * *)",
|
||||
"profiles.editInfo.fixedInterval": "Fixed Update Interval",
|
||||
"profiles.editInfo.override.title": "Override",
|
||||
"profiles.editInfo.override.global": "Global",
|
||||
|
||||
@ -217,6 +217,17 @@
|
||||
"webdav.restore.noBackups": "هیچ پشتیبانی موجود نیست",
|
||||
"webdav.notification.backupSuccess.title": "پشتیبانگیری موفق",
|
||||
"webdav.notification.backupSuccess.body": "فایل پشتیبان در WebDAV بارگذاری شد",
|
||||
"localBackup.title": "پشتیبانگیری و بازیابی",
|
||||
"localBackup.export.title": "صادر کردن پشتیبان محلی",
|
||||
"localBackup.export.button": "صادر کردن پشتیبان",
|
||||
"localBackup.import.title": "وارد کردن پشتیبان محلی",
|
||||
"localBackup.import.button": "وارد کردن پشتیبان",
|
||||
"localBackup.import.confirm.title": "تایید وارد کردن",
|
||||
"localBackup.import.confirm.body": "وارد کردن پشتیبان محلی، تمام پیکربندیهای فعلی را بازنویسی میکند. آیا مطمئن هستید که میخواهید ادامه دهید؟",
|
||||
"localBackup.notification.exportSuccess.title": "صادر کردن موفق",
|
||||
"localBackup.notification.exportSuccess.body": "پشتیبان محلی در مکان مشخص شده صادر شد",
|
||||
"localBackup.notification.importSuccess.title": "وارد کردن موفق",
|
||||
"localBackup.notification.importSuccess.body": "پشتیبان محلی با موفقیت وارد شد",
|
||||
"shortcuts.title": "میانبرهای صفحه کلید",
|
||||
"shortcuts.toggleWindow": "تغییر وضعیت پنجره",
|
||||
"shortcuts.toggleFloatingWindow": "تغییر وضعیت پنجره شناور",
|
||||
@ -408,8 +419,13 @@
|
||||
"profiles.editInfo.fixedInterval": "فاصله بهروزرسانی ثابت",
|
||||
"profiles.editInfo.override.title": "جایگزینی",
|
||||
"profiles.editInfo.override.global": "جهانی",
|
||||
"profiles.editInfo.override.noAvailable": "جایگزینی در دسترس نیست",
|
||||
"profiles.editInfo.override.noAvailable": "جایگزینیای در دسترس نیست",
|
||||
"profiles.editInfo.override.add": "افزودن جایگزینی",
|
||||
"profiles.editInfo.intervalPlaceholder": "مثال: 30 یا '0 * * * *'",
|
||||
"profiles.editInfo.intervalInvalid": "نامعتبر",
|
||||
"profiles.editInfo.intervalMinutes": "فاصله زمانی ثابت به دقیقه",
|
||||
"profiles.editInfo.intervalCron": "عبارت Cron معتبر",
|
||||
"profiles.editInfo.intervalHint": "لطفاً یک عدد یا عبارت Cron معتبر وارد کنید (مثال: 0 * * * *)",
|
||||
"profiles.editFile.title": "ویرایش پروفایل",
|
||||
"profiles.editFile.notice": "توجه: تغییرات اعمال شده در اینجا پس از بهروزرسانی پروفایل بازنشانی میشوند. برای پیکربندیهای سفارشی، لطفا از",
|
||||
"profiles.editFile.override": "جایگزینی",
|
||||
|
||||
@ -217,6 +217,17 @@
|
||||
"webdav.restore.noBackups": "Нет доступных резервных копий",
|
||||
"webdav.notification.backupSuccess.title": "Резервное копирование успешно",
|
||||
"webdav.notification.backupSuccess.body": "Файл резервной копии загружен на WebDAV",
|
||||
"localBackup.title": "Резервное копирование и восстановление",
|
||||
"localBackup.export.title": "Экспорт локальной резервной копии",
|
||||
"localBackup.export.button": "Экспорт резервной копии",
|
||||
"localBackup.import.title": "Импорт локальной резервной копии",
|
||||
"localBackup.import.button": "Импорт резервной копии",
|
||||
"localBackup.import.confirm.title": "Подтвердить импорт",
|
||||
"localBackup.import.confirm.body": "Импорт локальной резервной копии перезапишет все текущие конфигурации. Вы уверены, что хотите продолжить?",
|
||||
"localBackup.notification.exportSuccess.title": "Экспорт успешен",
|
||||
"localBackup.notification.exportSuccess.body": "Локальная резервная копия была экспортирована в указанное место",
|
||||
"localBackup.notification.importSuccess.title": "Импорт успешен",
|
||||
"localBackup.notification.importSuccess.body": "Локальная резервная копия была успешно импортирована",
|
||||
"shortcuts.title": "Горячие клавиши",
|
||||
"shortcuts.toggleWindow": "Показать/скрыть окно",
|
||||
"shortcuts.toggleFloatingWindow": "Показать/скрыть плавающее окно",
|
||||
@ -410,6 +421,11 @@
|
||||
"profiles.editInfo.override.global": "Глобальный",
|
||||
"profiles.editInfo.override.noAvailable": "Нет доступных переопределений",
|
||||
"profiles.editInfo.override.add": "Добавить переопределение",
|
||||
"profiles.editInfo.intervalPlaceholder": "например: 30 или '0 * * * *'",
|
||||
"profiles.editInfo.intervalInvalid": "Недействительно",
|
||||
"profiles.editInfo.intervalMinutes": "Фиксированный интервал в минутах",
|
||||
"profiles.editInfo.intervalCron": "Действительное Cron выражение",
|
||||
"profiles.editInfo.intervalHint": "Пожалуйста, введите число или действительное Cron выражение (например: 0 * * * *)",
|
||||
"profiles.editFile.title": "Редактировать профиль",
|
||||
"profiles.editFile.notice": "Примечание: Изменения, сделанные здесь, будут сброшены после обновления профиля. Для пользовательских настроек используйте",
|
||||
"profiles.editFile.override": "Переопределение",
|
||||
|
||||
@ -239,6 +239,17 @@
|
||||
"webdav.restore.noBackups": "还没有备份",
|
||||
"webdav.notification.backupSuccess.title": "备份成功",
|
||||
"webdav.notification.backupSuccess.body": "备份文件已上传到 WebDAV",
|
||||
"localBackup.title": "备份与恢复",
|
||||
"localBackup.export.title": "导出本地备份",
|
||||
"localBackup.export.button": "导出备份",
|
||||
"localBackup.import.title": "导入本地备份",
|
||||
"localBackup.import.button": "导入备份",
|
||||
"localBackup.import.confirm.title": "确认导入",
|
||||
"localBackup.import.confirm.body": "导入本地备份将会覆盖当前所有配置,确定要继续吗?",
|
||||
"localBackup.notification.exportSuccess.title": "导出成功",
|
||||
"localBackup.notification.exportSuccess.body": "本地备份已导出到指定位置",
|
||||
"localBackup.notification.importSuccess.title": "导入成功",
|
||||
"localBackup.notification.importSuccess.body": "本地备份已成功导入",
|
||||
"shortcuts.title": "快捷键设置",
|
||||
"shortcuts.toggleWindow": "打开/关闭窗口",
|
||||
"shortcuts.toggleFloatingWindow": "打开/关闭悬浮窗",
|
||||
@ -430,6 +441,11 @@
|
||||
"profiles.editInfo.url": "订阅地址",
|
||||
"profiles.editInfo.useProxy": "使用代理更新",
|
||||
"profiles.editInfo.interval": "更新间隔",
|
||||
"profiles.editInfo.intervalPlaceholder": "例如:30 或 '0 * * * *'",
|
||||
"profiles.editInfo.intervalInvalid": "不合法",
|
||||
"profiles.editInfo.intervalMinutes": "以分钟为单位的定时间隔",
|
||||
"profiles.editInfo.intervalCron": "有效的Cron表达式",
|
||||
"profiles.editInfo.intervalHint": "请输入数字或合法的Cron表达式(如:0 * * * *)",
|
||||
"profiles.editInfo.fixedInterval": "固定更新间隔",
|
||||
"profiles.editInfo.override.title": "覆写",
|
||||
"profiles.editInfo.override.global": "全局",
|
||||
|
||||
@ -1158,7 +1158,7 @@ const Mihomo: React.FC = () => {
|
||||
<SelectItem key="debug">{t('mihomo.debug')}</SelectItem>
|
||||
</Select>
|
||||
</SettingItem>
|
||||
<SettingItem title={t('mihomo.findProcess')} divider>
|
||||
<SettingItem title={t('mihomo.findProcess')} >
|
||||
<Select
|
||||
classNames={{ trigger: 'data-[hover=true]:bg-default-200' }}
|
||||
className="w-[100px]"
|
||||
|
||||
@ -10,6 +10,7 @@ import ShortcutConfig from '@renderer/components/settings/shortcut-config'
|
||||
import { FaTelegramPlane } from 'react-icons/fa'
|
||||
import SiderConfig from '@renderer/components/settings/sider-config'
|
||||
import SubStoreConfig from '@renderer/components/settings/substore-config'
|
||||
import LocalBackupConfig from '@renderer/components/settings/local-backup-config'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
@ -65,6 +66,7 @@ const Settings: React.FC = () => {
|
||||
<WebdavConfig />
|
||||
<MihomoConfig />
|
||||
<ShortcutConfig />
|
||||
<LocalBackupConfig />
|
||||
<Actions />
|
||||
</BasePage>
|
||||
)
|
||||
|
||||
@ -353,6 +353,15 @@ export async function reinitWebdavBackupScheduler(): Promise<void> {
|
||||
)
|
||||
}
|
||||
|
||||
// 本地备份相关 IPC 调用
|
||||
export async function exportLocalBackup(): Promise<boolean> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('exportLocalBackup'))
|
||||
}
|
||||
|
||||
export async function importLocalBackup(): Promise<boolean> {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('importLocalBackup'))
|
||||
}
|
||||
|
||||
export async function setTitleBarOverlay(overlay: TitleBarOverlayOptions): Promise<void> {
|
||||
try {
|
||||
return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setTitleBarOverlay', overlay))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user