Compare commits

...

18 Commits

36 changed files with 225 additions and 106 deletions

View File

@@ -57,6 +57,6 @@ body:
- type: textarea
attributes:
label: 日志 / Log
description: 请提供完整或相关部分的Debug日志请在“软件左侧菜单”->“设置”->“日志等级”调整到debugVerge错误请把“杂项设置”->“app日志等级”调整到trace并重启Verge生效。日志文件在“软件左侧菜单”->“设置”->“日志目录”下) / Please provide a complete or relevant part of the Debug log (please adjust the "Log level" to debug in "Software left menu" -> "Settings" -> "Log level". If there is a Verge error, please adjust "Miscellaneous settings" -> "app log level" to trace, and restart Verge to take effect. The log file is under "Software left menu" -> "Settings" -> "Log directory")
description: 请提供完整或相关部分的Debug日志请在“软件左侧菜单”->“设置”->“日志等级”调整到debugVerge错误请把“杂项设置”->“app日志等级”调整到debug/trace并重启Verge生效。日志文件在“软件左侧菜单”->“设置”->“日志目录”下) / Please provide a complete or relevant part of the Debug log (please adjust the "Log level" to debug in "Software left menu" -> "Settings" -> "Log level". If there is a Verge error, please adjust "Miscellaneous settings" -> "app log level" to trace, and restart Verge to take effect. The log file is under "Software left menu" -> "Settings" -> "Log directory")
validations:
required: true

View File

@@ -48,7 +48,7 @@ jobs:
workspaces: src-tauri
cache-all-crates: true
cache-on-failure: true
- name: Install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-22.04'
run: |
@@ -119,7 +119,7 @@ jobs:
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
@@ -129,7 +129,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: "20"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -141,10 +141,10 @@ jobs:
pnpm i
pnpm check ${{ matrix.target }}
- name: 'Setup for linux'
- name: "Setup for linux"
run: |-
sudo ls -lR /etc/apt/
cat > /tmp/sources.list << EOF
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
@@ -170,20 +170,20 @@ jobs:
patchelf:${{ matrix.arch }} \
librsvg2-dev:${{ matrix.arch }}
- name: 'Install aarch64 tools'
- name: "Install aarch64 tools"
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu
- name: 'Install armv7 tools'
- name: "Install armv7 tools"
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
run: |
sudo apt install -y \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf
- name: Build for Linux
run: |
export PKG_CONFIG_ALLOW_CROSS=1
@@ -196,7 +196,7 @@ jobs:
fi
pnpm build --target ${{ matrix.target }}
env:
NODE_OPTIONS: '--max_old_space_size=4096'
NODE_OPTIONS: "--max_old_space_size=4096"
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}

View File

@@ -50,7 +50,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -67,8 +67,8 @@ jobs:
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
@@ -109,7 +109,7 @@ jobs:
- name: Add Rust Target
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
@@ -119,7 +119,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: '20'
node-version: "22"
- name: Install pnpm
uses: pnpm/action-setup@v4
@@ -131,10 +131,10 @@ jobs:
pnpm i
pnpm check ${{ matrix.target }}
- name: 'Setup for linux'
- name: "Setup for linux"
run: |-
sudo ls -lR /etc/apt/
cat > /tmp/sources.list << EOF
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy main multiverse universe restricted
deb [arch=amd64,i386] http://archive.ubuntu.com/ubuntu jammy-security main multiverse universe restricted
@@ -160,20 +160,20 @@ jobs:
patchelf:${{ matrix.arch }} \
librsvg2-dev:${{ matrix.arch }}
- name: 'Install aarch64 tools'
- name: "Install aarch64 tools"
if: matrix.target == 'aarch64-unknown-linux-gnu'
run: |
sudo apt install -y \
gcc-aarch64-linux-gnu \
g++-aarch64-linux-gnu
- name: 'Install armv7 tools'
- name: "Install armv7 tools"
if: matrix.target == 'armv7-unknown-linux-gnueabihf'
run: |
sudo apt install -y \
gcc-arm-linux-gnueabihf \
g++-arm-linux-gnueabihf
- name: Build for Linux
run: |
export PKG_CONFIG_ALLOW_CROSS=1
@@ -186,7 +186,7 @@ jobs:
fi
pnpm build --target ${{ matrix.target }}
env:
NODE_OPTIONS: '--max_old_space_size=4096'
NODE_OPTIONS: "--max_old_space_size=4096"
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
@@ -195,6 +195,7 @@ jobs:
sudo apt-get update
sudo apt-get install jq
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
echo "BUILDTIME=$(TZ=Asia/Shanghai date)" >> $GITHUB_ENV
- name: Upload Release
uses: softprops/action-gh-release@v2
@@ -237,7 +238,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -262,8 +263,8 @@ jobs:
env:
NODE_OPTIONS: "--max_old_space_size=4096"
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tauriScript: pnpm
args: --target ${{ matrix.target }}
@@ -298,7 +299,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -323,7 +324,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm

