diff --git a/resources/icon.ico b/resources/icon.ico new file mode 100644 index 0000000..93b62a3 Binary files /dev/null and b/resources/icon.ico differ diff --git a/src/main/index.ts b/src/main/index.ts index 5ba3a33..c38b66c 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,29 +1,39 @@ -import { app, shell, BrowserWindow, ipcMain } from 'electron' +import { app, shell, BrowserWindow, Tray, Menu } from 'electron' import { join } from 'path' import { electronApp, optimizer, is } from '@electron-toolkit/utils' -import icon from '../../resources/icon.png?asset' +import icon from '../../resources/icon.ico?asset' + +let window: BrowserWindow | null = null +let tray: Tray | null = null +let trayContextMenu: Menu | null = null function createWindow(): void { // Create the browser window. - const mainWindow = new BrowserWindow({ + window = new BrowserWindow({ minWidth: 800, minHeight: 600, width: 800, height: 600, show: false, autoHideMenuBar: true, - ...(process.platform === 'linux' ? { icon } : {}), + icon: icon, webPreferences: { preload: join(__dirname, '../preload/index.js'), sandbox: false } }) - mainWindow.on('ready-to-show', () => { - mainWindow.show() + window.on('ready-to-show', () => { + window?.show() + window?.focusOnWebView() }) - mainWindow.webContents.setWindowOpenHandler((details) => { + window.on('close', (event) => { + event.preventDefault() + window?.hide() + }) + + window.webContents.setWindowOpenHandler((details) => { shell.openExternal(details.url) return { action: 'deny' } }) @@ -31,12 +41,44 @@ function createWindow(): void { // HMR for renderer base on electron-vite cli. // Load the remote URL for development or the local html file for production. if (is.dev && process.env['ELECTRON_RENDERER_URL']) { - mainWindow.loadURL(process.env['ELECTRON_RENDERER_URL']) + window.loadURL(process.env['ELECTRON_RENDERER_URL']) } else { - mainWindow.loadFile(join(__dirname, '../renderer/index.html')) + window.loadFile(join(__dirname, '../renderer/index.html')) } } +function createTray(): void { + tray = new Tray(icon) + trayContextMenu = Menu.buildFromTemplate([ + { + label: '显示窗口', + type: 'normal', + click: (): void => { + window?.show() + window?.focusOnWebView() + } + }, + { + label: '重启应用', + type: 'normal', + click: (): void => { + app.relaunch() + app.quit() + } + }, + { type: 'separator' }, + { label: '退出应用', type: 'normal', click: (): void => app.quit() } + ]) + + tray.setContextMenu(trayContextMenu) + tray.setIgnoreDoubleClickEvents(true) + tray.setToolTip('Another Mihomo GUI.') + tray.setTitle('Mihomo Party') + tray.addListener('click', () => { + window?.isVisible() ? window?.hide() : window?.show() + }) +} + // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. @@ -51,10 +93,8 @@ app.whenReady().then(() => { optimizer.watchWindowShortcuts(window) }) - // IPC test - ipcMain.on('ping', () => console.log('pong')) - createWindow() + createTray() app.on('activate', function () { // On macOS it's common to re-create a window in the app when the @@ -72,5 +112,6 @@ app.on('window-all-closed', () => { } }) -// In this file you can include the rest of your app"s specific main process -// code. You can also put them in separate files and require them here. +app.on('before-quit', () => { + app.exit() +}) diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 0b7b9d6..e80cb48 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -1,21 +1,23 @@ import { useTheme } from 'next-themes' import { useEffect } from 'react' -import { useRoutes } from 'react-router-dom' +import { useLocation, useNavigate, useRoutes } from 'react-router-dom' import OutboundModeSwitcher from '@renderer/components/sider/outbound-mode-switcher' import SysproxySwitcher from '@renderer/components/sider/sysproxy-switcher' import TunSwitcher from '@renderer/components/sider/tun-switcher' import { Button } from '@nextui-org/react' -import { IoHome, IoSettings } from 'react-icons/io5' -import { IoWifi } from 'react-icons/io5' -import { IoGitNetwork } from 'react-icons/io5' -import { IoLogoGithub } from 'react-icons/io5' +import { IoSettings } from 'react-icons/io5' import routes from '@renderer/routes' -import RouteItem from './components/sider/route-item' -import ProfileSwitcher from './components/sider/profile-switcher' +import ProfileCard from '@renderer/components/sider/profile-card' +import ProxyCard from '@renderer/components/sider/proxy-card' +import RuleCard from '@renderer/components/sider/rule-card' +import OverrideCard from '@renderer/components/sider/override-card' +import ConnCard from '@renderer/components/sider/conn-card' +import LogCard from '@renderer/components/sider/log-card' function App(): JSX.Element { const { setTheme } = useTheme() - + const navigate = useNavigate() + const location = useLocation() const page = useRoutes(routes) useEffect(() => { @@ -39,32 +41,45 @@ function App(): JSX.Element { return (
-
-
+
+

出站

+
+ +
- -

代理

-
+

代理

+
-

配置

- - - - - +

配置

+
+
+ + +
+ +
+ + +
+
+ + +
+
{page}
diff --git a/src/renderer/src/assets/main.css b/src/renderer/src/assets/main.css index b5c61c9..20a0bcd 100644 --- a/src/renderer/src/assets/main.css +++ b/src/renderer/src/assets/main.css @@ -1,3 +1,10 @@ @tailwind base; @tailwind components; @tailwind utilities; + +@layer utilities { + /* Hide scrollbar for Chrome, Safari and Opera */ + .no-scrollbar::-webkit-scrollbar { + display: none; + } +} diff --git a/src/renderer/src/components/sider/conn-card.tsx b/src/renderer/src/components/sider/conn-card.tsx new file mode 100644 index 0000000..02fb78a --- /dev/null +++ b/src/renderer/src/components/sider/conn-card.tsx @@ -0,0 +1,35 @@ +import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' +import { IoLink } from 'react-icons/io5' +import { useLocation, useNavigate } from 'react-router-dom' + +export default function ConnCard(): JSX.Element { + const navigate = useNavigate() + const location = useLocation() + + return ( + navigate('/connections')} + > + +
+ + + 1103 + +
+
+ +

连接

+
+
+ ) +} diff --git a/src/renderer/src/components/sider/log-card.tsx b/src/renderer/src/components/sider/log-card.tsx new file mode 100644 index 0000000..0182497 --- /dev/null +++ b/src/renderer/src/components/sider/log-card.tsx @@ -0,0 +1,32 @@ +import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' +import { IoJournal } from 'react-icons/io5' +import { useLocation, useNavigate } from 'react-router-dom' + +export default function LogCard(): JSX.Element { + const navigate = useNavigate() + const location = useLocation() + + return ( + navigate('/logs')} + > + +
+ +
+
+ +

