diff --git a/src/main/sys/misc.ts b/src/main/sys/misc.ts index c34d946..76d86bb 100644 --- a/src/main/sys/misc.ts +++ b/src/main/sys/misc.ts @@ -1,9 +1,17 @@ import { exec, execFile, execSync } from 'child_process' -import { dialog, nativeTheme } from 'electron' +import { dialog, nativeTheme, shell } from 'electron' import { readFile } from 'fs/promises' import path from 'path' import { promisify } from 'util' -import { exePath, mihomoCorePath, resourcesDir, resourcesFilesDir, taskDir } from '../utils/dirs' +import { + exePath, + mihomoCorePath, + overridePath, + profilePath, + resourcesDir, + resourcesFilesDir, + taskDir +} from '../utils/dirs' import { copyFileSync, writeFileSync } from 'fs' export function getFilePath(ext: string[]): string[] | undefined { @@ -18,6 +26,15 @@ export async function readTextFile(filePath: string): Promise { return await readFile(filePath, 'utf8') } +export function openFile(type: 'profile' | 'override', id: string, ext?: 'yaml' | 'js'): void { + if (type === 'profile') { + shell.openPath(profilePath(id)) + } + if (type === 'override') { + shell.openPath(overridePath(id, ext || 'js')) + } +} + export async function openUWPTool(): Promise { const execFilePromise = promisify(execFile) const uwpToolPath = path.join(resourcesDir(), 'files', 'enableLoopback.exe') diff --git a/src/main/utils/ipc.ts b/src/main/utils/ipc.ts index 1b28b23..29ba94f 100644 --- a/src/main/utils/ipc.ts +++ b/src/main/utils/ipc.ts @@ -49,7 +49,14 @@ import { import { isEncryptionAvailable, manualGrantCorePermition, restartCore } from '../core/manager' import { triggerSysProxy } from '../sys/sysproxy' import { checkUpdate, downloadAndInstallUpdate } from '../resolve/autoUpdater' -import { getFilePath, openUWPTool, readTextFile, setNativeTheme, setupFirewall } from '../sys/misc' +import { + getFilePath, + openFile, + openUWPTool, + readTextFile, + setNativeTheme, + setupFirewall +} from '../sys/misc' import { getRuntimeConfig, getRuntimeConfigStr } from '../core/factory' import { isPortable, setPortable } from './dirs' import { listWebdavBackups, webdavBackup, webdavDelete, webdavRestore } from '../resolve/backup' @@ -171,6 +178,7 @@ export function registerIpcMainHandlers(): void { ipcMain.handle('setNativeTheme', (_e, theme) => { setNativeTheme(theme) }) + ipcMain.handle('openFile', (_e, type, id, ext) => openFile(type, id, ext)) ipcMain.handle('copyEnv', ipcErrorWrapper(copyEnv)) ipcMain.handle('alert', (_e, msg) => { dialog.showErrorBox('Mihomo Party', msg) diff --git a/src/renderer/src/components/connections/connection-detail-modal.tsx b/src/renderer/src/components/connections/connection-detail-modal.tsx index e147105..ab5eb7f 100644 --- a/src/renderer/src/components/connections/connection-detail-modal.tsx +++ b/src/renderer/src/components/connections/connection-detail-modal.tsx @@ -7,23 +7,7 @@ interface Props { connection: IMihomoConnectionDetail onClose: () => void } -// sourceIP: string -// destinationIP: string -// destinationGeoIP: string -// destinationIPASN: string -// sourcePort: string -// destinationPort: string -// inboundIP: string -// inboundPort: string -// inboundName: string -// inboundUser: string -// host: string -// dnsMode: string -// specialProxy: string -// specialRules: string -// remoteDestination: string -// dscp: number -// sniffHost: string + const ConnectionDetailModal: React.FC = (props) => { const { connection, onClose } = props return ( diff --git a/src/renderer/src/components/override/override-item.tsx b/src/renderer/src/components/override/override-item.tsx index c430c79..bb02ff2 100644 --- a/src/renderer/src/components/override/override-item.tsx +++ b/src/renderer/src/components/override/override-item.tsx @@ -16,6 +16,7 @@ import EditInfoModal from './edit-info-modal' import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' import ExecLogModal from './exec-log-modal' +import { openFile } from '@renderer/utils/ipc' interface Props { info: IOverrideItem @@ -48,6 +49,13 @@ const menuItems: MenuItem[] = [ color: 'default', className: '' } as MenuItem, + { + key: 'open-file', + label: '打开文件', + showDivider: false, + color: 'default', + className: '' + } as MenuItem, { key: 'exec-log', label: '执行日志', @@ -68,8 +76,8 @@ const OverrideItem: React.FC = (props) => { const { info, addOverrideItem, removeOverrideItem, mutateOverrideConfig, updateOverrideItem } = props const [updating, setUpdating] = useState(false) - const [openInfo, setOpenInfo] = useState(false) - const [openFile, setOpenFile] = useState(false) + const [openInfoEditor, setOpenInfoEditor] = useState(false) + const [openFileEditor, setOpenFileEditor] = useState(false) const [openLog, setOpenLog] = useState(false) const { attributes, @@ -87,11 +95,15 @@ const OverrideItem: React.FC = (props) => { const onMenuAction = (key: Key): void => { switch (key) { case 'edit-info': { - setOpenInfo(true) + setOpenInfoEditor(true) break } case 'edit-file': { - setOpenFile(true) + setOpenFileEditor(true) + break + } + case 'open-file': { + openFile('override', info.id, info.ext) break } case 'exec-log': { @@ -128,17 +140,17 @@ const OverrideItem: React.FC = (props) => { zIndex: isDragging ? 'calc(infinity)' : undefined }} > - {openFile && ( + {openFileEditor && ( setOpenFile(false)} + onClose={() => setOpenFileEditor(false)} /> )} - {openInfo && ( + {openInfoEditor && ( setOpenInfo(false)} + onClose={() => setOpenInfoEditor(false)} updateOverrideItem={updateOverrideItem} /> )} @@ -148,7 +160,7 @@ const OverrideItem: React.FC = (props) => { isPressable onPress={() => { if (disableOpen) return - setOpenFile(true) + setOpenFileEditor(true) }} > diff --git a/src/renderer/src/components/profiles/profile-item.tsx b/src/renderer/src/components/profiles/profile-item.tsx index 7a1daf4..1f4c899 100644 --- a/src/renderer/src/components/profiles/profile-item.tsx +++ b/src/renderer/src/components/profiles/profile-item.tsx @@ -18,6 +18,7 @@ import EditFileModal from './edit-file-modal' import EditInfoModal from './edit-info-modal' import { useSortable } from '@dnd-kit/sortable' import { CSS } from '@dnd-kit/utilities' +import { openFile } from '@renderer/utils/ipc' interface Props { info: IProfileItem @@ -51,8 +52,8 @@ const ProfileItem: React.FC = (props) => { const total = extra?.total ?? 0 const [updating, setUpdating] = useState(false) const [selecting, setSelecting] = useState(false) - const [openInfo, setOpenInfo] = useState(false) - const [openFile, setOpenFile] = useState(false) + const [openInfoEditor, setOpenInfoEditor] = useState(false) + const [openFileEditor, setOpenFileEditor] = useState(false) const { attributes, listeners, @@ -78,6 +79,13 @@ const ProfileItem: React.FC = (props) => { { key: 'edit-file', label: '编辑文件', + showDivider: false, + color: 'default', + className: '' + } as MenuItem, + { + key: 'open-file', + label: '打开文件', showDivider: true, color: 'default', className: '' @@ -105,11 +113,15 @@ const ProfileItem: React.FC = (props) => { const onMenuAction = async (key: Key): Promise => { switch (key) { case 'edit-info': { - setOpenInfo(true) + setOpenInfoEditor(true) break } case 'edit-file': { - setOpenFile(true) + setOpenFileEditor(true) + break + } + case 'open-file': { + openFile('profile', info.id) break } case 'delete': { @@ -147,11 +159,11 @@ const ProfileItem: React.FC = (props) => { zIndex: isDragging ? 'calc(infinity)' : undefined }} > - {openFile && setOpenFile(false)} />} - {openInfo && ( + {openFileEditor && setOpenFileEditor(false)} />} + {openInfoEditor && ( setOpenInfo(false)} + onClose={() => setOpenInfoEditor(false)} updateProfileItem={updateProfileItem} /> )} diff --git a/src/renderer/src/utils/ipc.ts b/src/renderer/src/utils/ipc.ts index 25668ae..87de7d0 100644 --- a/src/renderer/src/utils/ipc.ts +++ b/src/renderer/src/utils/ipc.ts @@ -297,6 +297,14 @@ export async function setNativeTheme(theme: 'system' | 'light' | 'dark'): Promis return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('setNativeTheme', theme)) } +export async function openFile( + type: 'profile' | 'override', + id: string, + ext?: 'yaml' | 'js' +): Promise { + return ipcErrorWrapper(await window.electron.ipcRenderer.invoke('openFile', type, id, ext)) +} + export async function registerShortcut( oldShortcut: string, newShortcut: string,