View File

@@ -12,7 +12,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -36,7 +36,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
node-version: "22"
- uses: pnpm/action-setup@v4
name: Install pnpm

View File

@@ -1,16 +1,30 @@
## v2.0.0
## v2.0.1
### Notice
- 强烈建议完全删除 1.x 老版本再安装此版本!
- 历时3个月的紧密开发与严格测试稳定版2.0.0终于发布了巨量改进与性能、稳定性提升目前Clash Verge Rev已经有了比肩cfw的健壮性而且更强大易用
- 由于更改了服务安装逻辑Mac/Linux 首次安装需要输入 2 遍系统密码卸载和安装服务,以后可以丝滑使用 tun(虚拟网卡)模式
- 因 Tauri 2.0 底层 bug关闭窗口暂时修改为最小化功能
- 由于更改了服务安装逻辑Mac/Linux 首次安装需要输入系统密码卸载和安装服务,以后可以丝滑使用 tun(虚拟网卡)模式
- 因 Tauri 2.0 底层 bug关闭窗口后保留webview进程优点是再次打开面板更快缺点是内存使用略有增加
### 2.0.1相对于2.0.0改进了:
- 无法从 2.0rc和2.0.0 升级的问题已经安装了2.0版本的需手动下载安装)
- MacOS 系统下少有的无法安装服务,无法启动的问题,目前更健壮了
- 当系统中没有 yaml 编辑器的情况下,打开文件程序崩溃的问题
- Windows 应用内升级和覆盖安装不会删除老执行文件的问题
- 修改优化了 mac 下 fakeip 段和 dns
- 测试菜单 svg 图标格式检查
- 应用内升级重复安装 vs runtime 的问题
- 修复外部控制下密码有特殊字符认证出错的问题
- 修复恢复 Webdav 备份设置后, Webdav 设置丢失的问题
- 代理页面增加快速回到顶部的按钮
### Breaking changes
- 重大框架升级:使用 Tauri 2.0(巨量改进与性能提升)
- 强烈建议完全删除 1.x 老版本再安装此版本
- 出现 bug 到 issues 中提出以后不再接受1.x版本的bug反馈。
- 强烈建议完全删除 1.x 老版本再安装此版本
### Features
@@ -30,7 +44,7 @@
- 添加统一延迟的设置开关
- 添加 Windows 下自动检测并下载 vc runtime 的功能
- 支持显示 mux 和 mptcp 的节点标识
- 延迟测试连接更换 https 的 cp.cloudflare.com/generate_204 以防止机场劫持(关闭统一延迟的情况下延迟测试结果会有所增加)
- 延迟测试连接更换 http 的 cp.cloudflare.com/generate_204 (关闭统一延迟的情况下延迟测试结果会有所增加)
- 重构日志记录逻辑可以收集和筛选所有日志类型了之前无法记录debug的日志类型
### Performance
@@ -58,7 +72,7 @@
- 修复快捷键设置的相关 bug
- 修复 Win 下点左键菜单闪现的问题Mac 下的操作逻辑相反,默认情况下不管点左/右键均会打开菜单,闪现不属于 bug
### Know issues
### Known issues
- Windows 下窗口大小无法记忆(等待上游修复)
- Webdav 备份因为安全性和兼容性问题,暂不支持同步 Webdav 服务器地址和登录信息;跨平台配置同步

View File

