diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss
index c751c11..3d5ba11 100644
--- a/src/assets/styles/index.scss
+++ b/src/assets/styles/index.scss
@@ -1,5 +1,5 @@
html {
- background-color: #fff;
+ background-color: #fefefe;
}
body {
@@ -15,53 +15,4 @@ code {
monospace;
}
-.layout {
- width: 100%;
- height: 100%;
- display: flex;
-
- &__sidebar {
- height: 100vh;
- flex: 1 1 25%;
- border-right: 1px solid #ccc;
-
- > h1 {
- text-align: center;
- color: #303133;
- }
-
- > h3 {
- text-align: center;
- color: #909399;
- }
- }
-
- &__links {
- $link-height: 60px;
-
- border-top: 1px solid #ccc;
-
- > a {
- display: block;
- width: 100%;
- height: $link-height;
- line-height: $link-height;
- text-align: center;
- user-select: none;
- font-size: 24px;
- color: #606266;
- border-bottom: 1px solid #ccc;
- text-decoration: none;
-
- &.active {
- background-color: #eee;
- }
- }
- }
-
- &__content {
- flex: 1 1 75%;
- padding: 20px 30px;
- box-sizing: border-box;
- }
-}
+@import "./layout.scss";
diff --git a/src/assets/styles/layout.scss b/src/assets/styles/layout.scss
new file mode 100644
index 0000000..5f52266
--- /dev/null
+++ b/src/assets/styles/layout.scss
@@ -0,0 +1,28 @@
+.layout {
+ width: 100%;
+ height: 100%;
+ display: flex;
+
+ &__sidebar {
+ position: relative;
+ height: 100vh;
+ flex: 1 1 25%;
+ }
+
+ &__traffic {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 18px;
+
+ > div {
+ margin: 0 auto;
+ }
+ }
+
+ &__content {
+ flex: 1 1 75%;
+ padding: 20px 30px;
+ box-sizing: border-box;
+ }
+}
diff --git a/src/components/list-item-link.tsx b/src/components/list-item-link.tsx
new file mode 100644
index 0000000..95fc527
--- /dev/null
+++ b/src/components/list-item-link.tsx
@@ -0,0 +1,31 @@
+import { ListItem, ListItemButton, ListItemText } from "@mui/material";
+import { useMatch, useResolvedPath, useNavigate } from "react-router-dom";
+import type { LinkProps } from "react-router-dom";
+
+const ListItemLink = (props: LinkProps) => {
+ const { to, children } = props;
+
+ const resolved = useResolvedPath(to);
+ const match = useMatch({ path: resolved.pathname, end: true });
+ const navigate = useNavigate();
+
+ return (
+
+ navigate(to)}
+ >
+
+
+
+ );
+};
+
+export default ListItemLink;
diff --git a/src/components/traffic.tsx b/src/components/traffic.tsx
new file mode 100644
index 0000000..26b8f55
--- /dev/null
+++ b/src/components/traffic.tsx
@@ -0,0 +1,69 @@
+import axios from "axios";
+import { useEffect, useState } from "react";
+import { ArrowDownward, ArrowUpward } from "@mui/icons-material";
+import parseTraffic from "../utils/parse-traffic";
+import { Typography } from "@mui/material";
+import { Box } from "@mui/system";
+
+const Traffic = () => {
+ const [traffic, setTraffic] = useState({ up: 0, down: 0 });
+
+ useEffect(() => {
+ const onTraffic = () => {
+ axios({
+ url: `http://127.0.0.1:9090/traffic`,
+ method: "GET",
+ onDownloadProgress: (progressEvent) => {
+ const data = progressEvent.currentTarget.response || "";
+ const lastData = data.slice(data.trim().lastIndexOf("\n") + 1);
+ try {
+ if (lastData) setTraffic(JSON.parse(lastData));
+ } catch {}
+ },
+ }).catch(() => setTimeout(onTraffic, 500));
+ };
+
+ onTraffic();
+ }, []);
+
+ const [up, upUnit] = parseTraffic(traffic.up);
+ const [down, downUnit] = parseTraffic(traffic.down);
+
+ const valStyle: any = {
+ component: "span",
+ color: "primary",
+ textAlign: "center",
+ sx: { flex: "1 1 54px" },
+ };
+ const unitStyle: any = {
+ component: "span",
+ color: "grey.500",
+ fontSize: "12px",
+ textAlign: "right",
+ sx: { flex: "0 1 28px", userSelect: "none" },
+ };
+
+ return (
+
+
+ 0 ? "primary" : "disabled"}
+ />
+ {up}
+ {upUnit}
+
+
+
+ 0 ? "primary" : "disabled"}
+ />
+ {down}
+ {downUnit}
+
+
+ );
+};
+
+export default Traffic;
diff --git a/src/main.tsx b/src/main.tsx
index 40dca8c..a9fa329 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -2,39 +2,32 @@ import "./assets/styles/index.scss";
import React from "react";
import ReactDOM from "react-dom";
-import { BrowserRouter, NavLink, Route, Routes } from "react-router-dom";
-import HomePage from "./pages/home";
-import ProfilesPage from "./pages/profiles";
-import { version } from "../package.json";
+import { BrowserRouter } from "react-router-dom";
+import { createTheme, ThemeProvider } from "@mui/material";
+import Layout from "./pages/_layout";
-function Layout() {
- return (
-
-
-
Clash Verge
-
{version}
+const theme = createTheme({
+ palette: {
+ mode: "light",
+ primary: {
+ main: "#5b5c9d",
+ },
+ text: {
+ primary: "#637381",
+ secondary: "#909399",
+ },
+ },
+});
-
- Home
- Profiles
-
-
-
-
-
- } />
- } />
-
-
-
- );
-}
+// console.log(theme);
ReactDOM.render(
-
-
-
+
+
+
+
+
,
document.getElementById("root")
);
diff --git a/src/pages/_layout.tsx b/src/pages/_layout.tsx
new file mode 100644
index 0000000..ee09077
--- /dev/null
+++ b/src/pages/_layout.tsx
@@ -0,0 +1,74 @@
+import { Route, Routes } from "react-router-dom";
+import { List, Paper, Typography } from "@mui/material";
+import LogPage from "../pages/log";
+import HomePage from "../pages/home";
+import ProxyPage from "../pages/proxy";
+import SettingPage from "../pages/setting";
+import ProfilesPage from "../pages/profiles";
+import ConnectionsPage from "../pages/connections";
+import ListItemLink from "../components/list-item-link";
+import Traffic from "../components/traffic";
+
+const Layout = () => {
+ const routers = [
+ {
+ label: "代理",
+ link: "/proxy",
+ },
+ {
+ label: "规则",
+ link: "/profiles",
+ },
+ {
+ label: "连接",
+ link: "/connections",
+ },
+ {
+ label: "日志",
+ link: "/log",
+ },
+ {
+ label: "设置",
+ link: "/setting",
+ },
+ ];
+
+ return (
+
+
+
+ Clash Verge
+
+
+
+ {routers.map((router) => (
+
+ {router.label}
+
+ ))}
+
+
+
+
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+ } />
+ } />
+
+
+
+ );
+};
+
+export default Layout;
diff --git a/src/pages/connections.tsx b/src/pages/connections.tsx
new file mode 100644
index 0000000..cb267e0
--- /dev/null
+++ b/src/pages/connections.tsx
@@ -0,0 +1,5 @@
+const ConnectionsPage = () => {
+ return Connection
;
+};
+
+export default ConnectionsPage;
diff --git a/src/pages/home.tsx b/src/pages/home.tsx
index f7ff78f..5bda849 100644
--- a/src/pages/home.tsx
+++ b/src/pages/home.tsx
@@ -1,18 +1,10 @@
-import { useState } from "react";
-import { TextField } from "@material-ui/core";
+import { Typography } from "@mui/material";
const HomePage = () => {
- const [port, setPort] = useState("7890");
-
return (
-
- setPort(e.target.value)}
- />
-
+
+ Hello Clash!
+
);
};
diff --git a/src/pages/log.tsx b/src/pages/log.tsx
new file mode 100644
index 0000000..6b5c9e6
--- /dev/null
+++ b/src/pages/log.tsx
@@ -0,0 +1,5 @@
+const LogPage = () => {
+ return Log
;
+};
+
+export default LogPage;
diff --git a/src/pages/profiles.tsx b/src/pages/profiles.tsx
index 4ed657b..067833e 100644
--- a/src/pages/profiles.tsx
+++ b/src/pages/profiles.tsx
@@ -1,6 +1,6 @@
import { useState } from "react";
import { invoke } from "@tauri-apps/api";
-import { Button, Grid, TextField } from "@material-ui/core";
+import { Button, Grid, TextField } from "@mui/material";
const ProfilesPage = () => {
const [url, setUrl] = useState("");
diff --git a/src/pages/proxy.tsx b/src/pages/proxy.tsx
new file mode 100644
index 0000000..2808614
--- /dev/null
+++ b/src/pages/proxy.tsx
@@ -0,0 +1,5 @@
+const ProxyPage = () => {
+ return Proxy
;
+};
+
+export default ProxyPage;
diff --git a/src/pages/setting.tsx b/src/pages/setting.tsx
new file mode 100644
index 0000000..be8d857
--- /dev/null
+++ b/src/pages/setting.tsx
@@ -0,0 +1,5 @@
+const SettingPage = () => {
+ return Setting
;
+};
+
+export default SettingPage;
diff --git a/src/utils/parse-traffic.ts b/src/utils/parse-traffic.ts
new file mode 100644
index 0000000..529cc3d
--- /dev/null
+++ b/src/utils/parse-traffic.ts
@@ -0,0 +1,23 @@
+const parseTraffic = (num: number) => {
+ const gb = 1024 ** 3;
+ const mb = 1024 ** 2;
+ const kb = 1024;
+ let t = num;
+ let u = "B";
+
+ if (num < 1000) return [`${Math.round(t)}`, "B/s"];
+ if (num <= mb) {
+ t = num / kb;
+ u = "KB";
+ } else if (num <= gb) {
+ t = num / mb;
+ u = "MB";
+ } else {
+ t = num / gb;
+ u = "GB";
+ }
+ if (t >= 100) return [`${Math.round(t)}`, `${u}/s`];
+ return [`${Math.round(t * 10) / 10}`, `${u}/s`];
+};
+
+export default parseTraffic;