Compare commits

...

42 Commits

64 changed files with 885 additions and 1106 deletions

View File

@@ -1,33 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG]"
labels: bug
assignees: ""
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Information**
- OS: [e.g. macOS]
- Clash Verge Version: [e.g. 1.3.4]
- Clash Core: [e.g. Clash or Clash Meta]
**Additional context**
Add any other context about the problem here.

45
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,45 @@
name: Bug report
description: Create a report to help us improve
title: "[BUG]"
labels: ["bug"]
body:
- type: textarea
attributes:
label: Describe the bug
description: A clear and concise description of what the bug is.
validations:
required: true
- type: textarea
attributes:
label: To Reproduce
description: Steps to reproduce the behavior.
validations:
required: true
- type: dropdown
attributes:
label: Platform
options:
- Windows
- Linux
- MacOS
validations:
required: true
- type: input
attributes:
label: System Version
placeholder: "e.g. macOS 10.15.7"
validations:
required: true
- type: input
attributes:
label: Software Version
placeholder: "e.g. 1.4.3"
validations:
required: true
- type: textarea
attributes:
label: Log
description: "Log file content or screenshot"
- type: textarea
attributes:
label: Additional Information

View File

@@ -1,20 +0,0 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[Feature]"
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

View File

@@ -0,0 +1,27 @@
name: Feature request
description: Suggest an idea for this project
title: "[Feature]"
labels: ["enhancement"]
body:
- type: textarea
attributes:
label: Is your feature request related to a problem? Please describe.
description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
validations:
required: true
- type: textarea
attributes:
label: Describe the solution you'd like
description: A clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
attributes:
label: Describe alternatives you've considered
description: A clear and concise description of any alternative solutions or features you've considered.
validations:
required: true
- type: textarea
attributes:
label: Additional context
description: Add any other context or screenshots about the feature request here.

4
.github/build-for-linux/Dockerfile vendored Normal file
View File

@@ -0,0 +1,4 @@
FROM rust:buster
COPY entrypoint.sh /entrypoint.sh
RUN chmod a+x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

14
.github/build-for-linux/action.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: "Build for Linux"
branding:
icon: user-check
color: gray-dark
inputs:
target:
required: true
description: "Rust Target"
runs:
using: "docker"
image: "Dockerfile"
args:
- ${{ inputs.target }}

9
.github/build-for-linux/build.sh vendored Normal file
View File

@@ -0,0 +1,9 @@
# pnpm install --resolution-only
pnpm install
pnpm check $INPUT_TARGET
sed -i "s/#openssl/openssl={version=\"0.10\",features=[\"vendored\"]}/g" src-tauri/Cargo.toml
if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ] || [ "$INPUT_TARGET" = "i686-unknown-linux-gnu" ]; then
pnpm build --target $INPUT_TARGET
else
pnpm build --target $INPUT_TARGET -b deb
fi

47
.github/build-for-linux/entrypoint.sh vendored Normal file
View File

@@ -0,0 +1,47 @@
#!/bin/bash
wget https://nodejs.org/dist/v20.10.0/node-v20.10.0-linux-x64.tar.xz
tar -Jxvf ./node-v20.10.0-linux-x64.tar.xz
export PATH=$(pwd)/node-v20.10.0-linux-x64/bin:$PATH
npm install pnpm -g
rustup target add "$INPUT_TARGET"
if [ "$INPUT_TARGET" = "x86_64-unknown-linux-gnu" ]; then
apt-get update
apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev patchelf
elif [ "$INPUT_TARGET" = "i686-unknown-linux-gnu" ]; then
dpkg --add-architecture i386
apt-get update
apt-get install -y libstdc++6:i386 libgdk-pixbuf2.0-dev:i386 libatomic1:i386 gcc-multilib g++-multilib libwebkit2gtk-4.0-dev:i386 libssl-dev:i386 libgtk-3-dev:i386 librsvg2-dev:i386 patchelf:i386 libayatana-appindicator3-dev:i386
export PKG_CONFIG_PATH=/usr/lib/i386-linux-gnu/pkgconfig/:$PKG_CONFIG_PATH
export PKG_CONFIG_SYSROOT_DIR=/
elif [ "$INPUT_TARGET" = "aarch64-unknown-linux-gnu" ]; then
sed 's/http:\/\/\(.*\).ubuntu.com\/ubuntu\//[arch-=amd64,i386] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | tee /etc/apt/sources.list.d/ports.list
sed -i 's/http:\/\/\(.*\).ubuntu.com\/ubuntu\//[arch=amd64,i386] http:\/\/\1.archive.ubuntu.com\/ubuntu\//g' /etc/apt/sources.list
dpkg --add-architecture arm64
apt-get update
apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libssl-dev:arm64 libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libayatana-appindicator3-dev:arm64
export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc
export CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc
export CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++
export PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
elif [ "$INPUT_TARGET" = "armv7-unknown-linux-gnueabihf" ]; then
sed 's/http:\/\/\(.*\).ubuntu.com\/ubuntu\//[arch-=amd64,i386] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | tee /etc/apt/sources.list.d/ports.list
sed -i 's/http:\/\/\(.*\).ubuntu.com\/ubuntu\//[arch=amd64,i386] http:\/\/\1.archive.ubuntu.com\/ubuntu\//g' /etc/apt/sources.list
dpkg --add-architecture armhf
apt-get update
apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libssl3:armhf libcups2:armhf
apt-get install -y --no-install-recommends g++-arm-linux-gnueabihf libc6-dev-armhf-cross libssl-dev:armhf libwebkit2gtk-4.0-dev:armhf libgtk-3-dev:armhf patchelf:armhf librsvg2-dev:armhf libayatana-appindicator3-dev:armhf
export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc
export CC_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-gcc
export CXX_armv7_unknown_linux_gnueabihf=arm-linux-gnueabihf-g++
export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig
export PKG_CONFIG_ALLOW_CROSS=1
else
echo "Unknown target: $INPUT_TARGET" && exit 1
fi
bash .github/build-for-linux/build.sh

View File