@@ -1,6 +1,6 @@
{
"name": "clash-verge",
"version": "2.0.0",
"version": "2.0.1",
"license": "GPL-3.0-only",
"scripts": {
"dev": "cross-env RUST_BACKTRACE=1 tauri dev",

View File

@@ -153,13 +153,13 @@ async function getLatestReleaseVersion() {
*/
if (!META_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`
`clash meta alpha unsupported platform "${platform}-${arch}"`,
);
}
if (!META_ALPHA_MAP[`${platform}-${arch}`]) {
throw new Error(
`clash meta alpha unsupported platform "${platform}-${arch}"`
`clash meta alpha unsupported platform "${platform}-${arch}"`,
);
}
@@ -354,7 +354,7 @@ const resolvePlugin = async () => {
const tempDir = path.join(TEMP_DIR, "SimpleSC");
const tempZip = path.join(
tempDir,
"NSIS_Simple_Service_Plugin_Unicode_1.30.zip"
"NSIS_Simple_Service_Plugin_Unicode_1.30.zip",
);
const tempDll = path.join(tempDir, "SimpleSC.dll");
const pluginDir = path.join(process.env.APPDATA, "Local/NSIS");

View File

@@ -49,9 +49,9 @@ async function resolvePortable() {
zip.addLocalFolder(
path.join(
releaseDir,
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
),
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`
`Microsoft.WebView2.FixedVersionRuntime.109.0.1518.78.${arch}`,
);
zip.addLocalFolder(configDir, ".config");

2
src-tauri/Cargo.lock generated
View File

@@ -988,7 +988,7 @@ dependencies = [
[[package]]
name = "clash-verge"
version = "2.0.0"
version = "2.0.1"
dependencies = [
"aes-gcm",
"anyhow",

View File

@@ -1,6 +1,6 @@
[package]
name = "clash-verge"
version = "2.0.0"
version = "2.0.1"
description = "clash verge"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0-only"

View File

@@ -697,25 +697,62 @@ Var VC_REDIST_URL
Var VC_REDIST_EXE
Section CheckAndInstallVSRuntime
; 检查是否已安装 Visual C++ Redistributable
${If} ${IsNativeARM64}
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.arm64.exe"
StrCpy $VC_REDIST_EXE "vc_redist.arm64.exe"
IfFileExists "$SYSDIR\msvcp140.dll" Done
; 检查关键DLL
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
${ElseIf} ${RunningX64}
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x64.exe"
StrCpy $VC_REDIST_EXE "vc_redist.x64.exe"
IfFileExists "$WINDIR\SysWOW64\msvcp140.dll" Done
; 检查关键DLL
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
${Else}
StrCpy $VC_REDIST_URL "https://aka.ms/vs/17/release/vc_redist.x86.exe"
StrCpy $VC_REDIST_EXE "vc_redist.x86.exe"
IfFileExists "$SYSDIR\msvcp140.dll" Done
; 检查关键DLL
IfFileExists "$SYSDIR\vcruntime140.dll" 0 checkInstall
IfFileExists "$SYSDIR\msvcp140.dll" Done checkInstall
${EndIf}
; 下载并安装VC运行库
checkInstall:
; 检查注册表
${If} ${RunningX64}
SetRegView 64
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\${ARCH}" "Installed"
${If} $R0 == "1"
Goto Done
${EndIf}
${Else}
ReadRegDword $R0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x86" "Installed"
${If} $R0 == "1"
Goto Done
${EndIf}
${EndIf}
; 如果没有安装,则下载并安装
DetailPrint "正在下载 Visual C++ Redistributable..."
nsisdl::download "$VC_REDIST_URL" "$TEMP\$VC_REDIST_EXE"
Pop $0
${If} $0 == "success"
nsExec::Exec '"$TEMP\$VC_REDIST_EXE" /quiet /norestart'
DetailPrint "正在安装 Visual C++ Redistributable..."
ExecWait '"$TEMP\$VC_REDIST_EXE" /quiet /norestart' $0
${If} $0 == 0
DetailPrint "Visual C++ Redistributable 安装成功"
${Else}
DetailPrint "Visual C++ Redistributable 安装失败"
${EndIf}
Delete "$TEMP\$VC_REDIST_EXE"
${Else}
DetailPrint "Visual C++ Redistributable 下载失败"
${EndIf}
Done:
@@ -728,6 +765,12 @@ Section Install
nsExec::Exec 'netsh int tcp res'
!insertmacro CheckIfAppIsRunning
!insertmacro CheckAllVergeProcesses
; Delete old files before installation
; Delete clash-verge.desktop
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
Delete "$INSTDIR\Clash Verge.exe"
; Copy main executable
File "${MAINBINARYSRCPATH}"
@@ -862,6 +905,10 @@ Section Uninstall
Delete "$INSTDIR\\{{this}}"
{{/each}}
; Delete clash-verge.desktop
IfFileExists "$INSTDIR\Clash Verge.exe" 0 +2
Delete "$INSTDIR\Clash Verge.exe"
; Delete uninstaller
Delete "$INSTDIR\uninstall.exe"

View File

@@ -34,11 +34,11 @@ pub async fn use_tun(mut config: Mapping, enable: bool) -> Mapping {
revise!(dns_val, "enable", true);
revise!(dns_val, "ipv6", true);
revise!(dns_val, "enhanced-mode", "fake-ip");
revise!(dns_val, "fake-ip-range", "10.96.0.0/16");
revise!(dns_val, "fake-ip-range", "198.18.0.1/16");
#[cfg(target_os = "macos")]
{
crate::utils::resolve::restore_public_dns().await;
crate::utils::resolve::set_public_dns("10.96.0.2".to_string()).await;
crate::utils::resolve::set_public_dns("8.8.8.8".to_string()).await;
}
} else {
revise!(dns_val, "enhanced-mode", "redir-host");

View File

@@ -464,6 +464,12 @@ pub async fn delete_webdav_backup(filename: String) -> Result<()> {
}
pub async fn restore_webdav_backup(filename: String) -> Result<()> {
let verge = Config::verge();
let verge_data = verge.data().clone();
let webdav_url = verge_data.webdav_url.clone();
let webdav_username = verge_data.webdav_username.clone();
let webdav_password = verge_data.webdav_password.clone();
let backup_storage_path = app_home_dir().unwrap().join(&filename);
backup::WebDavClient::global()
.download(filename, backup_storage_path.clone())
@@ -477,6 +483,15 @@ pub async fn restore_webdav_backup(filename: String) -> Result<()> {
let mut zip = zip::ZipArchive::new(fs::File::open(backup_storage_path.clone())?)?;
zip.extract(app_home_dir()?)?;
log_err!(
patch_verge(IVerge {
webdav_url: webdav_url,
webdav_username: webdav_username,
webdav_password: webdav_password,
..IVerge::default()
})
.await
);
// 最后删除临时文件
fs::remove_file(backup_storage_path)?;
Ok(())

View File

@@ -99,9 +99,41 @@ pub fn get_last_part_and_decode(url: &str) -> Option<String> {
}
/// open file
/// use vscode by default
/// try to use vscode first, if not found then use system default app
pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> {
app.shell().open(path.to_string_lossy(), None).unwrap();
#[cfg(target_os = "macos")]
let code = "Visual Studio Code";
#[cfg(not(target_os = "macos"))]
let code = "code";
#[cfg(target_os = "windows")]
let vscode_exists = {
use std::process::Command;
Command::new("where").arg("code").output().is_ok()
};
#[cfg(target_os = "macos")]
let vscode_exists = {
use std::process::Command;
Command::new("which").arg("code").output().is_ok()
};
#[cfg(target_os = "linux")]
let vscode_exists = {
use std::process::Command;
Command::new("which").arg("code").output().is_ok()
};
// 如果 VS Code 存在就用它打开,否则用系统默认程序
if vscode_exists {
if let Err(err) = open::with(&path.as_os_str(), code) {
log::error!(target: "app", "Failed to open with VS Code: {}", err);
app.shell().open(path.to_string_lossy(), None)?;
}
} else {
app.shell().open(path.to_string_lossy(), None)?;
}
Ok(())
}

View File

@@ -25,7 +25,7 @@
"devUrl": "http://localhost:3000/"
},
"productName": "Clash Verge",
"version": "2.0.0",
"version": "2.0.1",
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"plugins": {
"updater": {

View File

@@ -38,7 +38,7 @@ export const ConnectionDetail = forwardRef<ConnectionDetailRef>(
}
/>
);
}
},
);
interface InnerProps {

View File

@@ -59,7 +59,7 @@ export const LayoutTraffic = () => {
this.close();
next(event, { up: 0, down: 0 });
},
}
},
);
return () => {
@@ -69,7 +69,7 @@ export const LayoutTraffic = () => {
{
fallbackData: { up: 0, down: 0 },
keepPreviousData: true,
}
},
);
/* --------- meta memory information --------- */
@@ -96,7 +96,7 @@ export const LayoutTraffic = () => {
this.close();
next(event, { inuse: 0 });
},
}
},
);
return () => {
@@ -106,7 +106,7 @@ export const LayoutTraffic = () => {
{
fallbackData: { inuse: 0 },
keepPreviousData: true,
}
},
);
const [up, upUnit] = parseTraffic(traffic.up);