日志

+
+
+ ) +} diff --git a/src/renderer/src/components/sider/override-card.tsx b/src/renderer/src/components/sider/override-card.tsx new file mode 100644 index 0000000..59b9ca6 --- /dev/null +++ b/src/renderer/src/components/sider/override-card.tsx @@ -0,0 +1,33 @@ +import { Button, Card, CardBody, CardFooter, Switch } from '@nextui-org/react' +import { MdFormatOverline } from 'react-icons/md' +import { useLocation, useNavigate } from 'react-router-dom' + +export default function OverrideCard(): JSX.Element { + const navigate = useNavigate() + const location = useLocation() + + return ( + navigate('/override')} + > + +
+ + +
+
+ +

覆写

+
+
+ ) +} diff --git a/src/renderer/src/components/sider/profile-switcher.tsx b/src/renderer/src/components/sider/profile-card.tsx similarity index 88% rename from src/renderer/src/components/sider/profile-switcher.tsx rename to src/renderer/src/components/sider/profile-card.tsx index a61fe9a..8834593 100644 --- a/src/renderer/src/components/sider/profile-switcher.tsx +++ b/src/renderer/src/components/sider/profile-card.tsx @@ -2,7 +2,7 @@ import { Button, Card, CardBody, CardFooter, Slider } from '@nextui-org/react' import { IoMdRefresh } from 'react-icons/io' import { useLocation, useNavigate } from 'react-router-dom' -export default function ProfileSwitcher(): JSX.Element { +export default function ProfileCard(): JSX.Element { const navigate = useNavigate() const location = useLocation() @@ -17,7 +17,7 @@ export default function ProfileSwitcher(): JSX.Element {

订阅名称

diff --git a/src/renderer/src/components/sider/proxy-card.tsx b/src/renderer/src/components/sider/proxy-card.tsx new file mode 100644 index 0000000..ee4acde --- /dev/null +++ b/src/renderer/src/components/sider/proxy-card.tsx @@ -0,0 +1,29 @@ +import { Button, Card, CardBody, CardFooter } from '@nextui-org/react' +import { SiSpeedtest } from 'react-icons/si' +import { useLocation, useNavigate } from 'react-router-dom' + +export default function ProxyCard(): JSX.Element { + const navigate = useNavigate() + const location = useLocation() + + return ( + navigate('/proxies')} + > + +
+

节点名称

+ +
+
+ + 二级节点 + +
+ ) +} diff --git a/src/renderer/src/components/sider/route-item.tsx b/src/renderer/src/components/sider/route-item.tsx deleted file mode 100644 index c66955c..0000000 --- a/src/renderer/src/components/sider/route-item.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Button } from '@nextui-org/react' -import { IconType } from 'react-icons' -import { useLocation, useNavigate } from 'react-router-dom' - -interface Props { - title: string - pathname: string - icon: IconType -} - -export default function RouteItem(props: Props): JSX.Element { - const { pathname, icon: Icon, title } = props - const navigate = useNavigate() - const location = useLocation() - - return ( - - ) -} diff --git a/src/renderer/src/components/sider/rule-card.tsx b/src/renderer/src/components/sider/rule-card.tsx new file mode 100644 index 0000000..5bd7ed8 --- /dev/null +++ b/src/renderer/src/components/sider/rule-card.tsx @@ -0,0 +1,35 @@ +import { Button, Card, CardBody, CardFooter, Chip } from '@nextui-org/react' +import { IoGitNetwork } from 'react-icons/io5' +import { useLocation, useNavigate } from 'react-router-dom' + +export default function RuleCard(): JSX.Element { + const navigate = useNavigate() + const location = useLocation() + + return ( + navigate('/rules')} + > + +
+ + + 1103 + +
+
+ +

规则

+
+
+ ) +} diff --git a/src/renderer/src/components/sider/sysproxy-switcher.tsx b/src/renderer/src/components/sider/sysproxy-switcher.tsx index 2a2b990..31a98da 100644 --- a/src/renderer/src/components/sider/sysproxy-switcher.tsx +++ b/src/renderer/src/components/sider/sysproxy-switcher.tsx @@ -3,7 +3,7 @@ import { IoSettings } from 'react-icons/io5' export default function SysproxySwitcher(): JSX.Element { return ( - +