diff --git a/package.json b/package.json
index 4cb1785..8b0e5e7 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "mihomo-party",
"version": "1.0.0",
- "description": "An Electron application with React and TypeScript",
+ "description": "Mihomo Party",
"main": "./out/main/index.js",
"author": "mihomo-party",
"homepage": "https://mihomo.party",
@@ -26,7 +26,9 @@
"@nextui-org/react": "^2.4.6",
"electron-updater": "^6.2.1",
"framer-motion": "^11.3.19",
- "next-themes": "^0.3.0"
+ "next-themes": "^0.3.0",
+ "react-icons": "^5.2.1",
+ "react-router-dom": "^6.25.1"
},
"devDependencies": {
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
@@ -36,17 +38,17 @@
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@vitejs/plugin-react": "^4.3.1",
+ "autoprefixer": "^10.4.19",
"electron": "^31.3.1",
"electron-builder": "^25.0.2",
"electron-vite": "^2.3.0",
"eslint": "^8.57.0",
"eslint-plugin-react": "^7.35.0",
- "autoprefixer": "^10.4.19",
"postcss": "^8.4.40",
- "tailwindcss": "^3.4.7",
"prettier": "^3.3.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
+ "tailwindcss": "^3.4.7",
"typescript": "^5.5.4",
"vite": "^5.3.5"
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4fa791b..23a1462 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -26,6 +26,12 @@ importers:
next-themes:
specifier: ^0.3.0
version: 0.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
+ react-icons:
+ specifier: ^5.2.1
+ version: 5.2.1(react@18.3.1)
+ react-router-dom:
+ specifier: ^6.25.1
+ version: 6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
devDependencies:
'@electron-toolkit/eslint-config-prettier':
specifier: ^2.0.0
@@ -1556,6 +1562,10 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
+ '@remix-run/router@1.18.0':
+ resolution: {integrity: sha512-L3jkqmqoSVBVKHfpGZmLrex0lxR5SucGA0sUfFzGctehw+S/ggL9L/0NnC5mw6P8HUWpFZ3nQw3cRApjjWx9Sw==}
+ engines: {node: '>=14.0.0'}
+
'@rollup/rollup-android-arm-eabi@4.19.1':
resolution: {integrity: sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==}
cpu: [arm]
@@ -3467,6 +3477,11 @@ packages:
peerDependencies:
react: ^18.3.1
+ react-icons@5.2.1:
+ resolution: {integrity: sha512-zdbW5GstTzXaVKvGSyTaBalt7HSfuK5ovrzlpyiWHAFXndXTdd/1hdDHI4xBM1Mn7YriT6aqESucFl9kEXzrdw==}
+ peerDependencies:
+ react: '*'
+
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -3494,6 +3509,19 @@ packages:
'@types/react':
optional: true
+ react-router-dom@6.25.1:
+ resolution: {integrity: sha512-0tUDpbFvk35iv+N89dWNrJp+afLgd+y4VtorJZuOCXK0kkCWjEvb3vTJM++SYvMEpbVwXKf3FjeVveVEb6JpDQ==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+
+ react-router@6.25.1:
+ resolution: {integrity: sha512-u8ELFr5Z6g02nUtpPAggP73Jigj1mRePSwhS/2nkTrlPU5yEkH1vYzWNyvSnSzeeE2DNqWdH+P8OhIh9wuXhTw==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ react: '>=16.8'
+
react-style-singleton@2.2.1:
resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
engines: {node: '>=10'}
@@ -6471,6 +6499,8 @@ snapshots:
'@react-types/shared': 3.24.1(react@18.3.1)
react: 18.3.1
+ '@remix-run/router@1.18.0': {}
+
'@rollup/rollup-android-arm-eabi@4.19.1':
optional: true
@@ -8662,6 +8692,10 @@ snapshots:
react: 18.3.1
scheduler: 0.23.2
+ react-icons@5.2.1(react@18.3.1):
+ dependencies:
+ react: 18.3.1
+
react-is@16.13.1: {}
react-refresh@0.14.2: {}
@@ -8685,6 +8719,18 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
+ react-router-dom@6.25.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
+ dependencies:
+ '@remix-run/router': 1.18.0
+ react: 18.3.1
+ react-dom: 18.3.1(react@18.3.1)
+ react-router: 6.25.1(react@18.3.1)
+
+ react-router@6.25.1(react@18.3.1):
+ dependencies:
+ '@remix-run/router': 1.18.0
+ react: 18.3.1
+
react-style-singleton@2.2.1(@types/react@18.3.3)(react@18.3.1):
dependencies:
get-nonce: 1.0.1
diff --git a/src/main/index.ts b/src/main/index.ts
index 7aaf1e7..5ba3a33 100644
--- a/src/main/index.ts
+++ b/src/main/index.ts
@@ -6,8 +6,10 @@ import icon from '../../resources/icon.png?asset'
function createWindow(): void {
// Create the browser window.
const mainWindow = new BrowserWindow({
- width: 900,
- height: 670,
+ minWidth: 800,
+ minHeight: 600,
+ width: 800,
+ height: 600,
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}),
diff --git a/src/renderer/index.html b/src/renderer/index.html
index e198e05..27d4b4e 100644
--- a/src/renderer/index.html
+++ b/src/renderer/index.html
@@ -2,7 +2,7 @@
- Electron
+ Mihomo Party
{
try {
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
@@ -24,7 +37,38 @@ function App(): JSX.Element {
}
}, [])
- return
+ return (
+
+
+
+
出站
+
+
+
+
代理
+
+
+
+
+
配置
+
+
+
+
+
+
+
{page}
+
+ )
}
export default App
diff --git a/src/renderer/src/components/sider/outbound-mode-switcher.tsx b/src/renderer/src/components/sider/outbound-mode-switcher.tsx
new file mode 100644
index 0000000..c7ea3c9
--- /dev/null
+++ b/src/renderer/src/components/sider/outbound-mode-switcher.tsx
@@ -0,0 +1,30 @@
+import { Tabs, Tab } from '@nextui-org/react'
+import { Key, useState } from 'react'
+
+export default function OutboundModeSwitcher(): JSX.Element {
+ const [mode, setMode] = useState('rule')
+ return (
+ setMode(key as OutboundMode)}
+ >
+
+
+
+
+ )
+}
diff --git a/src/renderer/src/components/sider/profile-switcher.tsx b/src/renderer/src/components/sider/profile-switcher.tsx
new file mode 100644
index 0000000..a61fe9a
--- /dev/null
+++ b/src/renderer/src/components/sider/profile-switcher.tsx
@@ -0,0 +1,29 @@
+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 {
+ const navigate = useNavigate()
+ const location = useLocation()
+
+ return (
+ navigate('/profiles')}
+ >
+
+
+
订阅名称
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/renderer/src/components/sider/route-item.tsx b/src/renderer/src/components/sider/route-item.tsx
new file mode 100644
index 0000000..c66955c
--- /dev/null
+++ b/src/renderer/src/components/sider/route-item.tsx
@@ -0,0 +1,28 @@
+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 (
+ }
+ onPress={() => navigate(pathname)}
+ >
+ {title}
+
+ )
+}
diff --git a/src/renderer/src/components/sider/sysproxy-switcher.tsx b/src/renderer/src/components/sider/sysproxy-switcher.tsx
new file mode 100644
index 0000000..2a2b990
--- /dev/null
+++ b/src/renderer/src/components/sider/sysproxy-switcher.tsx
@@ -0,0 +1,20 @@
+import { Button, Card, CardBody, CardFooter, Switch } from '@nextui-org/react'
+import { IoSettings } from 'react-icons/io5'
+
+export default function SysproxySwitcher(): JSX.Element {
+ return (
+
+
+
+
+
+
+
+
+ 系统代理
+
+
+ )
+}
diff --git a/src/renderer/src/components/sider/tun-switcher.tsx b/src/renderer/src/components/sider/tun-switcher.tsx
new file mode 100644
index 0000000..ab72bf3
--- /dev/null
+++ b/src/renderer/src/components/sider/tun-switcher.tsx
@@ -0,0 +1,20 @@
+import { Button, Card, CardBody, CardFooter, Switch } from '@nextui-org/react'
+import { IoSettings } from 'react-icons/io5'
+
+export default function SysproxySwitcher(): JSX.Element {
+ return (
+
+
+
+
+
+
+
+
+ 虚拟网卡
+
+
+ )
+}
diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx
index 6cccdfe..790a28e 100644
--- a/src/renderer/src/main.tsx
+++ b/src/renderer/src/main.tsx
@@ -1,5 +1,6 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
+import { BrowserRouter } from 'react-router-dom'
import { ThemeProvider as NextThemesProvider } from 'next-themes'
import { NextUIProvider } from '@nextui-org/react'
@@ -10,7 +11,9 @@ ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
-
+
+
+
diff --git a/src/renderer/src/pages/overview.tsx b/src/renderer/src/pages/overview.tsx
new file mode 100644
index 0000000..b9ccdad
--- /dev/null
+++ b/src/renderer/src/pages/overview.tsx
@@ -0,0 +1,3 @@
+export default function Overview(): JSX.Element {
+ return Overview
+}
diff --git a/src/renderer/src/pages/profiles.tsx b/src/renderer/src/pages/profiles.tsx
new file mode 100644
index 0000000..d31690b
--- /dev/null
+++ b/src/renderer/src/pages/profiles.tsx
@@ -0,0 +1,3 @@
+export default function Profiles(): JSX.Element {
+ return Profiles
+}
diff --git a/src/renderer/src/pages/proxies.tsx b/src/renderer/src/pages/proxies.tsx
new file mode 100644
index 0000000..df7545f
--- /dev/null
+++ b/src/renderer/src/pages/proxies.tsx
@@ -0,0 +1,3 @@
+export default function Proxies(): JSX.Element {
+ return Proxies
+}
diff --git a/src/renderer/src/pages/rules.tsx b/src/renderer/src/pages/rules.tsx
new file mode 100644
index 0000000..7f91eaa
--- /dev/null
+++ b/src/renderer/src/pages/rules.tsx
@@ -0,0 +1,3 @@
+export default function Rules(): JSX.Element {
+ return Rules
+}
diff --git a/src/renderer/src/pages/settings.tsx b/src/renderer/src/pages/settings.tsx
new file mode 100644
index 0000000..f851b6b
--- /dev/null
+++ b/src/renderer/src/pages/settings.tsx
@@ -0,0 +1,3 @@
+export default function Settings(): JSX.Element {
+ return Settings
+}
diff --git a/src/renderer/src/routes/index.tsx b/src/renderer/src/routes/index.tsx
new file mode 100644
index 0000000..b3b3ce4
--- /dev/null
+++ b/src/renderer/src/routes/index.tsx
@@ -0,0 +1,35 @@
+import { Navigate } from 'react-router-dom'
+import Overview from '@renderer/pages/overview'
+import Proxies from '@renderer/pages/proxies'
+import Rules from '@renderer/pages/rules'
+import Settings from '@renderer/pages/settings'
+import Profiles from '@renderer/pages/profiles'
+
+const routes = [
+ {
+ path: '/overview',
+ element:
+ },
+ {
+ path: '/proxies',
+ element:
+ },
+ {
+ path: '/rules',
+ element:
+ },
+ {
+ path: '/profiles',
+ element:
+ },
+ {
+ path: '/settings',
+ element:
+ },
+ {
+ path: '/',
+ element:
+ }
+]
+
+export default routes
diff --git a/src/renderer/src/env.d.ts b/src/renderer/src/utils/env.d.ts
similarity index 100%
rename from src/renderer/src/env.d.ts
rename to src/renderer/src/utils/env.d.ts
diff --git a/src/renderer/src/utils/types.d.ts b/src/renderer/src/utils/types.d.ts
new file mode 100644
index 0000000..fd2a435
--- /dev/null
+++ b/src/renderer/src/utils/types.d.ts
@@ -0,0 +1 @@
+type OutboundMode = 'rule' | 'global' | 'direct'
diff --git a/tsconfig.web.json b/tsconfig.web.json
index 9c16b66..7ce117a 100644
--- a/tsconfig.web.json
+++ b/tsconfig.web.json
@@ -1,7 +1,7 @@
{
"extends": "@electron-toolkit/tsconfig/tsconfig.web.json",
"include": [
- "src/renderer/src/env.d.ts",
+ "src/renderer/src/utils/env.d.ts",
"src/renderer/src/**/*",
"src/renderer/src/**/*.tsx",
"src/preload/*.d.ts"