mirror of
https://github.com/pompurin404/mihomo-party.git
synced 2024-11-15 19:22:31 +08:00
add tray icon
This commit is contained in:
parent
bad31c2398
commit
cb6b167fd5
BIN
resources/icon.ico
Normal file
BIN
resources/icon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 25 KiB |
|
@ -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()
|
||||
})
|
||||
|
|
|
@ -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 (
|
||||
<div className="w-full h-[100vh] flex">
|
||||
<div className="side w-[250px] h-full p-2 border-r border-neutral-700">
|
||||
<div className="flex justify-between h-[32px] mb-2">
|
||||
<div className="side w-[250px] h-full border-r border-neutral-700">
|
||||
<div className="flex justify-between h-[32px] m-2">
|
||||
<h3 className="select-none text-lg font-bold leading-[32px]">出站</h3>
|
||||
<Button
|
||||
size="sm"
|
||||
isIconOnly
|
||||
variant="light"
|
||||
color={location.pathname.includes('/settings') ? 'primary' : 'default'}
|
||||
variant={location.pathname.includes('/settings') ? 'solid' : 'light'}
|
||||
onPress={() => {
|
||||
open('https://github.com/pompurin404/mihomo-party')
|
||||
navigate('/settings')
|
||||
}}
|
||||
startContent={<IoLogoGithub className="text-[20px]" />}
|
||||
startContent={<IoSettings className="text-[20px]" />}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-2">
|
||||
<OutboundModeSwitcher />
|
||||
</div>
|
||||
|
||||
<OutboundModeSwitcher />
|
||||
<h3 className="select-none text-lg font-bold my-2">代理</h3>
|
||||
<div className="flex justify-between">
|
||||
<h3 className="select-none text-lg font-bold m-2">代理</h3>
|
||||
<div className="flex justify-between mx-2">
|
||||
<SysproxySwitcher />
|
||||
<TunSwitcher />
|
||||
</div>
|
||||
<h3 className="select-none text-lg font-bold my-2">配置</h3>
|
||||
<ProfileSwitcher />
|
||||
<RouteItem title="概览" pathname="/overview" icon={IoHome} />
|
||||
<RouteItem title="代理" pathname="/proxies" icon={IoWifi} />
|
||||
<RouteItem title="规则" pathname="/rules" icon={IoGitNetwork} />
|
||||
<RouteItem title="设置" pathname="/settings" icon={IoSettings} />
|
||||
<h3 className="select-none text-lg font-bold m-2">配置</h3>
|
||||
<div className="w-full h-[calc(100%-260px)] overflow-y-auto no-scrollbar">
|
||||
<div className="mx-2">
|
||||
<ProfileCard />
|
||||
<ProxyCard />
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between mx-2">
|
||||
<ConnCard />
|
||||
<LogCard />
|
||||
</div>
|
||||
<div className="flex justify-between mx-2">
|
||||
<RuleCard />
|
||||
<OverrideCard />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="main w-[calc(100%-250px)] h-full overflow-y-auto">{page}</div>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
35
src/renderer/src/components/sider/conn-card.tsx
Normal file
35
src/renderer/src/components/sider/conn-card.tsx
Normal file
|
@ -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 (
|
||||
<Card
|
||||
className={`w-[50%] mr-1 mb-2 ${location.pathname.includes('/connections') ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/connections')}
|
||||
>
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<IoLink color="default" className="text-[20px]" />
|
||||
</Button>
|
||||
<Chip size="sm" color="secondary" variant="bordered" className="mr-3 mt-2">
|
||||
1103
|
||||
</Chip>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className="select-none text-md font-bold">连接</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
32
src/renderer/src/components/sider/log-card.tsx
Normal file
32
src/renderer/src/components/sider/log-card.tsx
Normal file
|
@ -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 (
|
||||
<Card
|
||||
className={`w-[50%] ml-1 mb-2 ${location.pathname.includes('/logs') ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/logs')}
|
||||
>
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<IoJournal color="default" className="text-[20px]" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className="select-none text-md font-bold">日志</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
33
src/renderer/src/components/sider/override-card.tsx
Normal file
33
src/renderer/src/components/sider/override-card.tsx
Normal file
|
@ -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 (
|
||||
<Card
|
||||
className={`w-[50%] ml-1 mb-2 ${location.pathname.includes('/override') ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/override')}
|
||||
>
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<MdFormatOverline color="default" className="text-[24px]" />
|
||||
</Button>
|
||||
<Switch />
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className="select-none text-md font-bold">覆写</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
|
@ -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 {
|
|||
<div className="flex justify-between h-[32px]">
|
||||
<h3 className="select-none text-md font-bold leading-[32px]">订阅名称</h3>
|
||||
<Button isIconOnly size="sm" variant="light" color="default">
|
||||
<IoMdRefresh color="default" className="text-[20px]" />
|
||||
<IoMdRefresh color="default" className="text-[24px]" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardBody>
|
29
src/renderer/src/components/sider/proxy-card.tsx
Normal file
29
src/renderer/src/components/sider/proxy-card.tsx
Normal file
|
@ -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 (
|
||||
<Card
|
||||
fullWidth
|
||||
className={`mb-2 ${location.pathname.includes('/proxies') ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/proxies')}
|
||||
>
|
||||
<CardBody>
|
||||
<div className="flex justify-between h-[32px]">
|
||||
<h3 className="select-none text-md font-bold leading-[32px]">节点名称</h3>
|
||||
<Button isIconOnly size="sm" variant="light" color="default">
|
||||
<SiSpeedtest color="default" className="text-[20px]" />
|
||||
</Button>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<small>二级节点</small>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
|
@ -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 (
|
||||
<Button
|
||||
fullWidth
|
||||
color={location.pathname.includes(pathname) ? 'primary' : 'default'}
|
||||
variant={location.pathname.includes(pathname) ? 'solid' : 'light'}
|
||||
className="text-md mb-2"
|
||||
startContent={<Icon className="text-[20px]" />}
|
||||
onPress={() => navigate(pathname)}
|
||||
>
|
||||
<div className="w-full">{title}</div>
|
||||
</Button>
|
||||
)
|
||||
}
|
35
src/renderer/src/components/sider/rule-card.tsx
Normal file
35
src/renderer/src/components/sider/rule-card.tsx
Normal file
|
@ -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 (
|
||||
<Card
|
||||
className={`w-[50%] mr-1 mb-2 ${location.pathname.includes('/rules') ? 'bg-primary' : ''}`}
|
||||
isPressable
|
||||
onPress={() => navigate('/rules')}
|
||||
>
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button
|
||||
isIconOnly
|
||||
className="bg-transparent pointer-events-none"
|
||||
variant="flat"
|
||||
color="default"
|
||||
>
|
||||
<IoGitNetwork color="default" className="text-[20px]" />
|
||||
</Button>
|
||||
<Chip size="sm" color="secondary" variant="bordered" className="mr-3 mt-2">
|
||||
1103
|
||||
</Chip>
|
||||
</div>
|
||||
</CardBody>
|
||||
<CardFooter className="pt-1">
|
||||
<h3 className="select-none text-md font-bold">规则</h3>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
)
|
||||
}
|
|
@ -3,7 +3,7 @@ import { IoSettings } from 'react-icons/io5'
|
|||
|
||||
export default function SysproxySwitcher(): JSX.Element {
|
||||
return (
|
||||
<Card className="w-[48%]">
|
||||
<Card className="w-[50%] mr-1">
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button isIconOnly className="bg-transparent" variant="flat" color="default">
|
||||
|
|
|
@ -3,7 +3,7 @@ import { IoSettings } from 'react-icons/io5'
|
|||
|
||||
export default function SysproxySwitcher(): JSX.Element {
|
||||
return (
|
||||
<Card className="w-[48%]">
|
||||
<Card className="w-[50%] ml-1">
|
||||
<CardBody className="pb-1 pt-0 px-0">
|
||||
<div className="flex justify-between">
|
||||
<Button isIconOnly className="bg-transparent" variant="flat" color="default">
|
||||
|
|
3
src/renderer/src/pages/connections.tsx
Normal file
3
src/renderer/src/pages/connections.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function Connections(): JSX.Element {
|
||||
return <div>Connections</div>
|
||||
}
|
3
src/renderer/src/pages/logs.tsx
Normal file
3
src/renderer/src/pages/logs.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function Logs(): JSX.Element {
|
||||
return <div>Logs</div>
|
||||
}
|
3
src/renderer/src/pages/override.tsx
Normal file
3
src/renderer/src/pages/override.tsx
Normal file
|
@ -0,0 +1,3 @@
|
|||
export default function Override(): JSX.Element {
|
||||
return <div>Override</div>
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export default function Overview(): JSX.Element {
|
||||
return <div>Overview</div>
|
||||
}
|
|
@ -1,15 +1,13 @@
|
|||
import { Navigate } from 'react-router-dom'
|
||||
import Overview from '@renderer/pages/overview'
|
||||
import Override from '@renderer/pages/override'
|
||||
import Proxies from '@renderer/pages/proxies'
|
||||
import Rules from '@renderer/pages/rules'
|
||||
import Settings from '@renderer/pages/settings'
|
||||
import Profiles from '@renderer/pages/profiles'
|
||||
import Logs from '@renderer/pages/logs'
|
||||
import Connections from '@renderer/pages/connections'
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/overview',
|
||||
element: <Overview />
|
||||
},
|
||||
{
|
||||
path: '/proxies',
|
||||
element: <Proxies />
|
||||
|
@ -18,6 +16,18 @@ const routes = [
|
|||
path: '/rules',
|
||||
element: <Rules />
|
||||
},
|
||||
{
|
||||
path: '/logs',
|
||||
element: <Logs />
|
||||
},
|
||||
{
|
||||
path: '/connections',
|
||||
element: <Connections />
|
||||
},
|
||||
{
|
||||
path: '/override',
|
||||
element: <Override />
|
||||
},
|
||||
{
|
||||
path: '/profiles',
|
||||
element: <Profiles />
|
||||
|
@ -28,7 +38,7 @@ const routes = [
|
|||
},
|
||||
{
|
||||
path: '/',
|
||||
element: <Navigate to="/overview" />
|
||||
element: <Navigate to="/proxies" />
|
||||
}
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user