diff --git a/src/components/layout/layout-traffic.tsx b/src/components/layout/layout-traffic.tsx
index 296f677f764e122485915929525b197bd52a2035..7d8787dc068d1ad5ed52af809d9ef4122ccfc755 100644
--- a/src/components/layout/layout-traffic.tsx
+++ b/src/components/layout/layout-traffic.tsx
@@ -1,57 +1,40 @@
 import { useEffect, useRef, useState } from "react";
-import { useRecoilValue } from "recoil";
 import { Box, Typography } from "@mui/material";
 import { ArrowDownward, ArrowUpward } from "@mui/icons-material";
-import { listen } from "@tauri-apps/api/event";
-import { getInformation } from "@/services/api";
-import { atomClashPort } from "@/services/states";
+import { useClashInfo } from "@/hooks/use-clash";
 import { useVerge } from "@/hooks/use-verge";
-import TrafficGraph from "./traffic-graph";
-import useLogSetup from "./use-log-setup";
+import { TrafficGraph, type TrafficRef } from "./traffic-graph";
+import { useLogSetup } from "./use-log-setup";
 import parseTraffic from "@/utils/parse-traffic";
 
 // setup the traffic
 const LayoutTraffic = () => {
-  const portValue = useRecoilValue(atomClashPort);
-  const [traffic, setTraffic] = useState({ up: 0, down: 0 });
-  const [refresh, setRefresh] = useState({});
-
-  const trafficRef = useRef<any>();
+  const { clashInfo } = useClashInfo();
 
   // whether hide traffic graph
   const { verge } = useVerge();
   const trafficGraph = verge?.traffic_graph ?? true;
 
+  const trafficRef = useRef<TrafficRef>(null);
+  const [traffic, setTraffic] = useState({ up: 0, down: 0 });
+
   // setup log ws during layout
   useLogSetup();
 
   useEffect(() => {
-    // should reconnect the traffic ws
-    const unlisten = listen("verge://refresh-clash-config", () =>
-      setRefresh({})
-    );
-
-    return () => {
-      unlisten.then((fn) => fn());
-    };
-  }, []);
-
-  useEffect(() => {
-    let ws: WebSocket | null = null;
+    if (!clashInfo) return;
 
-    getInformation().then((result) => {
-      const { server = "", secret = "" } = result;
-      ws = new WebSocket(`ws://${server}/traffic?token=${secret}`);
+    const { server = "", secret = "" } = clashInfo;
+    const ws = new WebSocket(`ws://${server}/traffic?token=${secret}`);
 
-      ws.addEventListener("message", (event) => {
-        const data = JSON.parse(event.data) as ITrafficItem;
-        trafficRef.current?.appendData(data);
-        setTraffic(data);
-      });
+    ws.addEventListener("message", (event) => {
+      const data = JSON.parse(event.data) as ITrafficItem;
+      trafficRef.current?.appendData(data);
+      setTraffic(data);
     });
 
     return () => ws?.close();
-  }, [portValue, refresh]);
+  }, [clashInfo]);
 
   const [up, upUnit] = parseTraffic(traffic.up);
   const [down, downUnit] = parseTraffic(traffic.down);
@@ -60,7 +43,7 @@ const LayoutTraffic = () => {
     component: "span",
     color: "primary",
     textAlign: "center",
-    sx: { flex: "1 1 54px" },
+    sx: { flex: "1 1 54px", userSelect: "none" },
   };
   const unitStyle: any = {
     component: "span",
@@ -78,7 +61,7 @@ const LayoutTraffic = () => {
     >
       {trafficGraph && (
         <div style={{ width: "100%", height: 60, marginBottom: 6 }}>
-          <TrafficGraph instance={trafficRef} />
+          <TrafficGraph ref={trafficRef} />
         </div>
       )}
 
diff --git a/src/components/layout/traffic-graph.tsx b/src/components/layout/traffic-graph.tsx
index 0a9d376d81a55a298b334fcc68329ccd1ac874d3..5c1a6b7ef9687e9ebe938408ce098e52296523a4 100644
--- a/src/components/layout/traffic-graph.tsx
+++ b/src/components/layout/traffic-graph.tsx
@@ -1,4 +1,4 @@
-import { useEffect, useRef } from "react";
+import { forwardRef, useEffect, useImperativeHandle, useRef } from "react";
 import { useTheme } from "@mui/material";
 
 const maxPoint = 30;
@@ -16,34 +16,40 @@ const defaultList = Array(maxPoint + 2).fill({ up: 0, down: 0 });
 
 type TrafficData = { up: number; down: number };
 
-interface Props {
-  instance: React.MutableRefObject<{
-    appendData: (data: TrafficData) => void;
-    toggleStyle: () => void;
-  }>;
+export interface TrafficRef {
+  appendData: (data: TrafficData) => void;
+  toggleStyle: () => void;
 }
 
 /**
  * draw the traffic graph
  */
-const TrafficGraph = (props: Props) => {
-  const { instance } = props;
-
+export const TrafficGraph = forwardRef<TrafficRef>((props, ref) => {
   const countRef = useRef(0);
   const styleRef = useRef(true);
   const listRef = useRef<TrafficData[]>(defaultList);
   const canvasRef = useRef<HTMLCanvasElement>(null!);
 
+  const cacheRef = useRef<TrafficData | null>(null);
+
   const { palette } = useTheme();
 
+  useImperativeHandle(ref, () => ({
+    appendData: (data: TrafficData) => {
+      cacheRef.current = data;
+    },
+    toggleStyle: () => {
+      styleRef.current = !styleRef.current;
+    },
+  }));
+
   useEffect(() => {
     let timer: any;
-    let cache: TrafficData | null = null;
     const zero = { up: 0, down: 0 };
 
     const handleData = () => {
-      const data = cache ? cache : zero;
-      cache = null;
+      const data = cacheRef.current ? cacheRef.current : zero;
+      cacheRef.current = null;
 
       const list = listRef.current;
       if (list.length > maxPoint + 2) list.shift();
@@ -53,19 +59,9 @@ const TrafficGraph = (props: Props) => {
       timer = setTimeout(handleData, 1000);
     };
 
-    instance.current = {
-      appendData: (data: TrafficData) => {
-        cache = data;
-      },
-      toggleStyle: () => {
-        styleRef.current = !styleRef.current;
-      },
-    };
-
     handleData();
 
     return () => {
-      instance.current = null!;
       if (timer) clearTimeout(timer);
     };
   }, []);
@@ -196,6 +192,4 @@ const TrafficGraph = (props: Props) => {
   }, [palette]);
 
   return <canvas ref={canvasRef} style={{ width: "100%", height: "100%" }} />;
-};
-
-export default TrafficGraph;
+});
diff --git a/src/components/layout/use-log-setup.ts b/src/components/layout/use-log-setup.ts
index a56339313f3e569cacc15619b052ed0b7448a42f..d7f06a1b8b6659a4584e8a34a4a9f02a2cbeae77 100644
--- a/src/components/layout/use-log-setup.ts
+++ b/src/components/layout/use-log-setup.ts
@@ -1,48 +1,36 @@
 import dayjs from "dayjs";
-import { useEffect, useState } from "react";
+import { useEffect } from "react";
 import { useRecoilValue, useSetRecoilState } from "recoil";
-import { listen } from "@tauri-apps/api/event";
-import { getInformation } from "@/services/api";
 import { getClashLogs } from "@/services/cmds";
+import { useClashInfo } from "@/hooks/use-clash";
 import { atomEnableLog, atomLogData } from "@/services/states";
 
 const MAX_LOG_NUM = 1000;
 
 // setup the log websocket
-export default function useLogSetup() {
-  const [refresh, setRefresh] = useState({});
+export const useLogSetup = () => {
+  const { clashInfo } = useClashInfo();
 
   const enableLog = useRecoilValue(atomEnableLog);
   const setLogData = useSetRecoilState(atomLogData);
 
   useEffect(() => {
-    if (!enableLog) return;
+    if (!enableLog || !clashInfo) return;
 
     getClashLogs().then(setLogData);
 
-    const handler = (event: MessageEvent<any>) => {
+    const { server = "", secret = "" } = clashInfo;
+    const ws = new WebSocket(`ws://${server}/logs?token=${secret}`);
+
+    ws.addEventListener("message", (event) => {
       const data = JSON.parse(event.data) as ILogItem;
       const time = dayjs().format("MM-DD HH:mm:ss");
       setLogData((l) => {
         if (l.length >= MAX_LOG_NUM) l.shift();
         return [...l, { ...data, time }];
       });
-    };
-
-    const ws = getInformation().then((info) => {
-      const { server = "", secret = "" } = info;
-      const ws = new WebSocket(`ws://${server}/logs?token=${secret}`);
-      ws.addEventListener("message", handler);
-      return ws;
     });
 
-    const unlisten = listen("verge://refresh-clash-config", () =>
-      setRefresh({})
-    );
-
-    return () => {
-      ws.then((ws) => ws?.close());
-      unlisten.then((fn) => fn());
-    };
-  }, [refresh, enableLog]);
-}
+    return () => ws?.close();
+  }, [clashInfo, enableLog]);
+};
diff --git a/src/components/setting/mods/clash-port-viewer.tsx b/src/components/setting/mods/clash-port-viewer.tsx
index e09fe54896077821e8b718a453435e43667aa008..2079740536eed186cd77c707478692efc264eace 100644
--- a/src/components/setting/mods/clash-port-viewer.tsx
+++ b/src/components/setting/mods/clash-port-viewer.tsx
@@ -1,55 +1,37 @@
-import useSWR from "swr";
 import { forwardRef, useImperativeHandle, useState } from "react";
-import { useSetRecoilState } from "recoil";
 import { useTranslation } from "react-i18next";
 import { useLockFn } from "ahooks";
 import { List, ListItem, ListItemText, TextField } from "@mui/material";
-import { atomClashPort } from "@/services/states";
-import { getClashConfig } from "@/services/api";
-import { patchClashConfig } from "@/services/cmds";
+import { useClashInfo } from "@/hooks/use-clash";
 import { BaseDialog, DialogRef, Notice } from "@/components/base";
 
 export const ClashPortViewer = forwardRef<DialogRef>((props, ref) => {
   const { t } = useTranslation();
 
-  const { data: config, mutate: mutateClash } = useSWR(
-    "getClashConfig",
-    getClashConfig
-  );
+  const { clashInfo, patchInfo } = useClashInfo();
 
   const [open, setOpen] = useState(false);
-  const [port, setPort] = useState(config?.["mixed-port"] ?? 9090);
-
-  const setGlobalClashPort = useSetRecoilState(atomClashPort);
+  const [port, setPort] = useState(clashInfo?.port ?? 7890);
 
   useImperativeHandle(ref, () => ({
     open: () => {
-      if (config?.["mixed-port"]) {
-        setPort(config["mixed-port"]);
-      }
+      if (clashInfo?.port) setPort(clashInfo?.port);
       setOpen(true);
     },
     close: () => setOpen(false),
   }));
 
   const onSave = useLockFn(async () => {
-    if (port < 1000) {
-      return Notice.error("The port should not < 1000");
-    }
-    if (port > 65536) {
-      return Notice.error("The port should not > 65536");
+    if (port === clashInfo?.port) {
+      setOpen(false);
+      return;
     }
-
-    setOpen(false);
-    if (port === config?.["mixed-port"]) return;
-
     try {
-      await patchClashConfig({ "mixed-port": port });
-      setGlobalClashPort(port);
+      await patchInfo({ "mixed-port": port });
+      setOpen(false);
       Notice.success("Change Clash port successfully!", 1000);
-      mutateClash();
     } catch (err: any) {
-      Notice.error(err.message || err.toString(), 5000);
+      Notice.error(err.message || err.toString(), 4000);
     }
   });
 
diff --git a/src/components/setting/mods/controller-viewer.tsx b/src/components/setting/mods/controller-viewer.tsx
index d3d6a43d4ccf0ca3b99e49568d26a5204e9d65d0..17205d9cd8e899b633649c24c3047f596b56ea71 100644
--- a/src/components/setting/mods/controller-viewer.tsx
+++ b/src/components/setting/mods/controller-viewer.tsx
@@ -1,17 +1,16 @@
-import useSWR from "swr";
 import { forwardRef, useImperativeHandle, useState } from "react";
 import { useLockFn } from "ahooks";
 import { useTranslation } from "react-i18next";
 import { List, ListItem, ListItemText, TextField } from "@mui/material";
-import { getClashInfo, patchClashConfig } from "@/services/cmds";
-import { getAxios } from "@/services/api";
+import { useClashInfo } from "@/hooks/use-clash";
 import { BaseDialog, DialogRef, Notice } from "@/components/base";
 
 export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
   const { t } = useTranslation();
   const [open, setOpen] = useState(false);
 
-  const { data: clashInfo, mutate } = useSWR("getClashInfo", getClashInfo);
+  const { clashInfo, patchInfo } = useClashInfo();
+
   const [controller, setController] = useState(clashInfo?.server || "");
   const [secret, setSecret] = useState(clashInfo?.secret || "");
 
@@ -26,14 +25,11 @@ export const ControllerViewer = forwardRef<DialogRef>((props, ref) => {
 
   const onSave = useLockFn(async () => {
     try {
-      await patchClashConfig({ "external-controller": controller, secret });
-      mutate();
-      // 刷新接口
-      getAxios(true);
+      await patchInfo({ "external-controller": controller, secret });
       Notice.success("Change Clash Config successfully!", 1000);
       setOpen(false);
-    } catch (err) {
-      console.log(err);
+    } catch (err: any) {
+      Notice.error(err.message || err.toString(), 4000);
     }
   });
 
diff --git a/src/components/setting/mods/web-ui-viewer.tsx b/src/components/setting/mods/web-ui-viewer.tsx
index bb47a99b5541bb368093d12dc99fde07265c5aa8..ae1bf3194f4b193c0981d1d0c61f5b9c137a377f 100644
--- a/src/components/setting/mods/web-ui-viewer.tsx
+++ b/src/components/setting/mods/web-ui-viewer.tsx
@@ -1,23 +1,22 @@
-import useSWR from "swr";
 import { forwardRef, useImperativeHandle, useState } from "react";
 import { useLockFn } from "ahooks";
 import { useTranslation } from "react-i18next";
 import { Button, Box, Typography } from "@mui/material";
 import { useVerge } from "@/hooks/use-verge";
-import { getClashInfo, openWebUrl } from "@/services/cmds";
+import { openWebUrl } from "@/services/cmds";
 import { BaseDialog, BaseEmpty, DialogRef, Notice } from "@/components/base";
+import { useClashInfo } from "@/hooks/use-clash";
 import { WebUIItem } from "./web-ui-item";
 
 export const WebUIViewer = forwardRef<DialogRef>((props, ref) => {
   const { t } = useTranslation();
 
+  const { clashInfo } = useClashInfo();
   const { verge, patchVerge, mutateVerge } = useVerge();
 
   const [open, setOpen] = useState(false);
   const [editing, setEditing] = useState(false);
 
-  const { data: clashInfo } = useSWR("getClashInfo", getClashInfo);
-
   useImperativeHandle(ref, () => ({
     open: () => setOpen(true),
     close: () => setOpen(false),
@@ -53,9 +52,7 @@ export const WebUIViewer = forwardRef<DialogRef>((props, ref) => {
       if (url.includes("%port") || url.includes("%secret")) {
         if (!clashInfo) throw new Error("failed to get clash info");
         if (!clashInfo.server?.includes(":")) {
-          throw new Error(
-            `failed to parse server with status ${clashInfo.status}`
-          );
+          throw new Error(`failed to parse the server "${clashInfo.server}"`);
         }
 
         const port = clashInfo.server
diff --git a/src/components/setting/setting-clash.tsx b/src/components/setting/setting-clash.tsx
index caaf5de807de7e5f754f7c0eefe4872ba3cbfcd5..a3af39797bdf8b89a9ded69cfaf6ff25350ff9c8 100644
--- a/src/components/setting/setting-clash.tsx
+++ b/src/components/setting/setting-clash.tsx
@@ -1,4 +1,3 @@
-import useSWR from "swr";
 import { useRef } from "react";
 import { useTranslation } from "react-i18next";
 import {
@@ -10,9 +9,8 @@ import {
   IconButton,
 } from "@mui/material";
 import { ArrowForward } from "@mui/icons-material";
-import { patchClashConfig } from "@/services/cmds";
-import { getClashConfig, getVersion, updateConfigs } from "@/services/api";
 import { DialogRef } from "@/components/base";
+import { useClash } from "@/hooks/use-clash";
 import { GuardState } from "./mods/guard-state";
 import { CoreSwitch } from "./mods/core-switch";
 import { WebUIViewer } from "./mods/web-ui-viewer";
@@ -28,18 +26,14 @@ interface Props {
 const SettingClash = ({ onError }: Props) => {
   const { t } = useTranslation();
 
-  const { data: clashConfig, mutate: mutateClash } = useSWR(
-    "getClashConfig",
-    getClashConfig
-  );
-  const { data: versionData } = useSWR("getVersion", getVersion);
+  const { clash, version, mutateClash, patchClash } = useClash();
 
   const {
     ipv6,
     "allow-lan": allowLan,
     "log-level": logLevel,
     "mixed-port": mixedPort,
-  } = clashConfig ?? {};
+  } = clash ?? {};
 
   const webRef = useRef<DialogRef>(null);
   const fieldRef = useRef<DialogRef>(null);
@@ -50,15 +44,6 @@ const SettingClash = ({ onError }: Props) => {
   const onChangeData = (patch: Partial<IConfigData>) => {
     mutateClash((old) => ({ ...(old! || {}), ...patch }), false);
   };
-  const onUpdateData = async (patch: Partial<IConfigData>) => {
-    await updateConfigs(patch);
-    await patchClashConfig(patch);
-  };
-
-  // get clash core version
-  const clashVer = versionData?.premium
-    ? `${versionData.version} Premium`
-    : versionData?.version || "-";
 
   return (
     <SettingList title={t("Clash Setting")}>
@@ -74,7 +59,7 @@ const SettingClash = ({ onError }: Props) => {
           onCatch={onError}
           onFormat={onSwitchFormat}
           onChange={(e) => onChangeData({ "allow-lan": e })}
-          onGuard={(e) => onUpdateData({ "allow-lan": e })}
+          onGuard={(e) => patchClash({ "allow-lan": e })}
         >
           <Switch edge="end" />
         </GuardState>
@@ -87,7 +72,7 @@ const SettingClash = ({ onError }: Props) => {
           onCatch={onError}
           onFormat={onSwitchFormat}
           onChange={(e) => onChangeData({ ipv6: e })}
-          onGuard={(e) => onUpdateData({ ipv6: e })}
+          onGuard={(e) => patchClash({ ipv6: e })}
         >
           <Switch edge="end" />
         </GuardState>
@@ -100,7 +85,7 @@ const SettingClash = ({ onError }: Props) => {
           onCatch={onError}
           onFormat={(e: any) => e.target.value}
           onChange={(e) => onChangeData({ "log-level": e })}
-          onGuard={(e) => onUpdateData({ "log-level": e })}
+          onGuard={(e) => patchClash({ "log-level": e })}
         >
           <Select size="small" sx={{ width: 100, "> div": { py: "7.5px" } }}>
             <MenuItem value="debug">Debug</MenuItem>
@@ -159,7 +144,7 @@ const SettingClash = ({ onError }: Props) => {
       </SettingItem>
 
       <SettingItem label={t("Clash Core")} extra={<CoreSwitch />}>
-        <Typography sx={{ py: "7px" }}>{clashVer}</Typography>
+        <Typography sx={{ py: "7px", pr: 1 }}>{version}</Typography>
       </SettingItem>
     </SettingList>
   );
diff --git a/src/hooks/use-clash.ts b/src/hooks/use-clash.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9804c5fc4de1867e1963f9dc311c75df5eb13bb4
--- /dev/null
+++ b/src/hooks/use-clash.ts
@@ -0,0 +1,83 @@
+import useSWR, { mutate } from "swr";
+import { useLockFn } from "ahooks";
+import {
+  getAxios,
+  getClashConfig,
+  getVersion,
+  updateConfigs,
+} from "@/services/api";
+import { getClashInfo, patchClashConfig } from "@/services/cmds";
+
+export const useClash = () => {
+  const { data: clash, mutate: mutateClash } = useSWR(
+    "getClashConfig",
+    getClashConfig
+  );
+
+  const { data: versionData, mutate: mutateVersion } = useSWR(
+    "getVersion",
+    getVersion
+  );
+
+  const patchClash = useLockFn(async (patch: Partial<IConfigData>) => {
+    await updateConfigs(patch);
+    await patchClashConfig(patch);
+    mutateClash();
+  });
+
+  const version = versionData?.premium
+    ? `${versionData.version} Premium`
+    : versionData?.meta
+    ? `${versionData.version} Meta`
+    : versionData?.version || "-";
+
+  return {
+    clash,
+    version,
+    mutateClash,
+    mutateVersion,
+    patchClash,
+  };
+};
+
+export const useClashInfo = () => {
+  const { data: clashInfo, mutate: mutateInfo } = useSWR(
+    "getClashInfo",
+    getClashInfo
+  );
+
+  const patchInfo = async (
+    patch: Partial<
+      Pick<IConfigData, "mixed-port" | "external-controller" | "secret">
+    >
+  ) => {
+    const hasInfo =
+      patch["mixed-port"] != null ||
+      patch["external-controller"] != null ||
+      patch.secret != null;
+
+    if (!hasInfo) return;
+
+    if (patch["mixed-port"]) {
+      const port = patch["mixed-port"];
+      if (port < 1000) {
+        throw new Error("The port should not < 1000");
+      }
+      if (port > 65536) {
+        throw new Error("The port should not > 65536");
+      }
+    }
+
+    await patchClashConfig(patch);
+    mutateInfo();
+    mutate("getClashConfig");
+    // 刷新接口
+    getAxios(true);
+  };
+
+  return {
+    clashInfo,
+    mutateInfo,
+    patchInfo,
+  };
+};
diff --git a/src/pages/connections.tsx b/src/pages/connections.tsx
index eefaa6aed9806814851b0a77443daa28f092bade..7ea14449ff3ce257ec4d0a059697df19efcddb67 100644
--- a/src/pages/connections.tsx
+++ b/src/pages/connections.tsx
@@ -13,8 +13,9 @@ import { useRecoilState } from "recoil";
 import { Virtuoso } from "react-virtuoso";
 import { useTranslation } from "react-i18next";
 import { TableChartRounded, TableRowsRounded } from "@mui/icons-material";
-import { closeAllConnections, getInformation } from "@/services/api";
+import { closeAllConnections } from "@/services/api";
 import { atomConnectionSetting } from "@/services/states";
+import { useClashInfo } from "@/hooks/use-clash";
 import { BaseEmpty, BasePage } from "@/components/base";
 import ConnectionItem from "@/components/connection/connection-item";
 import ConnectionTable from "@/components/connection/connection-table";
@@ -25,6 +26,7 @@ type OrderFunc = (list: IConnectionsItem[]) => IConnectionsItem[];
 
 const ConnectionsPage = () => {
   const { t, i18n } = useTranslation();
+  const { clashInfo } = useClashInfo();
 
   const [filterText, setFilterText] = useState("");
   const [curOrderOpt, setOrderOpt] = useState("Default");
@@ -52,51 +54,49 @@ const ConnectionsPage = () => {
   }, [connData, filterText, curOrderOpt]);
 
   useEffect(() => {
-    let ws: WebSocket | null = null;
+    if (!clashInfo) return;
 
-    getInformation().then((result) => {
-      const { server = "", secret = "" } = result;
-      ws = new WebSocket(`ws://${server}/connections?token=${secret}`);
+    const { server = "", secret = "" } = clashInfo;
+    const ws = new WebSocket(`ws://${server}/connections?token=${secret}`);
 
-      ws.addEventListener("message", (event) => {
-        const data = JSON.parse(event.data) as IConnections;
+    ws.addEventListener("message", (event) => {
+      const data = JSON.parse(event.data) as IConnections;
 
-        // 与前一次connections的展示顺序尽量保持一致
-        setConnData((old) => {
-          const oldConn = old.connections;
-          const maxLen = data.connections.length;
+      // 尽量与前一次connections的展示顺序保持一致
+      setConnData((old) => {
+        const oldConn = old.connections;
+        const maxLen = data.connections.length;
 
-          const connections: typeof oldConn = [];
+        const connections: typeof oldConn = [];
 
-          const rest = data.connections.filter((each) => {
-            const index = oldConn.findIndex((o) => o.id === each.id);
+        const rest = data.connections.filter((each) => {
+          const index = oldConn.findIndex((o) => o.id === each.id);
 
-            if (index >= 0 && index < maxLen) {
-              const old = oldConn[index];
-              each.curUpload = each.upload - old.upload;
-              each.curDownload = each.download - old.download;
+          if (index >= 0 && index < maxLen) {
+            const old = oldConn[index];
+            each.curUpload = each.upload - old.upload;
+            each.curDownload = each.download - old.download;
 
-              connections[index] = each;
-              return false;
-            }
-            return true;
-          });
-
-          for (let i = 0; i < maxLen; ++i) {
-            if (!connections[i] && rest.length > 0) {
-              connections[i] = rest.shift()!;
-              connections[i].curUpload = 0;
-              connections[i].curDownload = 0;
-            }
+            connections[index] = each;
+            return false;
           }
-
-          return { ...data, connections };
+          return true;
         });
+
+        for (let i = 0; i < maxLen; ++i) {
+          if (!connections[i] && rest.length > 0) {
+            connections[i] = rest.shift()!;
+            connections[i].curUpload = 0;
+            connections[i].curDownload = 0;
+          }
+        }
+
+        return { ...data, connections };
       });
     });
 
     return () => ws?.close();
-  }, []);
+  }, [clashInfo]);
 
   const onCloseAll = useLockFn(closeAllConnections);
 
@@ -140,6 +140,7 @@ const ConnectionsPage = () => {
             height: "36px",
             display: "flex",
             alignItems: "center",
+            userSelect: "text",
           }}
         >
           {!isTableLayout && (
@@ -176,7 +177,7 @@ const ConnectionsPage = () => {
           />
         </Box>
 
-        <Box height="calc(100% - 50px)">
+        <Box height="calc(100% - 50px)" sx={{ userSelect: "text" }}>
           {filterConn.length === 0 ? (
             <BaseEmpty text="No Connections" />
           ) : isTableLayout ? (
diff --git a/src/services/api.ts b/src/services/api.ts
index 8ff75b7fc75dac6f742ad8477ffaf6bba8dc5551..2613a29028cfd97b0b4cea500f1536114b170db8 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -43,6 +43,7 @@ export async function getVersion() {
   const instance = await getAxios();
   return instance.get("/version") as Promise<{
     premium: boolean;
+    meta?: boolean;
     version: string;
   }>;
 }
diff --git a/src/services/states.ts b/src/services/states.ts
index 8ef38c26174a19b5a7fe274b6f74da040c1ed98a..399eb3e85516ae8b35d718dce49b804d3d2f1532 100644
--- a/src/services/states.ts
+++ b/src/services/states.ts
@@ -5,11 +5,6 @@ export const atomThemeMode = atom<"light" | "dark">({
   default: "light",
 });
 
-export const atomClashPort = atom<number>({
-  key: "atomClashPort",
-  default: 0,
-});
-
 export const atomLogData = atom<ILogItem[]>({
   key: "atomLogData",
   default: [],
diff --git a/src/services/types.d.ts b/src/services/types.d.ts
index aea39387dc860d1d4542f6619bbb37a0e69e2b9b..17fb5bf2bcc901cb4758cbb37235642848efb4ba 100644
--- a/src/services/types.d.ts
+++ b/src/services/types.d.ts
@@ -94,8 +94,8 @@ interface IConnections {
 type IProfileType = "local" | "remote" | "merge" | "script";
 
 interface IClashInfo {
-  status: string;
-  port?: string; // clash mixed port
+  // status: string;
+  port?: number; // clash mixed port
   server?: string; // external-controller
   secret?: string;
 }