View File

@@ -104,7 +104,7 @@ export const useCustomTheme = () => {
rootEle.style.setProperty("--primary-main", theme.palette.primary.main);
rootEle.style.setProperty(
"--background-color-alpha",
alpha(theme.palette.primary.main, 0.1)
alpha(theme.palette.primary.main, 0.1),
);
// inject css

View File

@@ -34,7 +34,7 @@ export const ProviderButton = () => {
const hasProvider = Object.keys(data || {}).length > 0;
const [updating, setUpdating] = useState(
Object.keys(data || {}).map(() => false)
Object.keys(data || {}).map(() => false),
);
const setUpdatingAt = (status: boolean, index: number) => {
@@ -107,7 +107,7 @@ export const ProviderButton = () => {
const expire = sub?.Expire || 0;
const progress = Math.min(
Math.round(((download + upload) * 100) / (total + 0.01)) + 1,
100
100,
);
return (
<>
@@ -213,7 +213,7 @@ const StyledTypeBox = styled(Box)<{ component?: React.ElementType }>(
marginRight: "4px",
padding: "0 2px",
lineHeight: 1.25,
})
}),
);
const boxStyle = {

View File

@@ -25,7 +25,7 @@ export const useRenderList = (mode: string) => {
const { data: proxiesData, mutate: mutateProxies } = useSWR(
"getProxies",
getProxies,
{ refreshInterval: 45000 }
{ refreshInterval: 45000 },
);
const { verge } = useVerge();
@@ -78,7 +78,7 @@ export const useRenderList = (mode: string) => {
group.all,
group.name,
headState.filterText,
headState.sortType
headState.sortType,
);
ret.push({ type: 1, key: `head-${group.name}`, group, headState });
@@ -97,7 +97,7 @@ export const useRenderList = (mode: string) => {
headState,
col,
proxyCol,
}))
})),
);
}
@@ -108,7 +108,7 @@ export const useRenderList = (mode: string) => {
group,
proxy,
headState,
}))
})),
);
}
return ret;