@@ -1,101 +0,0 @@
name: Compatible CI
on:
workflow_dispatch:
# push:
# tags:
# - v**
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
build:
strategy:
fail-fast: false
matrix:
targets:
- tag: macOS-10.15
os: macos-10.15
- tag: Ubuntu18
os: ubuntu-18.04
- tag: Ubuntu22
os: ubuntu-22.04
runs-on: ${{ matrix.targets.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 16
# - name: Install Dependencies (ubuntu18 only)
# if: matrix.targets.os == 'ubuntu-18.04'
# run: |
# sudo apt-get update
# sudo apt-get install -y libwebkit2gtk-4.0-dev build-essential curl wget libssl-dev libgtk-3-dev libappindicator3-dev librsvg2-dev libayatana-appindicator3-dev
- name: Install Dependencies (ubuntu22 only)
if: startsWith(matrix.targets.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000
yarn run check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: ${{ matrix.targets.tag }}
releaseName: "Compatible For ${{ matrix.targets.tag }}"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
# - name: Portable Bundle
# if: matrix.os == 'windows-latest'
# # rebuild with env settings
# run: |
# yarn build
# yarn run portable
# env:
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
# TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
# VITE_WIN_PORTABLE: 1

View File

@@ -1,97 +0,0 @@
name: Dev Build
on:
workflow_dispatch:
push:
branches:
- dev
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
strategy:
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "20"
- uses: pnpm/action-setup@v2
name: Install pnpm
with:
version: 8
run_install: false
- name: Delete current release assets
if: startsWith(matrix.os, 'ubuntu-')
uses: mknejp/delete-release-assets@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: dev
fail-if-no-assets: false
fail-if-no-release: false
assets: |
*.zip
*.gz
*.AppImage
*.deb
*.dmg
*.msi
*.sig
*.exe
*.json
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf openssl
- name: Pnpm install and check
run: |
pnpm i
pnpm check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: dev
releaseName: "Clash Verge Dev"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: true
tauriScript: pnpm
args: -f default-meta
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: |
pnpm portable
env:
TAG_NAME: dev
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1

View File

@@ -1,106 +0,0 @@
name: Meta CI
on:
workflow_dispatch:
push:
tags:
- v**
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
strategy:
fail-fast: false
matrix:
os: [windows-latest, ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
uses: actions/checkout@v2
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 16
- name: Delete current release assets
if: matrix.os == 'ubuntu-latest'
uses: mknejp/delete-release-assets@v1
with:
token: ${{ secrets.GITHUB_TOKEN }}
tag: meta
fail-if-no-assets: false
fail-if-no-release: false
assets: |
*.zip
*.gz
*.AppImage
*.deb
*.dmg
*.msi
*.sig
- name: Install Dependencies (ubuntu only)
if: startsWith(matrix.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf openssl
- name: Get yarn cache dir path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn cache dir)"
- name: Yarn Cache
uses: actions/cache@v2
id: yarn-cache
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000
yarn run check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: meta
releaseName: "Clash Verge Meta"
releaseBody: ""
releaseDraft: false
prerelease: true
args: -f default-meta
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: |
yarn build -f default-meta
yarn run portable
env:
TAG_NAME: meta
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1

View File

@@ -20,30 +20,34 @@ jobs:
target: x86_64-pc-windows-msvc
- os: windows-latest
target: i686-pc-windows-msvc
# - os: windows-latest
# target: aarch64-pc-windows-msvc
- os: windows-latest
target: aarch64-pc-windows-msvc
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
# - os: ubuntu-latest
# target: armv7-unknown-linux-gnueabihf
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Apply Patch
if: matrix.target == 'aarch64-pc-windows-msvc'
run: |
git config --global user.email "clash-verge-rev@github.io"
git config --global user.name "clash-verge-rev"
git am patches/support-windows-aarch64.patch
- name: Init Submodule
if: matrix.target == 'aarch64-pc-windows-msvc'
run: git submodule update --init --recursive
- name: Install Rust Stable
uses: dtolnay/rust-toolchain@stable
- name: Add Rust Target
run: |
rustup target add ${{ matrix.target }}
run: rustup target add ${{ matrix.target }}
- name: Rust Cache
uses: Swatinem/rust-cache@v2
@@ -61,43 +65,12 @@ jobs:
version: 8
run_install: false
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'x86_64')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libayatana-appindicator3-dev librsvg2-dev patchelf openssl
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'aarch64')
run: |
sed 's/mirror+file:\/etc\/apt\/apt-mirrors.txt/[arch-=amd64] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/ports.list
sudo sed -i 's/mirror+file/[arch=amd64] mirror+file/g' /etc/apt/sources.list
cat /etc/apt/sources.list
cat /etc/apt/sources.list.d/ports.list
sudo dpkg --add-architecture arm64
sudo apt-get update
sudo apt-get install -y libncurses6:arm64 libtinfo6:arm64 linux-libc-dev:arm64 libncursesw6:arm64 libssl3:arm64 libcups2:arm64
sudo apt-get install -y --no-install-recommends g++-aarch64-linux-gnu libc6-dev-arm64-cross libssl-dev:arm64 libwebkit2gtk-4.0-dev:arm64 libgtk-3-dev:arm64 patchelf:arm64 librsvg2-dev:arm64 libayatana-appindicator3-dev:arm64
- name: Install Dependencies (Ubuntu Only)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'armv7')
run: |
sed 's/mirror+file:\/etc\/apt\/apt-mirrors.txt/[arch-=amd64] http:\/\/ports.ubuntu.com\/ubuntu-ports\//g' /etc/apt/sources.list | sudo tee /etc/apt/sources.list.d/ports.list
sudo sed -i 's/mirror+file/[arch=amd64] mirror+file/g' /etc/apt/sources.list
cat /etc/apt/sources.list
cat /etc/apt/sources.list.d/ports.list
sudo dpkg --add-architecture armhf
sudo apt-get update
sudo apt-get install -y libncurses6:armhf libtinfo6:armhf linux-libc-dev:armhf libncursesw6:armhf libssl3:armhf libcups2:armhf
sudo apt-get install -y --no-install-recommends g++-arm-linux-gnueabihf libc6-dev-armhf-cross libssl-dev:armhf libwebkit2gtk-4.0-dev:armhf libgtk-3-dev:armhf patchelf:armhf librsvg2-dev:armhf libayatana-appindicator3-dev:armhf
- name: Pnpm install and check
run: |
pnpm i
pnpm check ${{ matrix.target }}
- name: Tauri build
if: startsWith(matrix.os, 'windows') || startsWith(matrix.os,'macos') || startsWith(matrix.target,'x86_64')
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -110,60 +83,65 @@ jobs:
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: -f default-meta --target ${{ matrix.target }}
- name: Tauri build (Ubuntu Arm64)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'aarch64')
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
CC_aarch64_unknown_linux_gnu: aarch64-linux-gnu-gcc
CXX_aarch64_unknown_linux_gnu: aarch64-linux-gnu-g++
PKG_CONFIG_PATH: /usr/lib/aarch64-linux-gnu/pkgconfig
PKG_CONFIG_ALLOW_CROSS: 1
with:
tagName: v__VERSION__
releaseName: "Clash Verge v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: -f default-meta --target ${{ matrix.target }} -b deb,updater
- name: Tauri build (Ubuntu Armv7)
if: startsWith(matrix.os, 'ubuntu') && startsWith(matrix.target,'armv7')
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
CC_armv7_unknown_linux_gnueabihf: arm-linux-gnueabihf-gcc
CXX_armv7_unknown_linux_gnueabihf: arm-linux-gnueabihf-g++
PKG_CONFIG_PATH: /usr/lib/arm-linux-gnueabihf/pkgconfig
PKG_CONFIG_ALLOW_CROSS: 1
with:
tagName: v__VERSION__
releaseName: "Clash Verge v__VERSION__"
releaseBody: "More new features are now supported."
releaseDraft: false
prerelease: false
tauriScript: pnpm
args: -f default-meta --target ${{ matrix.target }} -b deb,updater
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: |
pnpm portable ${{ matrix.target }}
run: pnpm portable ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
VITE_WIN_PORTABLE: 1
release-for-linux:
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-gnu
- os: ubuntu-latest
target: i686-unknown-linux-gnu
- os: ubuntu-latest
target: aarch64-unknown-linux-gnu
# - os: ubuntu-latest
# target: armv7-unknown-linux-gnueabihf
runs-on: ${{ matrix.os }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Build for Linux
uses: ./.github/build-for-linux
env:
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
target: ${{ matrix.target }}
- name: Get Version
run: |
sudo apt-get update
sudo apt-get install jq
echo "VERSION=$(cat package.json | jq '.version' | tr -d '"')" >> $GITHUB_ENV
- name: Upload Release
if: startsWith(matrix.target, 'x86_64') || startsWith(matrix.target, 'i686')
uses: softprops/action-gh-release@v1
with:
tag_name: v${{env.VERSION}}
name: "Clash Verge v${{env.VERSION}}"
body: "More new features are now supported."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage*
- name: Upload Release
uses: softprops/action-gh-release@v1
with:
tag_name: v${{env.VERSION}}
name: "Clash Verge v${{env.VERSION}}"
body: "More new features are now supported."
token: ${{ secrets.GITHUB_TOKEN }}
files: src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb
release-update:
needs: [release]
runs-on: ubuntu-latest

View File

@@ -1,75 +0,0 @@
name: Test CI
on:
workflow_dispatch:
inputs:
os:
description: "Runs on OS"
required: true
default: windows-latest
type: choice
options:
- windows-latest
- ubuntu-latest
- macos-latest
- ubuntu-18.04
- ubuntu-20.04
- ubuntu-22.04
- macos-10.15
- macos-11
- macos-12
- windows-2019
- windows-2022
env:
CARGO_INCREMENTAL: 0
RUST_BACKTRACE: short
jobs:
release:
runs-on: ${{ github.event.inputs.os }}
steps:
- name: System Version
run: |
echo ${{ github.event.inputs.os }}
- name: Checkout repository
uses: actions/checkout@v4
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
- name: Rust Cache
uses: Swatinem/rust-cache@v2
with:
workspaces: src-tauri
- name: Install Node
uses: actions/setup-node@v4
with:
node-version: "16"
cache: "yarn"
- name: Install Dependencies (ubuntu only)
if: startsWith(github.event.inputs.os, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: Yarn install and check
run: |
yarn install --network-timeout 1000000 --frozen-lockfile
yarn run check
- name: Tauri build
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
with:
tagName: alpha
releaseName: "Clash Verge Alpha"
releaseBody: "Alpha Version (include debug)"
releaseDraft: false
includeUpdaterJson: false

View File

@@ -41,15 +41,18 @@ A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a
Download from [release](https://github.com/clash-verge-rev/clash-verge-rev/releases). Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/Clash.Verge_1.4.2_x64-setup.exe)
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/Clash.Verge_1.4.2_x86-setup.exe)
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x64-setup.exe)
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x86-setup.exe)
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_arm64-setup.exe)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/Clash.Verge_1.4.2_x64.dmg)
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/Clash.Verge_1.4.2_aarch64.dmg)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x64.dmg)
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_aarch64.dmg)
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/clash-verge_1.4.2_amd64.AppImage)
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/clash-verge_1.4.2_amd64.deb)
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.2/clash-verge_1.4.2_arm64.deb)
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_amd64.AppImage)
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_amd64.deb)
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_i386.AppImage)
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_i386.deb)
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_arm64.deb)
Or you can build it yourself. Supports Windows, Linux and macOS 10.15+
@@ -109,11 +112,10 @@ Issue and PR welcome!
Clash Verge rev was based on or inspired by these projects and so on:
- [keiko233/clash-nyanpasu](https://github.com/keiko233/clash-nyanpasu): A Clash Verge variant.
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): A Clash GUI based on tauri. Supports Windows, macOS and Linux.
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Build smaller, faster, and more secure desktop applications with a web frontend.
- [Dreamacro/clash](https://github.com/Dreamacro/clash): A rule-based tunnel in Go.
- [MetaCubeX/Clash.Meta](https://github.com/MetaCubeX/Clash.Meta): A rule-based tunnel in Go.
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): A rule-based tunnel in Go.
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): A Windows/macOS GUI based on Clash.
- [vitejs/vite](https://github.com/vitejs/vite): Next generation frontend tooling. It's fast!

View File

@@ -1,3 +1,41 @@
## v1.4.5
### Features
- 更新 MacOS 托盘图标样式(@gxx2778 贡献)
### Bugs Fixes
- Windows 下更新时无法覆盖`clash-verge-service.exe`的问题(需要卸载重装一次服务,下次更新生效)
- 窗口最大化按钮变化问题
- 窗口尺寸保存错误问题
- 复制环境变量类型无法切换问题
- 某些情况下闪退的问题
- 某些订阅无法导入的问题
---
## v1.4.4
### Features
- 支持 Windows aarch64(arm64) 版本
- 支持一键更新 GeoData
- 支持一键更新 Alpha 内核
- MacOS 支持在系统代理时显示不同的托盘图标
- Linux 支持在系统代理时显示不同的托盘图标
- 优化复制环境变量逻辑
### Bugs Fixes
- 修改 PID 文件的路径
### Performance
- 优化创建窗口的速度
---
## v1.4.3
### Break Changes

View File

@@ -1,19 +1,17 @@
{
"name": "clash-verge",
"version": "1.4.3",
"version": "1.4.5",
"license": "GPL-3.0",
"scripts": {
"dev": "tauri dev -f default-meta",
"dev": "tauri dev",
"dev:diff": "tauri dev -f verge-dev",
"build": "tauri build -f default-meta",
"build": "tauri build",
"tauri": "tauri",
"web:dev": "vite",
"web:build": "tsc && vite build",
"web:serve": "vite preview",
"aarch": "node scripts/aarch.mjs",
"check": "node scripts/check.mjs",
"updater": "node scripts/updater.mjs",
"publish": "node scripts/publish.mjs",
"portable": "node scripts/portable.mjs",
"prepare": "husky install"
},

View File

@@ -0,0 +1,195 @@
From 8b085aea2f11e64f433244eda092c178a2bb50bc Mon Sep 17 00:00:00 2001
From: MystiPanda <mystipanda@proton.me>
Date: Sun, 10 Dec 2023 19:47:45 +0800
Subject: [PATCH] feat: Support windows aarch64
---
.gitmodules | 3 +
src-tauri/Cargo.toml | 2 +-
src-tauri/quick-rs | 1 +
src-tauri/src/enhance/script.rs | 130 +++++++++++++++++++-------------
4 files changed, 81 insertions(+), 55 deletions(-)
create mode 100644 .gitmodules
create mode 160000 src-tauri/quick-rs
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..2eda7e4
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "src-tauri/quick-rs"]
+ path = src-tauri/quick-rs
+ url = https://github.com/clash-verge-rev/quick-rs.git
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 4c6dde5..5fd9ad8 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -25,7 +25,6 @@ log4rs = "1"
nanoid = "0.4"
chrono = "0.4"
sysinfo = "0.29"
-rquickjs = "0.3" # 高版本不支持 Linux aarch64
serde_json = "1.0"
serde_yaml = "0.9"
auto-launch = "0.5"
@@ -34,6 +33,7 @@ port_scanner = "0.1.5"
delay_timer = "0.11"
parking_lot = "0.12"
percent-encoding = "2.3.1"
+quick-rs = { path = "quick-rs" }
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
diff --git a/src-tauri/quick-rs b/src-tauri/quick-rs
new file mode 160000
index 0000000..78277c4
--- /dev/null
+++ b/src-tauri/quick-rs
@@ -0,0 +1 @@
+Subproject commit 78277c4509c64f18c0fc5c9f2b84671de7c83343
diff --git a/src-tauri/src/enhance/script.rs b/src-tauri/src/enhance/script.rs
index 6c207d9..d47dc33 100644
--- a/src-tauri/src/enhance/script.rs
+++ b/src-tauri/src/enhance/script.rs
@@ -3,61 +3,83 @@ use anyhow::Result;
use serde_yaml::Mapping;
pub fn use_script(script: String, config: Mapping) -> Result<(Mapping, Vec<(String, String)>)> {
- use rquickjs::{function::Func, Context, Runtime};
- use std::sync::{Arc, Mutex};
-
- let runtime = Runtime::new().unwrap();
- let context = Context::full(&runtime).unwrap();
- let outputs = Arc::new(Mutex::new(vec![]));
-
- let copy_outputs = outputs.clone();
- let result = context.with(|ctx| -> Result<Mapping> {
- ctx.globals().set(
- "__verge_log__",
- Func::from(move |level: String, data: String| {
- let mut out = copy_outputs.lock().unwrap();
- out.push((level, data));
- }),
- )?;
-
- ctx.eval(
- r#"var console = Object.freeze({
- log(data){__verge_log__("log",JSON.stringify(data))},
- info(data){__verge_log__("info",JSON.stringify(data))},
- error(data){__verge_log__("error",JSON.stringify(data))},
- debug(data){__verge_log__("debug",JSON.stringify(data))},
- });"#,
- )?;
-
- let config = use_lowercase(config.clone());
- let config_str = serde_json::to_string(&config)?;
-
- let code = format!(
- r#"try{{
+ use quick_rs::{context::Context, function::Function, module::Module, runtime::Runtime};
+
+ let config = use_lowercase(config.clone());
+ let config_str = serde_json::to_string(&config)?;
+
+ let runtime = Runtime::new();
+ let context = Context::from(&runtime);
+
+ let code = format!(
+ r#"
+ let output = [];
+
+ function __verge_log__(type, data) {{
+ output.push([type, data]);
+ }}
+
+ var console = Object.freeze({{
+ log(data) {{ __verge_log__("log", JSON.stringify(data)) }},
+ info(data) {{ __verge_log__("info", JSON.stringify(data)) }},
+ error(data) {{ __verge_log__("error", JSON.stringify(data)) }},
+ debug(data) {{ __verge_log__("debug", JSON.stringify(data)) }},
+ }});
+
{script};
- JSON.stringify(main({config_str})||'')
- }} catch(err) {{
- `__error_flag__ ${{err.toString()}}`
- }}"#
- );
- let result: String = ctx.eval(code.as_str())?;
- if result.starts_with("__error_flag__") {
- anyhow::bail!(result[15..].to_owned());
- }
- if result == "\"\"" {
- anyhow::bail!("main function should return object");
- }
- return Ok(serde_json::from_str::<Mapping>(result.as_str())?);
- });
-
- let mut out = outputs.lock().unwrap();
- match result {
- Ok(config) => Ok((use_lowercase(config), out.to_vec())),
- Err(err) => {
- out.push(("exception".into(), err.to_string()));
- Ok((config, out.to_vec()))
- }
- }
+
+ export function _main(){{
+ try{{
+ let result = JSON.stringify(main({config_str})||"");
+ return JSON.stringify({{result, output}});
+ }} catch(err) {{
+ output.push(["exception", err.toString()]);
+ return JSON.stringify({{result: "__error__", output}});
+ }}
+ }}
+ "#
+ );
+ let value = context.eval_module(&code, "_main")?;
+ let module = Module::new(value)?;
+ let value = module.get("_main")?;
+ let function = Function::new(value)?;
+ let value = function.call(vec![])?;
+ let result = serde_json::from_str::<serde_json::Value>(&value.to_string()?)?;
+ result
+ .as_object()
+ .map(|obj| {
+ let result = obj.get("result").unwrap().as_str().unwrap();
+ let output = obj.get("output").unwrap();
+
+ let mut out = output
+ .as_array()
+ .unwrap()
+ .iter()
+ .map(|item| {
+ let item = item.as_array().unwrap();
+ (
+ item[0].as_str().unwrap().into(),
+ item[1].as_str().unwrap().into(),
+ )
+ })
+ .collect::<Vec<_>>();
+ if result.is_empty() {
+ anyhow::bail!("main function should return object");
+ }
+ if result == "__error__" {
+ return Ok((config, out.to_vec()));
+ }
+ let result = serde_json::from_str::<Mapping>(result);
+
+ match result {
+ Ok(config) => Ok((use_lowercase(config), out.to_vec())),
+ Err(err) => {
+ out.push(("exception".into(), err.to_string()));
+ Ok((config, out.to_vec()))
+ }
+ }
+ })
+ .unwrap_or_else(|| anyhow::bail!("Unknown result"))
}
#[test]
--
2.43.0.windows.1

