feat: clash meta core supports

This commit is contained in:
GyDi 2022-05-17 01:59:49 +08:00
parent f5c6fa842a
commit be9ea4ea8e
No known key found for this signature in database
GPG Key ID: 1C95E0D3467B3084
13 changed files with 195 additions and 36 deletions

View File

@ -5,7 +5,7 @@
"scripts": {
"dev": "tauri dev",
"dev:diff": "tauri dev -f verge-dev -c src-tauri/tauri.diff.json",
"dev:meta": "tauri dev -f meta-dev -c src-tauri/tauri.meta.json",
"dev:core": "cross-env VITE_MULTI_CORE=1 yarn dev:diff",
"build": "tauri build",
"tauri": "tauri",
"web:dev": "vite",
@ -47,6 +47,7 @@
"@types/react-dom": "^17.0.0",
"@vitejs/plugin-react": "^1.3.2",
"adm-zip": "^0.5.9",
"cross-env": "^7.0.3",
"fs-extra": "^10.0.0",
"https-proxy-agent": "^5.0.1",
"husky": "^7.0.0",

View File

@ -45,9 +45,7 @@ windows-sys = { version = "0.36", features = ["Win32_System_LibraryLoader", "Win
[features]
default = ["custom-protocol", "tauri/ayatana-tray"]
custom-protocol = ["tauri/custom-protocol"]
meta-dev = ["clash-meta", "verge-dev"]
verge-dev = []
clash-meta = []
debug-yml = []
[profile.release]

View File

@ -196,10 +196,16 @@ pub fn patch_verge_config(
payload: Verge,
app_handle: tauri::AppHandle,
core: State<'_, Core>,
) -> Result<(), String> {
) -> CmdResult {
wrap_err!(core.patch_verge(payload, &app_handle))
}
/// change clash core
#[tauri::command]
pub fn change_clash_core(core: State<'_, Core>, clash_core: Option<String>) -> CmdResult {
wrap_err!(core.change_core(clash_core))
}
/// restart the sidecar
#[tauri::command]
pub fn restart_sidecar(core: State<'_, Core>) -> CmdResult {

View File

@ -65,22 +65,21 @@ impl Core {
/// initialize the core state
pub fn init(&self, app_handle: tauri::AppHandle) {
let verge = self.verge.lock();
let clash_core = verge.clash_core.clone();
let mut service = self.service.lock();
service.set_core(clash_core);
#[cfg(windows)]
{
let verge = self.verge.lock();
let enable = verge.enable_service_mode.clone();
let mut service = self.service.lock();
service.set_mode(enable.unwrap_or(false));
log_if_err!(service.start());
}
#[cfg(not(windows))]
{
let mut service = self.service.lock();
log_if_err!(service.start());
}
log_if_err!(service.start());
drop(verge);
drop(service);
log_if_err!(self.activate());
@ -138,6 +137,31 @@ impl Core {
self.activate_enhanced(true)
}
/// change the clash core
pub fn change_core(&self, clash_core: Option<String>) -> Result<()> {
let clash_core = clash_core.unwrap_or("clash".into());
if &clash_core != "clash" && &clash_core != "clash-meta" {
bail!("invalid clash core name \"{clash_core}\"");
}
let mut verge = self.verge.lock();
verge.patch_config(Verge {
clash_core: Some(clash_core.clone()),
..Verge::default()
})?;
drop(verge);
let mut service = self.service.lock();
service.stop()?;
service.set_core(Some(clash_core));
service.start()?;
drop(service);
self.activate()?;
self.activate_enhanced(true)
}
/// Patch Clash
/// handle the clash config changed
pub fn patch_clash(&self, patch: Mapping) -> Result<()> {

View File

@ -0,0 +1,26 @@
use anyhow::{Context, Result};
use std::env::current_exe;
pub struct CoreItem {
pub name: String,
pub path: String,
}
pub struct Multi {}
impl Multi {
pub fn list() -> Result<Vec<CoreItem>> {
let paths = current_exe()
.unwrap()
.parent()
.unwrap()
.read_dir()
.context("failed to current dir")?;
for path in paths {
dbg!(path.unwrap().path().metadata().unwrap().permissions().);
}
Ok(vec![])
}
}

View File

@ -100,15 +100,16 @@ fn main() -> std::io::Result<()> {
})
.invoke_handler(tauri::generate_handler![
// common
cmds::restart_sidecar,
cmds::get_sys_proxy,
cmds::get_cur_proxy,
cmds::kill_sidecar,
cmds::open_app_dir,
cmds::open_logs_dir,
cmds::kill_sidecar,
cmds::restart_sidecar,
// clash
cmds::get_clash_info,
cmds::patch_clash_config,
cmds::change_clash_core,
// verge
cmds::get_verge_config,
cmds::patch_verge_config,

View File

@ -1,9 +1,8 @@
{
"tauri": {
"bundle": {
"identifier": "top.gydi.clashverge.dev",
"externalBin": ["sidecar/clash", "sidecar/clash-meta"]
}
}
}
{
"tauri": {
"bundle": {
"identifier": "top.gydi.clashverge.dev",
"externalBin": ["sidecar/clash", "sidecar/clash-meta"]
}
}
}

View File

@ -1,7 +0,0 @@
{
"tauri": {
"bundle": {
"externalBin": ["sidecar/clash", "sidecar/clash-meta"]
}
}
}

View File

@ -0,0 +1,84 @@
import useSWR, { useSWRConfig } from "swr";
import { useState } from "react";
import { useLockFn } from "ahooks";
import { Menu, MenuItem } from "@mui/material";
import { Settings } from "@mui/icons-material";
import { changeClashCore, getVergeConfig } from "../../services/cmds";
import getSystem from "../../utils/get-system";
import Notice from "../base/base-notice";
const OS = getSystem();
const VALID_CORE = [
{ name: "Clash", core: "clash" },
{ name: "Clash Meta", core: "clash-meta" },
];
const CoreSwitch = () => {
const { mutate } = useSWRConfig();
const { data: vergeConfig } = useSWR("getVergeConfig", getVergeConfig);
const [anchorEl, setAnchorEl] = useState<any>(null);
const [position, setPosition] = useState({ left: 0, top: 0 });
const { clash_core = "clash" } = vergeConfig ?? {};
const onCoreChange = useLockFn(async (core: string) => {
if (core === clash_core) return;
try {
await changeClashCore(core);
mutate("getVergeConfig");
mutate("getClashConfig");
mutate("getVersion");
setAnchorEl(null);
Notice.success(`Successfully switch to ${core}`, 1000);
} catch (err: any) {
Notice.error(err?.message || err.toString());
}
});
return (
<>
<Settings
fontSize="small"
style={{ cursor: "pointer" }}
onClick={(event) => {
const { clientX, clientY } = event;
setPosition({ top: clientY, left: clientX });
setAnchorEl(event.currentTarget);
}}
/>
<Menu
open={!!anchorEl}
anchorEl={anchorEl}
onClose={() => setAnchorEl(null)}
anchorPosition={position}
anchorReference="anchorPosition"
transitionDuration={225}
TransitionProps={
OS === "macos" ? { style: { transitionDuration: "225ms" } } : {}
}
onContextMenu={(e) => {
setAnchorEl(null);
e.preventDefault();
}}
>
{VALID_CORE.map((each) => (
<MenuItem
key={each.core}
sx={{ minWidth: 125 }}
selected={each.core === clash_core}
onClick={() => onCoreChange(each.core)}
>
{each.name}
</MenuItem>
))}
</Menu>
</>
);
};
export default CoreSwitch;

View File

@ -8,6 +8,7 @@ import {
Select,
MenuItem,
Typography,
Box,
} from "@mui/material";
import { ApiType } from "../../services/types";
import { atomClashPort } from "../../services/states";
@ -16,11 +17,14 @@ import { SettingList, SettingItem } from "./setting";
import { getClashConfig, getVersion, updateConfigs } from "../../services/api";
import Notice from "../base/base-notice";
import GuardState from "./guard-state";
import CoreSwitch from "./core-switch";
interface Props {
onError: (err: Error) => void;
}
const MULTI_CORE = !!import.meta.env.VITE_MULTI_CORE;
const SettingClash = ({ onError }: Props) => {
const { t } = useTranslation();
const { mutate } = useSWRConfig();
@ -54,7 +58,7 @@ const SettingClash = ({ onError }: Props) => {
}
await patchClashConfig({ "mixed-port": port });
setGlobalClashPort(port);
Notice.success("Change Clash port successfully!");
Notice.success("Change Clash port successfully!", 1000);
// update the config
mutate("getClashConfig");
@ -129,7 +133,18 @@ const SettingClash = ({ onError }: Props) => {
</SettingItem>
<SettingItem>
<ListItemText primary={t("Clash Core")} />
<ListItemText
primary={
MULTI_CORE ? (
<Box sx={{ display: "flex", alignItems: "center" }}>
<span style={{ marginRight: 4 }}>{t("Clash Core")}</span>
<CoreSwitch />
</Box>
) : (
t("Clash Core")
)
}
/>
<Typography sx={{ py: 1 }}>{clashVer}</Typography>
</SettingItem>
</SettingList>

View File

@ -86,6 +86,10 @@ export async function getSystemProxy() {
return invoke<any>("get_sys_proxy");
}
export async function changeClashCore(clashCore: string) {
return invoke<any>("change_clash_core", { clashCore });
}
export async function restartSidecar() {
return invoke<void>("restart_sidecar");
}

View File

@ -1,7 +1,7 @@
/**
* Some interface for clash api
*/
export namespace ApiType {
export namespace ApiType {
export interface ConfigData {
port: number;
mode: string;
@ -125,6 +125,7 @@ export namespace CmdType {
export interface VergeConfig {
language?: string;
clash_core?: string;
theme_mode?: "light" | "dark";
theme_blur?: boolean;
traffic_graph?: boolean;

View File

@ -1201,7 +1201,14 @@ cosmiconfig@^7.0.1:
path-type "^4.0.0"
yaml "^1.10.0"
cross-spawn@^7.0.0:
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-spawn@^7.0.0, cross-spawn@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==