View File

@@ -32,7 +32,7 @@ export const ProviderButton = () => {
const hasProvider = Object.keys(data || {}).length > 0;
const [updating, setUpdating] = useState(
Object.keys(data || {}).map(() => false)
Object.keys(data || {}).map(() => false),
);
const setUpdatingAt = (status: boolean, index: number) => {

View File

@@ -38,7 +38,7 @@ export interface BackupTableViewerProps {
page: number;
onPageChange: (
event: React.MouseEvent<HTMLButtonElement> | null,
page: number
page: number,
) => void;
total: number;
onRefresh: () => Promise<void>;
@@ -109,7 +109,7 @@ export const BackupTableViewer = memo(
onClick={async (e: React.MouseEvent) => {
e.preventDefault();
const confirmed = await window.confirm(
t("Confirm to delete this backup file?")
t("Confirm to delete this backup file?"),
);
if (confirmed) {
await handleDelete(file.filename);
@@ -132,7 +132,7 @@ export const BackupTableViewer = memo(
onClick={async (e: React.MouseEvent) => {
e.preventDefault();
const confirmed = await window.confirm(
t("Confirm to restore this backup file?")
t("Confirm to restore this backup file?"),
);
if (confirmed) {
await handleRestore(file.filename);
@@ -181,7 +181,7 @@ export const BackupTableViewer = memo(
/>
</TableContainer>
);
}
},
);
function LinuxIcon(props: SVGProps<SVGSVGElement>) {

View File

@@ -48,7 +48,7 @@ export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
(_: React.MouseEvent<HTMLButtonElement> | null, page: number) => {
setPage(page);
},
[]
[],
);
const fetchAndSetBackupFiles = async () => {
@@ -95,8 +95,8 @@ export const BackupViewer = forwardRef<DialogRef>((props, ref) => {
setDataSource(
backupFiles.slice(
page * DEFAULT_ROWS_PER_PAGE,
page * DEFAULT_ROWS_PER_PAGE + DEFAULT_ROWS_PER_PAGE
)
page * DEFAULT_ROWS_PER_PAGE + DEFAULT_ROWS_PER_PAGE,
),
);
}, [page, backupFiles]);

View File

@@ -76,10 +76,10 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
setDownloaded((a) => {
return a + e.payload.chunkLength;
});
}
},
);
try {
await updateInfo.install();
await updateInfo.downloadAndInstall();
await relaunch();
} catch (err: any) {
Notice.error(err?.message || err.toString());
@@ -100,7 +100,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
size="small"
onClick={() => {
openUrl(
`https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/v${updateInfo?.version}`
`https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/v${updateInfo?.version}`,
);
}}
>

View File

@@ -139,7 +139,7 @@ const SettingClash = ({ onError }: Props) => {
>
<GuardState
// clash premium 2022.08.26 值为warn
value={logLevel === "warn" ? "warning" : logLevel ?? "info"}
value={logLevel === "warn" ? "warning" : (logLevel ?? "info")}
onCatch={onError}
onFormat={(e: any) => e.target.value}
onChange={(e) => onChangeData({ "log-level": e })}
@@ -165,7 +165,7 @@ const SettingClash = ({ onError }: Props) => {
onClick={() => {
Notice.success(
t("Restart Application to Apply Modifications"),
1000
1000,
);
onChangeVerge({ enable_random_port: !enable_random_port });
patchVerge({ enable_random_port: !enable_random_port });

View File

@@ -64,13 +64,18 @@ export const TestViewer = forwardRef<TestViewerRef, Props>((props, ref) => {
try {
if (!form.name) throw new Error("`Name` should not be null");
if (!form.url) throw new Error("`Url` should not be null");
let newList;
let uid;
if (form.icon && form.icon.startsWith("<svg")) {
// 移除 icon 中的注释
if (form.icon) {
form.icon = form.icon.replace(/<!--[\s\S]*?-->/g, "");
}
const doc = new DOMParser().parseFromString(
form.icon,
"image/svg+xml"
"image/svg+xml",
);
if (doc.querySelector("parsererror")) {
throw new Error("`Icon`svg format error");
@@ -97,7 +102,7 @@ export const TestViewer = forwardRef<TestViewerRef, Props>((props, ref) => {
Notice.error(err.message || err.toString());
setLoading(false);
}
})
}),
);
const handleClose = () => {

View File

@@ -10,12 +10,12 @@ import {
export const useClash = () => {
const { data: clash, mutate: mutateClash } = useSWR(
"getRuntimeConfig",
getRuntimeConfig
getRuntimeConfig,
);
const { data: versionData, mutate: mutateVersion } = useSWR(
"getVersion",
getVersion
getVersion,
);
const patchClash = useLockFn(async (patch: Partial<IConfigData>) => {
@@ -26,8 +26,8 @@ export const useClash = () => {
const version = versionData?.premium
? `${versionData.version} Premium`
: versionData?.meta
? `${versionData.version} Mihomo`
: versionData?.version || "-";
? `${versionData.version} Mihomo`
: versionData?.version || "-";
return {
clash,
@@ -41,7 +41,7 @@ export const useClash = () => {
export const useClashInfo = () => {
const { data: clashInfo, mutate: mutateInfo } = useSWR(
"getClashInfo",
getClashInfo
getClashInfo,
);
const patchInfo = async (
@@ -56,7 +56,7 @@ export const useClashInfo = () => {
| "external-controller"
| "secret"
>
>
>,
) => {
const hasInfo =
patch["redir-port"] != null ||

View File

@@ -7,7 +7,7 @@ export const useListen = () => {
const addListener = async <T>(
eventName: string,
handler: EventCallback<T>
handler: EventCallback<T>,
) => {
const unlisten = await listen(eventName, handler);
unlistenFns.current.push(unlisten);

View File

@@ -21,7 +21,7 @@ const buildWSUrl = (server: string, secret: string, logLevel: LogLevel) => {
const params = new URLSearchParams();
if (secret) {
params.append("token", encodeURIComponent(secret));
params.append("token", secret);
}
if (logLevel === "all") {
params.append("level", "debug");

View File

@@ -47,7 +47,7 @@ const ConnectionsPage = () => {
list.sort(
(a, b) =>
new Date(b.start || "0").getTime()! -
new Date(a.start || "0").getTime()!
new Date(a.start || "0").getTime()!,
),
"Upload Speed": (list) => list.sort((a, b) => b.curUpload! - a.curUpload!),
"Download Speed": (list) =>
@@ -103,7 +103,7 @@ const ConnectionsPage = () => {
next(event);
},
},
3
3,
);
return () => {
@@ -114,7 +114,7 @@ const ConnectionsPage = () => {
const [filterConn, download, upload] = useMemo(() => {
const orderFunc = orderOpts[curOrderOpt];
let connections = connData.connections.filter((conn) =>
match(conn.metadata.host || conn.metadata.destinationIP || "")
match(conn.metadata.host || conn.metadata.destinationIP || ""),
);
if (orderFunc) connections = orderFunc(connections);
@@ -152,7 +152,7 @@ const ConnectionsPage = () => {
setSetting((o) =>
o?.layout !== "table"
? { ...o, layout: "table" }
: { ...o, layout: "list" }
: { ...o, layout: "list" },
)
}
>

View File

@@ -64,7 +64,7 @@ const ProfilePage = () => {
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
}),
);
const { current } = location.state || {};
@@ -105,7 +105,7 @@ const ProfilePage = () => {
const { data: chainLogs = {}, mutate: mutateLogs } = useSWR(
"getRuntimeLogs",
getRuntimeLogs
getRuntimeLogs,
);
const viewerRef = useRef<ProfileViewerRef>(null);
@@ -240,7 +240,7 @@ const ProfilePage = () => {
setLoadingCache((cache) => {
// 获取没有正在更新的订阅
const items = profileItems.filter(
(e) => e.type === "remote" && !cache[e.uid]
(e) => e.type === "remote" && !cache[e.uid],
);
const change = Object.fromEntries(items.map((e) => [e.uid, true]));

View File

@@ -15,7 +15,7 @@ const ProxyPage = () => {
const { data: clashConfig, mutate: mutateClash } = useSWR(
"getClashConfig",
getClashConfig
getClashConfig,
);
const { verge } = useVerge();

View File

@@ -1,6 +1,7 @@
import { useEffect, useRef } from "react";
import { useVerge } from "@/hooks/use-verge";
import { Box, Button, Grid } from "@mui/material";
import { Box, Button } from "@mui/material";
import Grid2 from "@mui/material/Grid2";
import {
DndContext,
closestCenter,
@@ -34,7 +35,7 @@ const TestPage = () => {
useSensor(PointerSensor),
useSensor(KeyboardSensor, {
coordinateGetter: sortableKeyboardCoordinates,
})
}),
);
const { verge, mutateVerge, patchVerge } = useVerge();
@@ -68,7 +69,7 @@ const TestPage = () => {
const onTestListItemChange = (
uid: string,
patch?: Partial<IVergeTestItem>
patch?: Partial<IVergeTestItem>,
) => {
if (patch) {
const newList = testList.map((x) => {
@@ -157,24 +158,28 @@ const TestPage = () => {
onDragEnd={onDragEnd}
>
<Box sx={{ mb: 4.5 }}>
<Grid container spacing={{ xs: 1, lg: 1 }}>
<Grid2 container spacing={{ xs: 1, lg: 1 }}>
<SortableContext
items={testList.map((x) => {
return x.uid;
})}
>
{testList.map((item) => (
<Grid item xs={6} sm={4} md={3} lg={2} key={item.uid}>
<Grid2
component={"div"}
size={{ xs: 6, lg: 2, sm: 4, md: 3 }}
key={item.uid}
>
<TestItem
id={item.uid}
itemData={item}
onEdit={() => viewerRef.current?.edit(item)}
onDelete={onDeleteTestListItem}
/>
</Grid>
</Grid2>
))}
</SortableContext>
</Grid>
</Grid2>
</Box>
</DndContext>
</Box>

View File

@@ -20,7 +20,7 @@ export const useConnectionSetting = () =>
{
serializer: JSON.stringify,
deserializer: JSON.parse,
}
},
);
// save the state of each profile item loading

View File

@@ -6,7 +6,7 @@ import Sockette, { type SocketteOptions } from "sockette";
export const createSockette = (
url: string,
opt: SocketteOptions,
maxError = 10
maxError = 10,
) => {
let remainRetryCount = maxError;

View File

@@ -7,7 +7,7 @@ import monacoEditorPlugin, {
type IMonacoEditorOpts,
} from "vite-plugin-monaco-editor";
const monacoEditorPluginDefault = (monacoEditorPlugin as any).default as (
options: IMonacoEditorOpts
options: IMonacoEditorOpts,
) => any;
export default defineConfig({