View File

@@ -1,119 +0,0 @@
/**
* Build and upload assets
* for macOS(aarch)
*/
import fs from "fs-extra";
import path from "path";
import { exit } from "process";
import { execSync } from "child_process";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
// to `meta` tag
const META = process.argv.includes("--meta");
// to `alpha` tag
const ALPHA = process.argv.includes("--alpha");
const require = createRequire(import.meta.url);
async function resolve() {
if (!process.env.GITHUB_TOKEN) {
throw new Error("GITHUB_TOKEN is required");
}
if (!process.env.GITHUB_REPOSITORY) {
throw new Error("GITHUB_REPOSITORY is required");
}
if (!process.env.TAURI_PRIVATE_KEY) {
throw new Error("TAURI_PRIVATE_KEY is required");
}
if (!process.env.TAURI_KEY_PASSWORD) {
throw new Error("TAURI_KEY_PASSWORD is required");
}
const { version } = require("../package.json");
const tag = META ? "meta" : ALPHA ? "alpha" : `v${version}`;
const buildCmd = META ? `pnpm build -f default-meta` : `pnpm build`;
console.log(`[INFO]: Upload to tag "${tag}"`);
console.log(`[INFO]: Building app. "${buildCmd}"`);
execSync(buildCmd);
const cwd = process.cwd();
const bundlePath = path.join(cwd, "src-tauri/target/release/bundle");
const join = (p) => path.join(bundlePath, p);
const appPathList = [
join("macos/Clash Verge.aarch64.app.tar.gz"),
join("macos/Clash Verge.aarch64.app.tar.gz.sig"),
];
for (const appPath of appPathList) {
if (fs.pathExistsSync(appPath)) {
fs.removeSync(appPath);
}
}
fs.copyFileSync(join("macos/Clash Verge.app.tar.gz"), appPathList[0]);
fs.copyFileSync(join("macos/Clash Verge.app.tar.gz.sig"), appPathList[1]);
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag,
});
if (!release.id) throw new Error("failed to find the release");
await uploadAssets(release.id, [
join(`dmg/Clash Verge_${version}_aarch64.dmg`),
...appPathList,
]);
}
// From tauri-apps/tauri-action
// https://github.com/tauri-apps/tauri-action/blob/dev/packages/action/src/upload-release-assets.ts
async function uploadAssets(releaseId, assets) {
const github = getOctokit(process.env.GITHUB_TOKEN);
// Determine content-length for header to upload asset
const contentLength = (filePath) => fs.statSync(filePath).size;
for (const assetPath of assets) {
const headers = {
"content-type": "application/zip",
"content-length": contentLength(assetPath),
};
const ext = path.extname(assetPath);
const filename = path.basename(assetPath).replace(ext, "");
const assetName = path.dirname(assetPath).includes(`target${path.sep}debug`)
? `${filename}-debug${ext}`
: `${filename}${ext}`;
console.log(`[INFO]: Uploading ${assetName}...`);
try {
await github.rest.repos.uploadReleaseAsset({
headers,
name: assetName,
data: fs.readFileSync(assetPath),
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
});
} catch (error) {
console.log(error.message);
}
}
}
if (process.platform === "darwin" && process.arch === "arm64") {
resolve();
} else {
console.error("invalid");
exit(1);
}

View File

@@ -18,6 +18,7 @@ const PLATFORM_MAP = {
"x86_64-apple-darwin": "darwin",
"aarch64-apple-darwin": "darwin",
"x86_64-unknown-linux-gnu": "linux",
"i686-unknown-linux-gnu": "linux",
"aarch64-unknown-linux-gnu": "linux",
"armv7-unknown-linux-gnueabihf": "linux",
};
@@ -28,6 +29,7 @@ const ARCH_MAP = {
"x86_64-apple-darwin": "x64",
"aarch64-apple-darwin": "arm64",
"x86_64-unknown-linux-gnu": "x64",
"i686-unknown-linux-gnu": "ia32",
"aarch64-unknown-linux-gnu": "arm64",
"armv7-unknown-linux-gnueabihf": "arm",
};
@@ -58,6 +60,7 @@ const META_ALPHA_MAP = {
"darwin-x64": "mihomo-darwin-amd64",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
"linux-arm": "mihomo-linux-armv7",
};
@@ -86,6 +89,7 @@ const META_MAP = {
"darwin-x64": "mihomo-darwin-amd64",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
"linux-arm64": "mihomo-linux-arm64",
"linux-arm": "mihomo-linux-armv7",
};

View File

@@ -9,6 +9,7 @@ const target = process.argv.slice(2)[0];
const ARCH_MAP = {
"i686-pc-windows-msvc": "x86",
"x86_64-pc-windows-msvc": "x64",
"aarch64-pc-windows-msvc": "arm64",
};
/// Script for ci
@@ -31,7 +32,6 @@ async function resolvePortable() {
const zip = new AdmZip();
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
// zip.addLocalFile(path.join(releaseDir, "clash.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-meta-alpha.exe"));
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");

View File

@@ -1,53 +0,0 @@
import fs from "fs-extra";
import { createRequire } from "module";
import { execSync } from "child_process";
import { resolveUpdateLog } from "./updatelog.mjs";
const require = createRequire(import.meta.url);
// publish
async function resolvePublish() {
const flag = process.argv[2] ?? "patch";
const packageJson = require("../package.json");
const tauriJson = require("../src-tauri/tauri.conf.json");
let [a, b, c] = packageJson.version.split(".").map(Number);
if (flag === "major") {
a += 1;
b = 0;
c = 0;
} else if (flag === "minor") {
b += 1;
c = 0;
} else if (flag === "patch") {
c += 1;
} else throw new Error(`invalid flag "${flag}"`);
const nextVersion = `${a}.${b}.${c}`;
packageJson.version = nextVersion;
tauriJson.package.version = nextVersion;
// 发布更新前先写更新日志
const nextTag = `v${nextVersion}`;
await resolveUpdateLog(nextTag);
await fs.writeFile(
"./package.json",
JSON.stringify(packageJson, undefined, 2)
);
await fs.writeFile(
"./src-tauri/tauri.conf.json",
JSON.stringify(tauriJson, undefined, 2)
);
execSync("git add ./package.json");
execSync("git add ./src-tauri/tauri.conf.json");
execSync(`git commit -m "v${nextVersion}"`);
execSync(`git tag -a v${nextVersion} -m "v${nextVersion}"`);
execSync(`git push`);
execSync(`git push origin v${nextVersion}`);
console.log(`Publish Successfully...`);
}
resolvePublish();

View File

@@ -45,9 +45,12 @@ async function resolveUpdater() {
"darwin-intel": { signature: "", url: "" },
"darwin-x86_64": { signature: "", url: "" },
"linux-x86_64": { signature: "", url: "" },
"linux-i686": { signature: "", url: "" },
"linux-aarch64": { signature: "", url: "" },
"linux-armv7": { signature: "", url: "" },
"windows-x86_64": { signature: "", url: "" },
"windows-i686": { signature: "", url: "" },
"windows-aarch64": { signature: "", url: "" },
},
};
@@ -76,6 +79,16 @@ async function resolveUpdater() {
updateData.platforms["windows-i686"].signature = sig;
}
// win arm url
if (name.endsWith("arm64-setup.nsis.zip")) {
updateData.platforms["windows-aarch64"].url = browser_download_url;
}
// win arm signature
if (name.endsWith("arm64-setup.nsis.zip.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["windows-aarch64"].signature = sig;
}
// darwin url (intel)
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
updateData.platforms.darwin.url = browser_download_url;
@@ -100,22 +113,32 @@ async function resolveUpdater() {
updateData.platforms["darwin-aarch64"].signature = sig;
}
// linux url
if (name.endsWith(".AppImage.tar.gz")) {
// linux x64 url
if (name.endsWith("amd64.AppImage.tar.gz")) {
updateData.platforms.linux.url = browser_download_url;
updateData.platforms["linux-x86_64"].url = browser_download_url;
// 暂时使用x64版本的url和sig使得可以检查更新但aarch64版本还不支持构建appimage
updateData.platforms["linux-aarch64"].url = browser_download_url;
// updateData.platforms["linux-armv7"].url = browser_download_url;
updateData.platforms["linux-armv7"].url = browser_download_url;
}
// linux signature
if (name.endsWith(".AppImage.tar.gz.sig")) {
// linux x64 signature
if (name.endsWith("amd64.AppImage.tar.gz.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms.linux.signature = sig;
updateData.platforms["linux-x86_64"].signature = sig;
// 暂时使用x64版本的url和sig使得可以检查更新但aarch64版本还不支持构建appimage
updateData.platforms["linux-aarch64"].signature = sig;
// updateData.platforms["linux-armv7"].signature = sig;
updateData.platforms["linux-armv7"].signature = sig;
}
// linux x86 url
if (name.endsWith("i386.AppImage.tar.gz")) {
updateData.platforms["linux-i686"].url = browser_download_url;
}
// linux x86 signature
if (name.endsWith("i386.AppImage.tar.gz.sig")) {
const sig = await getSignature(browser_download_url);
updateData.platforms["linux-i686"].signature = sig;
}
});

30
src-tauri/Cargo.lock generated
View File

@@ -559,7 +559,7 @@ dependencies = [
[[package]]
name = "clash-verge"
version = "1.4.3"
version = "1.4.5"
dependencies = [
"anyhow",
"auto-launch",
@@ -591,7 +591,6 @@ dependencies = [
"warp",
"which 5.0.0",
"window-shadows",
"window-vibrancy",
"windows-sys 0.52.0",
"winreg 0.52.0",
]
@@ -2917,15 +2916,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-src"
version = "300.1.6+3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085"
dependencies = [
"cc",
]
[[package]]
name = "openssl-sys"
version = "0.9.96"
@@ -2934,7 +2924,6 @@ checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f"
dependencies = [
"cc",
"libc",
"openssl-src",
"pkg-config",
"vcpkg",
]
@@ -3683,9 +3672,9 @@ dependencies = [
[[package]]
name = "ring"
version = "0.17.6"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "684d5e6e18f669ccebf64a92236bb7db9a34f07be010e3627368182027180866"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom 0.2.11",
@@ -5653,19 +5642,6 @@ dependencies = [
"windows-sys 0.48.0",
]
[[package]]
name = "window-vibrancy"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af6abc2b9c56bd95887825a1ce56cde49a2a97c07e28db465d541f5098a2656c"
dependencies = [
"cocoa 0.25.0",
"objc",
"raw-window-handle",
"windows-sys 0.52.0",
"windows-version",
]
[[package]]
name = "windows"
version = "0.37.0"

View File

@@ -1,8 +1,8 @@
[package]
name = "clash-verge"
version = "1.4.3"
version = "1.4.5"
description = "clash verge"
authors = ["zzzgydi"]
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0"
repository = "https://github.com/clash-verge-rev/clash-verge-rev.git"
default-run = "clash-verge"
@@ -25,8 +25,7 @@ log4rs = "1"
nanoid = "0.4"
chrono = "0.4"
sysinfo = "0.29"
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
rquickjs = "0.3"
rquickjs = "0.3" # 高版本不支持 Linux aarch64
serde_json = "1.0"
serde_yaml = "0.9"
auto-launch = "0.5"
@@ -34,33 +33,27 @@ once_cell = "1.18"
port_scanner = "0.1.5"
delay_timer = "0.11"
parking_lot = "0.12"
percent-encoding = "2.3.1"
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }
tauri = { version = "1.5", features = ["clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
window-vibrancy = { version = "0.4.3" }
window-shadows = { version = "0.2" }
percent-encoding = "2.3.1"
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
tauri = { version = "1.5", features = ["icon-png", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
[target.'cfg(windows)'.dependencies]
runas = "=1.0.0"
runas = "=1.0.0" # 高版本会返回错误 Status
deelevate = "0.2.0"
winreg = { version = "0.52", features = ["transactions"] }
windows-sys = { version = "0.52", features = ["Win32_System_LibraryLoader", "Win32_System_SystemInformation"] }
[target.'cfg(windows)'.dependencies.tauri]
features = ["global-shortcut-all", "icon-png", "process-all", "shell-all", "system-tray", "updater", "window-all"]
[target.'cfg(linux)'.dependencies.tauri]
features = ["global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "native-tls-vendored", "reqwest-native-tls-vendored"]
[target.'cfg(target_os = "linux")'.dependencies]
#openssl
[features]
default = ["custom-protocol"]
custom-protocol = ["tauri/custom-protocol"]
verge-dev = []
default-meta = []
[profile.release]
panic = "abort"

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -24,29 +24,12 @@ impl IClashTemp {
pub fn template() -> Self {
let mut map = Mapping::new();
map.insert(
"mixed-port".into(),
match cfg!(feature = "default-meta") {
false => 7890.into(),
true => 7898.into(),
},
);
map.insert("mixed-port".into(), 7897.into());
map.insert("log-level".into(), "info".into());
map.insert("allow-lan".into(), false.into());
map.insert("mode".into(), "rule".into());
map.insert(
"external-controller".into(),
match cfg!(feature = "default-meta") {
false => "127.0.0.1:9090".into(),
true => "127.0.0.1:9098".into(),
},
);
map.insert("external-controller".into(), "127.0.0.1:9097".into());
map.insert("secret".into(), "".into());
#[cfg(feature = "default-meta")]
map.insert("unified-delay".into(), true.into());
#[cfg(feature = "default-meta")]
map.insert("tcp-concurrent".into(), true.into());
// map.insert("ipv6".into(), false.into());
Self(map)
}
@@ -101,9 +84,9 @@ impl IClashTemp {
Value::Number(val_num) => val_num.as_u64().map(|u| u as u16),
_ => None,
})
.unwrap_or(7890);
.unwrap_or(7897);
if port == 0 {
port = 7890;
port = 7897;
}
port
}
@@ -126,7 +109,7 @@ impl IClashTemp {
}
None => None,
})
.unwrap_or("127.0.0.1:9090".into())
.unwrap_or("127.0.0.1:9097".into())
}
pub fn guard_client_ctrl(config: &Mapping) -> String {
@@ -138,7 +121,7 @@ impl IClashTemp {
}
socket.to_string()
}
Err(_) => "127.0.0.1:9090".into(),
Err(_) => "127.0.0.1:9097".into(),
}
}
}
@@ -173,12 +156,12 @@ fn test_clash_info() {
assert_eq!(
IClashTemp(IClashTemp::guard(Mapping::new())).get_client_info(),
get_result(7890, "127.0.0.1:9090")
get_result(7897, "127.0.0.1:9097")
);
assert_eq!(get_case("", ""), get_result(7890, "127.0.0.1:9090"));
assert_eq!(get_case("", ""), get_result(7897, "127.0.0.1:9097"));
assert_eq!(get_case(65537, ""), get_result(1, "127.0.0.1:9090"));
assert_eq!(get_case(65537, ""), get_result(1, "127.0.0.1:9097"));
assert_eq!(
get_case(8888, "127.0.0.1:8888"),
@@ -187,7 +170,7 @@ fn test_clash_info() {
assert_eq!(
get_case(8888, " :98888 "),
get_result(8888, "127.0.0.1:9090")
get_result(8888, "127.0.0.1:9097")
);
assert_eq!(
@@ -212,7 +195,7 @@ fn test_clash_info() {
assert_eq!(
get_case(8888, "192.168.1.1:80800"),
get_result(8888, "127.0.0.1:9090")
get_result(8888, "127.0.0.1:9097")
);
}

View File

@@ -231,8 +231,7 @@ impl PrfItem {
};
}
let version = unsafe { dirs::APP_VERSION };
let version = format!("clash-verge/{version}");
let version = format!("clash-verge-rev");
builder = builder.user_agent(user_agent.unwrap_or(version));
let resp = builder.build()?.get(url).send().await?;

View File

@@ -26,6 +26,9 @@ pub struct IVerge {
/// tray click event
pub tray_event: Option<String>,
/// copy env type
pub env_type: Option<String>,
/// enable traffic graph default is true
pub traffic_graph: Option<bool>,
@@ -130,15 +133,13 @@ impl IVerge {
pub fn template() -> Self {
Self {
clash_core: match cfg!(feature = "default-meta") {
false => Some("clash".into()),
true => Some("clash-meta".into()),
},
language: match cfg!(feature = "default-meta") {
false => Some("en".into()),
true => Some("zh".into()),
},
clash_core: Some("clash-meta".into()),
language: Some("zh".into()),
theme_mode: Some("system".into()),
#[cfg(not(target_os = "windows"))]
env_type: Some("bash".into()),
#[cfg(target_os = "windows")]
env_type: Some("powershell".into()),
theme_blur: Some(false),
traffic_graph: Some(true),
enable_memory_usage: Some(true),
@@ -146,12 +147,12 @@ impl IVerge {
enable_silent_start: Some(false),
enable_system_proxy: Some(false),
enable_random_port: Some(false),
verge_mixed_port: Some(7890),
verge_mixed_port: Some(7897),
enable_proxy_guard: Some(false),
proxy_guard_duration: Some(30),
auto_close_connection: Some(true),
enable_builtin_enhanced: Some(true),
enable_clash_fields: Some(false),
enable_clash_fields: Some(true),
auto_log_clean: Some(3),
..Self::default()
}
@@ -178,6 +179,7 @@ impl IVerge {
patch!(theme_mode);
patch!(theme_blur);
patch!(tray_event);
patch!(env_type);
patch!(traffic_graph);
patch!(enable_memory_usage);

View File

@@ -1,5 +1,3 @@
use std::borrow::Cow;
/// 给clash内核的tun模式授权
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn grant_permission(core: String) -> anyhow::Result<()> {
@@ -13,9 +11,6 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
#[cfg(target_os = "macos")]
let output = {
// the path of clash /Applications/Clash Verge.app/Contents/MacOS/clash
// https://apple.stackexchange.com/questions/82967/problem-with-empty-spaces-when-executing-shell-commands-in-applescript
// let path = escape(&path);
let path = path.replace(' ', "\\\\ ");
let shell = format!("chown root:admin {path}\nchmod +sx {path}");
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
@@ -50,33 +45,3 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
anyhow::bail!("{stderr}");
}
}
#[allow(unused)]
pub fn escape<'a>(text: &'a str) -> Cow<'a, str> {
let bytes = text.as_bytes();
let mut owned = None;
for pos in 0..bytes.len() {
let special = match bytes[pos] {
b' ' => Some(b' '),
_ => None,
};
if let Some(s) = special {
if owned.is_none() {
owned = Some(bytes[0..pos].to_owned());
}
owned.as_mut().unwrap().push(b'\\');
owned.as_mut().unwrap().push(b'\\');
owned.as_mut().unwrap().push(s);
} else if let Some(owned) = owned.as_mut() {
owned.push(bytes[pos]);
}
}
if let Some(owned) = owned {
unsafe { Cow::Owned(String::from_utf8_unchecked(owned)) }
} else {
unsafe { Cow::Borrowed(std::str::from_utf8_unchecked(bytes)) }
}
}

View File

@@ -41,10 +41,6 @@ impl Tray {
"direct_mode",
t!("Direct Mode", "直连模式"),
))
.add_item(CustomMenuItem::new(
"script_mode",
t!("Script Mode", "脚本模式"),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"system_proxy",
@@ -52,16 +48,8 @@ impl Tray {
))
.add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
.add_item(CustomMenuItem::new(
"copy_env_sh",
t!("Copy Env (sh)", "复制环境变量(sh)"),
))
.add_item(CustomMenuItem::new(
"copy_env_cmd",
t!("Copy Env (CMD)", "复制环境变量(CMD)"),
))
.add_item(CustomMenuItem::new(
"copy_env_ps",
t!("Copy Env (PS)", "复制环境变量(PS)"),
"copy_env",
t!("Copy Env", "复制环境变量"),
))
.add_submenu(SystemTraySubmenu::new(
t!("Open Dir", "打开目录"),
@@ -136,28 +124,36 @@ impl Tray {
let _ = tray.get_item("rule_mode").set_selected(mode == "rule");
let _ = tray.get_item("global_mode").set_selected(mode == "global");
let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
let _ = tray.get_item("script_mode").set_selected(mode == "script");
let verge = Config::verge();
let verge = verge.latest();
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
#[cfg(target_os = "windows")]
{
let mut indication_icon = if *system_proxy {
include_bytes!("../../icons/win-tray-icon-activated.png").to_vec()
} else {
include_bytes!("../../icons/win-tray-icon.png").to_vec()
};
let mut indication_icon = if *system_proxy {
#[cfg(not(target_os = "macos"))]
let icon = include_bytes!("../../icons/tray-icon-sys.png").to_vec();
#[cfg(target_os = "macos")]
let icon = include_bytes!("../../icons/mac-tray-icon-sys.png").to_vec();
icon
} else {
#[cfg(not(target_os = "macos"))]
let icon = include_bytes!("../../icons/tray-icon.png").to_vec();
#[cfg(target_os = "macos")]
let icon = include_bytes!("../../icons/mac-tray-icon.png").to_vec();
icon
};
if *tun_mode {
indication_icon = include_bytes!("../../icons/win-tray-icon-tun.png").to_vec();
}
let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
if *tun_mode {
#[cfg(not(target_os = "macos"))]
let icon = include_bytes!("../../icons/tray-icon-tun.png").to_vec();
#[cfg(target_os = "macos")]
let icon = include_bytes!("../../icons/mac-tray-icon-tun.png").to_vec();
indication_icon = icon
}
let _ = tray.set_icon(tauri::Icon::Raw(indication_icon));
let _ = tray.get_item("system_proxy").set_selected(*system_proxy);
let _ = tray.get_item("tun_mode").set_selected(*tun_mode);
@@ -168,7 +164,6 @@ impl Tray {
map
};
#[cfg(not(target_os = "linux"))]
let _ = tray.set_tooltip(&format!(
"Clash Verge {version}\n{}: {}\n{}: {}",
t!("System Proxy", "系统代理"),
@@ -193,22 +188,16 @@ impl Tray {
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
match event {
#[cfg(not(target_os = "linux"))]
SystemTrayEvent::LeftClick { .. } => Tray::on_left_click(app_handle),
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
mode @ ("rule_mode" | "global_mode" | "direct_mode" | "script_mode") => {
mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
let mode = &mode[0..mode.len() - 5];
feat::change_clash_mode(mode.into());
}
"open_window" => resolve::create_window(app_handle),
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(),
"copy_env_sh" => feat::copy_clash_env(app_handle, "sh"),
#[cfg(target_os = "windows")]
"copy_env_cmd" => feat::copy_clash_env(app_handle, "cmd"),
#[cfg(target_os = "windows")]
"copy_env_ps" => feat::copy_clash_env(app_handle, "ps"),
"copy_env" => feat::copy_clash_env(app_handle),
"open_app_dir" => crate::log_err!(cmds::open_app_dir()),
"open_core_dir" => crate::log_err!(cmds::open_core_dir()),
"open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),

View File

@@ -7,8 +7,8 @@ use runas::Command as RunasCommand;
use std::process::Command as StdCommand;
pub async fn invoke_uwptools() -> Result<()> {
let binary_path = dirs::service_path()?;
let tool_path = binary_path.with_file_name("enableLoopback.exe");
let resource_dir = dirs::app_resources_dir()?;
let tool_path = resource_dir.join("enableLoopback.exe");
if !tool_path.exists() {
bail!("enableLoopback exe not found");
@@ -17,10 +17,9 @@ pub async fn invoke_uwptools() -> Result<()> {
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
match level {
match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(tool_path).status()?,
_ => StdCommand::new(tool_path)
.status()?,
_ => StdCommand::new(tool_path).status()?,
};
Ok(())

View File

@@ -337,8 +337,8 @@ async fn update_core_config() -> Result<()> {
}
/// copy env variable
pub fn copy_clash_env(app_handle: &AppHandle, option: &str) {
let port = { Config::verge().latest().verge_mixed_port.unwrap_or(7890) };
pub fn copy_clash_env(app_handle: &AppHandle) {
let port = { Config::verge().latest().verge_mixed_port.unwrap_or(7897) };
let http_proxy = format!("http://127.0.0.1:{}", port);
let socks5_proxy = format!("socks5://127.0.0.1:{}", port);
@@ -346,12 +346,25 @@ pub fn copy_clash_env(app_handle: &AppHandle, option: &str) {
format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
let cmd: String = format!("set http_proxy={http_proxy} \n set https_proxy={http_proxy}");
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
let mut cliboard = app_handle.clipboard_manager();
match option {
"sh" => cliboard.write_text(sh).unwrap_or_default(),
let env_type = { Config::verge().latest().env_type.clone() };
let env_type = match env_type {
Some(env_type) => env_type,
None => {
#[cfg(not(target_os = "windows"))]
let default = "bash";
#[cfg(target_os = "windows")]
let default = "powershell";
default.to_string()
}
};
match env_type.as_str() {
"bash" => cliboard.write_text(sh).unwrap_or_default(),
"cmd" => cliboard.write_text(cmd).unwrap_or_default(),
"ps" => cliboard.write_text(ps).unwrap_or_default(),
_ => log::error!(target: "app", "copy_clash_env: Invalid option! {option}"),
"powershell" => cliboard.write_text(ps).unwrap_or_default(),
_ => log::error!(target: "app", "copy_clash_env: Invalid env type! {env_type}"),
};
}

View File

@@ -131,6 +131,9 @@ fn main() -> std::io::Result<()> {
tauri::RunEvent::WindowEvent { label, event, .. } => {
if label == "main" {
match event {
tauri::WindowEvent::Destroyed => {
let _ = resolve::save_window_size_position(&app_handle, true);
}
tauri::WindowEvent::CloseRequested { .. } => {
let _ = resolve::save_window_size_position(&app_handle, true);
}

View File

@@ -1,8 +1,9 @@
use crate::core::handle;
use anyhow::Result;
use std::path::PathBuf;
use tauri::{
api::path::{data_dir, resource_dir},
Env, PackageInfo,
Env,
};
#[cfg(not(feature = "verge-dev"))]
@@ -14,73 +15,38 @@ static CLASH_CONFIG: &str = "config.yaml";
static VERGE_CONFIG: &str = "verge.yaml";
static PROFILE_YAML: &str = "profiles.yaml";
static mut RESOURCE_DIR: Option<PathBuf> = None;
/// portable flag
#[allow(unused)]
static mut PORTABLE_FLAG: bool = false;
pub static mut APP_VERSION: &str = "v1.2.0";
/// initialize portable flag
#[cfg(target_os = "windows")]
pub unsafe fn init_portable_flag() -> Result<()> {
/// get the verge app home dir
pub fn app_home_dir() -> Result<PathBuf> {
use tauri::utils::platform::current_exe;
let exe = current_exe()?;
if let Some(dir) = exe.parent() {
let app_exe = current_exe()?;
if let Some(dir) = app_exe.parent() {
let dir = PathBuf::from(dir).join(".config/PORTABLE");
if dir.exists() {
PORTABLE_FLAG = true;
}
}
Ok(())
}
/// get the verge app home dir
pub fn app_home_dir() -> Result<PathBuf> {
#[cfg(target_os = "windows")]
unsafe {
use tauri::utils::platform::current_exe;
if !PORTABLE_FLAG {
Ok(data_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(APP_ID))
} else {
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
Ok(PathBuf::from(app_dir).join(".config").join(APP_ID))
return Ok(PathBuf::from(app_dir).join(".config").join(APP_ID));
}
}
#[cfg(not(target_os = "windows"))]
Ok(data_dir()
.ok_or(anyhow::anyhow!("failed to get the app home dir"))?
.join("io.github.clash_verge_rev.clash_verge_rev"))
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(APP_ID))
}
/// get the resources dir
pub fn app_resources_dir(package_info: &PackageInfo) -> Result<PathBuf> {
let res_dir = resource_dir(package_info, &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
unsafe {
RESOURCE_DIR = Some(res_dir.clone());
let ver = package_info.version.to_string();
let ver_str = format!("v{ver}");
APP_VERSION = Box::leak(Box::new(ver_str));
}
Ok(res_dir)
pub fn app_resources_dir() -> Result<PathBuf> {
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() {
let res_dir = resource_dir(app_handle.package_info(), &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
return Ok(res_dir);
};
Err(anyhow::anyhow!("failed to get the resource dir"))
}
/// profiles dir
@@ -105,32 +71,18 @@ pub fn profiles_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join(PROFILE_YAML))
}
#[allow(unused)]
pub fn app_res_dir() -> Result<PathBuf> {
unsafe {
Ok(RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?)
}
pub fn clash_pid_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join("clash.pid"))
}
pub fn clash_pid_path() -> Result<PathBuf> {
unsafe {
Ok(RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("clash.pid"))
}
#[cfg(windows)]
pub fn service_dir() -> Result<PathBuf> {
Ok(app_home_dir()?.join("service"))
}
#[cfg(windows)]
pub fn service_path() -> Result<PathBuf> {
unsafe {
let res_dir = RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?;
Ok(res_dir.join("clash-verge-service.exe"))
}
Ok(service_dir()?.join("clash-verge-service.exe"))
}
#[cfg(windows)]

View File

@@ -9,7 +9,6 @@ use log4rs::config::{Appender, Logger, Root};
use log4rs::encode::pattern::PatternEncoder;
use std::fs::{self, DirEntry};
use std::str::FromStr;
use tauri::PackageInfo;
/// initialize this instance's log file
fn init_log() -> Result<()> {
@@ -116,7 +115,10 @@ pub fn delete_log() -> Result<()> {
if file_name.ends_with(".log") {
let now = Local::now();
let created_time = parse_time_str(&file_name[0..file_name.len() - 4])?;
let file_time = Local.from_local_datetime(&created_time).single().ok_or(anyhow::anyhow!("invalid local datetime"))?;
let file_time = Local
.from_local_datetime(&created_time)
.single()
.ok_or(anyhow::anyhow!("invalid local datetime"))?;
let duration = now.signed_duration_since(file_time);
if duration.num_days() > day {
@@ -139,11 +141,6 @@ pub fn delete_log() -> Result<()> {
/// Initialize all the config files
/// before tauri setup
pub fn init_config() -> Result<()> {
#[cfg(target_os = "windows")]
unsafe {
let _ = dirs::init_portable_flag();
}
let _ = init_log();
let _ = delete_log();
@@ -185,9 +182,9 @@ pub fn init_config() -> Result<()> {
/// initialize app resources
/// after tauri setup
pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
pub fn init_resources() -> Result<()> {
let app_dir = dirs::app_home_dir()?;
let res_dir = dirs::app_resources_dir(package_info)?;
let res_dir = dirs::app_resources_dir()?;
if !app_dir.exists() {
let _ = fs::create_dir_all(&app_dir);
@@ -241,3 +238,64 @@ pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
Ok(())
}
/// initialize service resources
/// after tauri setup
#[cfg(target_os = "windows")]
pub fn init_service() -> Result<()> {
let service_dir = dirs::service_dir()?;
let res_dir = dirs::app_resources_dir()?;
if !service_dir.exists() {
let _ = fs::create_dir_all(&service_dir);
}
if !res_dir.exists() {
let _ = fs::create_dir_all(&res_dir);
}
let file_list = [
"clash-verge-service.exe",
"install-service.exe",
"uninstall-service.exe",
];
// copy the resource file
// if the source file is newer than the destination file, copy it over
for file in file_list.iter() {
let src_path = res_dir.join(file);
let dest_path = service_dir.join(file);
let handle_copy = || {
match fs::copy(&src_path, &dest_path) {
Ok(_) => log::debug!(target: "app", "resources copied '{file}'"),
Err(err) => {
log::error!(target: "app", "failed to copy resources '{file}', {err}")
}
};
};
if src_path.exists() && !dest_path.exists() {
handle_copy();
continue;
}
let src_modified = fs::metadata(&src_path).and_then(|m| m.modified());
let dest_modified = fs::metadata(&dest_path).and_then(|m| m.modified());
match (src_modified, dest_modified) {
(Ok(src_modified), Ok(dest_modified)) => {
if src_modified > dest_modified {
handle_copy();
} else {
log::debug!(target: "app", "skipping resource copy '{file}'");
}
}
_ => {
log::debug!(target: "app", "failed to get modified '{file}'");
handle_copy();
}
};
}
Ok(())
}

View File

@@ -4,4 +4,3 @@ pub mod init;
pub mod resolve;
pub mod server;
pub mod tmpl;
// mod winhelp;

View File

@@ -30,8 +30,9 @@ pub fn resolve_setup(app: &mut App) {
handle::Handle::global().init(app.app_handle());
log_err!(init::init_resources(app.package_info()));
log_err!(init::init_resources());
#[cfg(target_os = "windows")]
log_err!(init::init_service());
// 处理随机端口
let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
@@ -107,6 +108,7 @@ pub fn create_window(app_handle: &AppHandle) {
tauri::WindowUrl::App("index.html".into()),
)
.title("Clash Verge")
.visible(false)
.fullscreen(false)
.min_inner_size(600.0, 520.0);
@@ -138,8 +140,6 @@ pub fn create_window(app_handle: &AppHandle) {
#[cfg(target_os = "windows")]
{
use std::time::Duration;
use tokio::time::sleep;
use window_shadows::set_shadow;
match builder
@@ -173,19 +173,11 @@ pub fn create_window(app_handle: &AppHandle) {
log::trace!("try to create window");
let app_handle = app_handle.clone();
// 加点延迟避免界面闪一下
tauri::async_runtime::spawn(async move {
sleep(Duration::from_millis(888)).await;
if let Some(window) = app_handle.get_window("main") {
trace_err!(set_shadow(&window, true), "set win shadow");
trace_err!(window.show(), "set win visible");
trace_err!(window.unminimize(), "set win unminimize");
trace_err!(window.set_focus(), "set win focus");
} else {
log::error!(target: "app", "failed to create window, get_window is None")
}
});
if let Some(window) = app_handle.get_window("main") {
trace_err!(set_shadow(&window, true), "set win shadow");
} else {
log::error!(target: "app", "failed to create window, get_window is None")
}
}
Err(err) => log::error!(target: "app", "failed to create window, {err}"),
}
@@ -204,6 +196,13 @@ pub fn create_window(app_handle: &AppHandle) {
/// save window size and position
pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) -> Result<()> {
let verge = Config::verge();
let mut verge = verge.latest();
if save_to_file {
verge.save_file()?;
}
let win = app_handle
.get_window("main")
.ok_or(anyhow::anyhow!("failed to get window"))?;
@@ -214,12 +213,8 @@ pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) ->
let pos = win.outer_position()?;
let pos = pos.to_logical::<f64>(scale);
let verge = Config::verge();
let mut verge = verge.latest();
verge.window_size_position = Some(vec![size.width, size.height, pos.x, pos.y]);
if save_to_file {
verge.save_file()?;
if size.width >= 600.0 && size.height >= 520.0 {
verge.window_size_position = Some(vec![size.width, size.height, pos.x, pos.y]);
}
Ok(())

View File

@@ -1,69 +0,0 @@
#![cfg(target_os = "windows")]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
//!
//! From https://github.com/tauri-apps/window-vibrancy/blob/dev/src/windows.rs
//!
use windows_sys::Win32::{
Foundation::*,
System::{LibraryLoader::*, SystemInformation::*},
};
fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
let module = unsafe { LoadLibraryA(library.as_ptr()) };
if module == 0 {
return None;
}
Some(unsafe { GetProcAddress(module, function.as_ptr()) })
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0')).map(|f| unsafe {
std::mem::transmute::<::windows_sys::Win32::Foundation::FARPROC, $func>(f)
})
};
}
/// Returns a tuple of (major, minor, buildnumber)
fn get_windows_ver() -> Option<(u32, u32, u32)> {
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
let handle = get_function!("ntdll.dll", RtlGetVersion);
if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
dwBuildNumber: 0,
dwPlatformId: 0,
szCSDVersion: [0; 128],
};
let status = (rtl_get_version)(&mut vi as _);
if status >= 0 {
Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
} else {
None
}
}
} else {
None
}
}
pub fn is_win11() -> bool {
let v = get_windows_ver().unwrap_or_default();
v.2 >= 22000
}
#[test]
fn test_version() {
dbg!(get_windows_ver().unwrap_or_default());
}

View File

@@ -1,7 +1,7 @@
{
"package": {
"productName": "Clash Verge",
"version": "1.4.3"
"version": "1.4.5"
},
"build": {
"distDir": "../dist",
@@ -10,13 +10,8 @@
"beforeBuildCommand": "pnpm run web:build"
},
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.ico",
"iconAsTemplate": true
},
"bundle": {
"active": true,
"targets": ["deb", "appimage", "nsis", "app", "dmg", "updater"],
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"icon": [
"icons/32x32.png",
@@ -30,32 +25,7 @@
"copyright": "© 2022 zzzgydi All Rights Reserved",
"category": "DeveloperTool",
"shortDescription": "A Clash Meta GUI based on tauri.",
"longDescription": "A Clash Meta GUI based on tauri.",
"deb": {
"depends": ["openssl"]
},
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "embedBootstrapper",
"silent": true
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE"
}
}
"longDescription": "A Clash Meta GUI based on tauri."
},
"updater": {
"active": true,

View File

@@ -0,0 +1,13 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"targets": ["deb", "appimage", "updater"],
"deb": {
"depends": ["openssl"]
}
}
}
}

View File

@@ -0,0 +1,18 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/mac-tray-icon.png",
"iconAsTemplate": true
},
"bundle": {
"targets": ["app", "dmg", "updater"],
"macOS": {
"frameworks": [],
"minimumSystemVersion": "",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
}
}
}
}

View File

@@ -0,0 +1,25 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.png"
},
"bundle": {
"targets": ["nsis", "updater"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "embedBootstrapper",
"silent": true
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE"
}
}
}
}
}

View File

@@ -18,7 +18,7 @@ export const BasePage: React.FC<Props> = (props) => {
return (
<BaseErrorBoundary>
<div className="base-page" data-windrag>
<div className="base-page">
<header data-windrag style={{ userSelect: "none" }}>
<Typography variant="h4" component="h1" data-windrag>
{title}
@@ -38,7 +38,7 @@ export const BasePage: React.FC<Props> = (props) => {
: "",
}}
>
<div className="base-content" style={contentStyle} data-windrag>
<div className="base-content" style={contentStyle}>
{children}
</div>
</section>

View File

@@ -3,12 +3,19 @@ import { appWindow } from "@tauri-apps/api/window";
import {
CloseRounded,
CropSquareRounded,
FilterNoneRounded,
HorizontalRuleRounded,
} from "@mui/icons-material";
import { useState } from "react";
export const LayoutControl = () => {
const minWidth = 40;
const [isMaximized, setIsMaximized] = useState(false);
appWindow.isMaximized().then((isMaximized) => {
setIsMaximized(() => isMaximized);
});
return (
<>
<Button
@@ -22,9 +29,21 @@ export const LayoutControl = () => {
<Button
size="small"
sx={{ minWidth, svg: { transform: "scale(0.9)" } }}
onClick={() => appWindow.toggleMaximize()}
onClick={() => {
setIsMaximized((isMaximized) => !isMaximized);
appWindow.toggleMaximize();
}}
>
<CropSquareRounded fontSize="small" />
{isMaximized ? (
<FilterNoneRounded
fontSize="small"
style={{
transform: "rotate(180deg) scale(0.7)",
}}
/>
) : (
<CropSquareRounded fontSize="small" />
)}
</Button>
<Button

View File

@@ -209,7 +209,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
<TextField
{...text}
{...field}
placeholder={`clash-verge/v${version}`}
placeholder={`clash-verge-rev`}
label="User Agent"
/>
)}

View File

@@ -4,7 +4,8 @@ import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { useTranslation } from "react-i18next";
import { useVerge } from "@/hooks/use-verge";
import { useLockFn } from "ahooks";
import { Lock } from "@mui/icons-material";
import { LoadingButton } from "@mui/lab";
import { SwitchAccessShortcut, RestartAlt } from "@mui/icons-material";
import {
Box,
Button,
@@ -14,7 +15,7 @@ import {
ListItemText,
} from "@mui/material";
import { changeClashCore, restartSidecar } from "@/services/cmds";
import { closeAllConnections } from "@/services/api";
import { closeAllConnections, upgradeCore } from "@/services/api";
import { grantPermission } from "@/services/cmds";
import getSystem from "@/utils/get-system";
@@ -31,6 +32,7 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
const { verge, mutateVerge } = useVerge();
const [open, setOpen] = useState(false);
const [upgrading, setUpgrading] = useState(false);
useImperativeHandle(ref, () => ({
open: () => setOpen(true),
@@ -76,21 +78,52 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
}
});
const onUpgrade = useLockFn(async () => {
try {
setUpgrading(true);
await upgradeCore();
setUpgrading(false);
Notice.success(`Successfully upgrade core`, 1000);
} catch (err: any) {
setUpgrading(false);
Notice.error(err?.response.data.message || err.toString());
}
});
return (
<BaseDialog
open={open}
title={
<Box display="flex" justifyContent="space-between">
{t("Clash Core")}
<Button variant="contained" size="small" onClick={onRestart}>
{t("Restart")}
</Button>
<Box>
{clash_core !== "clash-meta" && (
<LoadingButton
variant="contained"
size="small"
startIcon={<SwitchAccessShortcut />}
loadingPosition="start"
loading={upgrading}
sx={{ marginRight: "8px" }}
onClick={onUpgrade}
>
{t("Upgrade")}
</LoadingButton>
)}
<Button
variant="contained"
size="small"
onClick={onRestart}
startIcon={<RestartAlt />}
>
{t("Restart")}
</Button>
</Box>
</Box>
}
contentSx={{
pb: 0,
width: 320,
width: 400,
height: 180,
overflowY: "auto",
userSelect: "text",

View File

@@ -14,7 +14,7 @@ export const ClashPortViewer = forwardRef<DialogRef>((props, ref) => {
const [open, setOpen] = useState(false);
const [port, setPort] = useState(
verge?.verge_mixed_port ?? clashInfo?.port ?? 7890
verge?.verge_mixed_port ?? clashInfo?.port ?? 7897
);
useImperativeHandle(ref, () => ({

View File

@@ -20,8 +20,8 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
const [open, setOpen] = useState(false);
const [values, setValues] = useState({
appLogLevel: "info",
autoCloseConnection: false,
enableClashFields: false,
autoCloseConnection: true,
enableClashFields: true,
enableBuiltinEnhanced: true,
proxyLayoutColumn: 6,
defaultLatencyTest: "",
@@ -34,7 +34,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
setValues({
appLogLevel: verge?.app_log_level ?? "info",
autoCloseConnection: verge?.auto_close_connection ?? true,
enableClashFields: verge?.enable_clash_fields ?? false,
enableClashFields: verge?.enable_clash_fields ?? true,
enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true,
proxyLayoutColumn: verge?.proxy_layout_column || 6,
defaultLatencyTest: verge?.default_latency_test || "",

View File

@@ -59,7 +59,7 @@ export const WebUIViewer = forwardRef<DialogRef>((props, ref) => {
.slice(clashInfo.server.indexOf(":") + 1)
.trim();
url = url.replaceAll("%port", port || "9090");
url = url.replaceAll("%port", port || "9097");
url = url.replaceAll(
"%secret",
encodeURIComponent(clashInfo.secret || "")

View File

@@ -1,5 +1,6 @@
import { useRef } from "react";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
import {
TextField,
Switch,
@@ -22,6 +23,7 @@ import { ClashCoreViewer } from "./mods/clash-core-viewer";
import { invoke_uwp_tool } from "@/services/cmds";
import getSystem from "@/utils/get-system";
import { useVerge } from "@/hooks/use-verge";
import { updateGeoData } from "@/services/api";
const isWIN = getSystem() === "windows";
@@ -33,7 +35,6 @@ const SettingClash = ({ onError }: Props) => {
const { t } = useTranslation();
const { clash, version, mutateClash, patchClash } = useClash();
const { verge, mutateVerge, patchVerge } = useVerge();
const { ipv6, "allow-lan": allowLan, "log-level": logLevel } = clash ?? {};
@@ -41,7 +42,7 @@ const SettingClash = ({ onError }: Props) => {
const {
enable_random_port = false,
verge_mixed_port,
enable_clash_fields = false,
enable_clash_fields = true,
} = verge ?? {};
const webRef = useRef<DialogRef>(null);
@@ -57,6 +58,15 @@ const SettingClash = ({ onError }: Props) => {
const onChangeVerge = (patch: Partial<IVergeConfig>) => {
mutateVerge({ ...verge, ...patch }, false);
};
const onUpdateGeo = useLockFn(async () => {
try {
await updateGeoData();
Notice.success("Start update geodata");
} catch (err: any) {
Notice.error(err?.response.data.message || err.toString());
}
});
return (
<SettingList title={t("Clash Setting")}>
<WebUIViewer ref={webRef} />
@@ -135,7 +145,7 @@ const SettingClash = ({ onError }: Props) => {
disabled={enable_random_port}
autoComplete="off"
size="small"
value={verge_mixed_port ?? 7890}
value={verge_mixed_port ?? 7897}
sx={{ width: 100, input: { py: "7.5px", cursor: "pointer" } }}
onClick={(e) => {
portRef.current?.open();
@@ -209,6 +219,17 @@ const SettingClash = ({ onError }: Props) => {
</IconButton>
</SettingItem>
)}
<SettingItem label={t("Update GeoData")}>
<IconButton
color="inherit"
size="small"
sx={{ my: "2px" }}
onClick={onUpdateGeo}
>
<ArrowForward />
</IconButton>
</SettingItem>
</SettingList>
);
};

View File

@@ -29,7 +29,7 @@ const SettingVerge = ({ onError }: Props) => {
const { t } = useTranslation();
const { verge, patchVerge, mutateVerge } = useVerge();
const { theme_mode, language, tray_event } = verge ?? {};
const { theme_mode, language, tray_event, env_type } = verge ?? {};
const configRef = useRef<DialogRef>(null);
const hotkeyRef = useRef<DialogRef>(null);
const miscRef = useRef<DialogRef>(null);
@@ -109,6 +109,22 @@ const SettingVerge = ({ onError }: Props) => {
</SettingItem>
)}
<SettingItem label={t("Copy Env Type")}>
<GuardState
value={env_type ?? (OS === "windows" ? "powershell" : "bash")}
onCatch={onError}
onFormat={(e: any) => e.target.value}
onChange={(e) => onChangeData({ env_type: e })}
onGuard={(e) => patchVerge({ env_type: e })}
>
<Select size="small" sx={{ width: 140, "> div": { py: "7.5px" } }}>
<MenuItem value="bash">Bash</MenuItem>
<MenuItem value="cmd">CMD</MenuItem>
<MenuItem value="powershell">PowerShell</MenuItem>
</Select>
</GuardState>
</SettingItem>
<SettingItem label={t("Theme Setting")}>
<IconButton
color="inherit"

View File

@@ -80,6 +80,7 @@
"System Proxy": "System Proxy",
"System Proxy Setting": "System Proxy Setting",
"Open UWP tool": "Open UWP tool",
"Update GeoData": "Update GeoData",
"Proxy Guard": "Proxy Guard",
"Guard Duration": "Guard Duration",
"Proxy Bypass": "Proxy Bypass",
@@ -90,6 +91,7 @@
"Theme Mode": "Theme Mode",
"Theme Blur": "Theme Blur",
"Tray Click Event": "Tray Click Event",
"Copy Env Type": "Copy Env Type",
"Show Main Window": "Show Main Window",
"Theme Setting": "Theme Setting",
"Layout Setting": "Layout Setting",
@@ -110,6 +112,7 @@
"Runtime Config": "Runtime Config",
"ReadOnly": "ReadOnly",
"Restart": "Restart",
"Upgrade": "Upgrade",
"Back": "Back",
"Save": "Save",

View File

@@ -74,6 +74,7 @@
"System Proxy": "Системный прокси",
"System Proxy Setting": "Настройка системного прокси",
"Open UWP tool": "Открыть UWP инструмент",
"Update GeoData": "Обновление GeoData",
"Proxy Guard": "Защита прокси",
"Guard Duration": "Период защиты",
"Proxy Bypass": "Игнорирование прокси",
@@ -81,6 +82,7 @@
"Theme Mode": "Режим темы",
"Theme Blur": "Размытие темы",
"Tray Click Event": "Событие щелчка в лотке",
"Copy Env Type": "Скопировать тип Env",
"Show Main Window": "Показать главное окно",
"Theme Setting": "Настройка темы",
"Hotkey Setting": "Настройка клавиатурных сокращений",
@@ -97,6 +99,7 @@
"Runtime Config": "Используемый конфиг",
"ReadOnly": "Только для чтения",
"Restart": "Перезапуск",
"Upgrade": "Обновлять",
"Back": "Назад",
"Save": "Сохранить",

View File

@@ -80,6 +80,7 @@
"System Proxy": "系统代理",
"System Proxy Setting": "系统代理设置",
"Open UWP tool": "UWP工具",
"Update GeoData": "更新 GeoData",
"Proxy Guard": "系统代理守卫",
"Guard Duration": "代理守卫间隔",
"Proxy Bypass": "代理绕过",
@@ -90,6 +91,7 @@
"Theme Mode": "主题模式",
"Theme Blur": "背景模糊",
"Tray Click Event": "托盘点击事件",
"Copy Env Type": "复制环境变量类型",
"Show Main Window": "显示主窗口",
"Theme Setting": "主题设置",
"Layout Setting": "界面设置",
@@ -110,6 +112,7 @@
"Runtime Config": "当前配置",
"ReadOnly": "只读",
"Restart": "重启内核",
"Upgrade": "升级内核",
"Back": "返回",
"Save": "保存",

View File

@@ -71,6 +71,11 @@ const Layout = () => {
break;
}
});
setTimeout(() => {
void appWindow.unminimize();
void appWindow.show();
void appWindow.setFocus();
}, 50);
}, []);
useEffect(() => {

View File

@@ -25,7 +25,7 @@ const ProxyPage = () => {
const { verge } = useVerge();
const modeList = useMemo(() => {
if (verge?.clash_core === "clash-meta") {
if (verge?.clash_core?.includes("clash-meta")) {
return ["rule", "global", "direct"];
}
return ["rule", "global", "direct", "script"];

View File

@@ -55,6 +55,18 @@ export const updateConfigs = async (config: Partial<IConfigData>) => {
return instance.patch("/configs", config);
};
/// Update geo data
export const updateGeoData = async () => {
const instance = await getAxios();
return instance.post("/configs/geo");
};
/// Upgrade clash core
export const upgradeCore = async () => {
const instance = await getAxios();
return instance.post("/upgrade");
};
/// Get current rules
export const getRules = async () => {
const instance = await getAxios();

View File

@@ -156,6 +156,7 @@ interface IVergeConfig {
app_log_level?: "trace" | "debug" | "info" | "warn" | "error" | string;
language?: string;
tray_event?: "main_window" | "system_proxy" | "tun_mode" | string;
env_type?: "bash" | "cmd" | "powershell" | string;
clash_core?: string;
theme_mode?: "light" | "dark" | "system";
theme_blur?: boolean;