Compare commits

...

13 Commits

Author SHA1 Message Date
2dust
67494108ad up 7.15.7 2025-10-31 19:05:57 +08:00
2dust
38b2a7d2ca Rename QRCodeWindowsUtils 2025-10-31 17:50:28 +08:00
dependabot[bot]
bf3703bca1 Bump actions/download-artifact from 4 to 6 (#8225)
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 4 to 6.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v4...v6)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-30 20:36:43 +08:00
2dust
554632cc07 Bug fix
https://github.com/2dust/v2rayN/issues/8207
2025-10-30 20:34:47 +08:00
2dust
12fc3e9566 Bug fix 2025-10-30 20:00:15 +08:00
2dust
c2ef3a4a8c Add Design.IsDesignMode to the desktop version 2025-10-29 20:21:41 +08:00
2dust
86eb8297dd Update JsonUtils.cs 2025-10-29 20:20:44 +08:00
2dust
c63d4e83f9 Use OperatingSystem replace RuntimeInformation 2025-10-29 20:20:40 +08:00
JieXu
bf1fb0f92e RPM file remove x86-64-v1 Support. Update French translation. (#8216)
* Update ResUI.fr.resx

* Update build-linux.yml

* Update package-rhel.sh

* 更新 build-linux.yml

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx
2025-10-29 09:21:37 +08:00
dependabot[bot]
3c4865982b Bump actions/upload-artifact from 4.6.2 to 5.0.0 (#8211)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4.6.2 to 5.0.0.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v4.6.2...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 13:55:57 +08:00
Aron Yang
22c233f0cd Fix TUN mode cleanup on Linux/macOS (#8202) 2025-10-26 11:09:23 +08:00
JieXu
b2d6282755 Remove AppImage. Update package.sh (#8201)
* Refactor AppRun script generation in packaging

* Update minimum kernel version requirement to 6.13

* Update minimum kernel version requirement to 5.14

* Revise runtime dependencies with version constraints

Updated runtime dependencies for package-rhel.sh to include version constraints and additional requirements.

* Modify package dependencies in package-debian.sh

Updated package dependencies to include libc6, fontconfig, coreutils, and bash.

* Remove AppImage packaging and upload steps

Removed AppImage packaging and upload steps from the workflow.

* Delete package-appimage.sh

* Simplify environment checks in Utils.cs

Removed checks for APPIMAGE environment variable and mount path.

* Update v2rayN.slnx

* Remove package scripts from v2rayN solution

Removed references to package-appimage.sh and pkg2appimage.yml from the solution file.
2025-10-26 10:13:49 +08:00
2dust
c8d89e3dce Adjusted the items in the configuration right-click menu 2025-10-25 20:10:42 +08:00
27 changed files with 213 additions and 207 deletions

View File

@@ -9,6 +9,12 @@ on:
push: push:
branches: branches:
- master - master
tags:
- 'v*'
- 'V*'
permissions:
contents: write
env: env:
OutputArch: "linux-64" OutputArch: "linux-64"
@@ -21,7 +27,6 @@ jobs:
strategy: strategy:
matrix: matrix:
configuration: [Release] configuration: [Release]
runs-on: ubuntu-24.04 runs-on: ubuntu-24.04
steps: steps:
@@ -31,7 +36,7 @@ jobs:
submodules: 'recursive' submodules: 'recursive'
fetch-depth: '0' fetch-depth: '0'
- name: Setup - name: Setup .NET
uses: actions/setup-dotnet@v5.0.0 uses: actions/setup-dotnet@v5.0.0
with: with:
dotnet-version: '8.0.x' dotnet-version: '8.0.x'
@@ -39,13 +44,13 @@ jobs:
- name: Build - name: Build
run: | run: |
cd v2rayN cd v2rayN
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o $OutputPath64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-x64 --self-contained=true -o "$OutputPath64"
dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o $OutputPathArm64 dotnet publish ./v2rayN.Desktop/v2rayN.Desktop.csproj -c Release -r linux-arm64 --self-contained=true -o "$OutputPathArm64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o $OutputPath64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-x64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPath64"
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r linux-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v5.0.0
with: with:
name: v2rayN-linux name: v2rayN-linux
path: | path: |
@@ -56,8 +61,8 @@ jobs:
if: github.event.inputs.release_tag != '' if: github.event.inputs.release_tag != ''
run: | run: |
chmod 755 package-debian.sh chmod 755 package-debian.sh
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }} ./package-debian.sh "$OutputArch" "$OutputPath64" "${{ github.event.inputs.release_tag }}"
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ github.event.inputs.release_tag }} ./package-debian.sh "$OutputArchArm" "$OutputPathArm64" "${{ github.event.inputs.release_tag }}"
- name: Upload deb to release - name: Upload deb to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
@@ -68,28 +73,13 @@ jobs:
file_glob: true file_glob: true
prerelease: true prerelease: true
- name: Package AppImage
if: github.event.inputs.release_tag != ''
run: |
chmod a+x package-appimage.sh
./package-appimage.sh
- name: Upload AppImage to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/v2rayN*.AppImage
tag: ${{ github.event.inputs.release_tag }}
file_glob: true
prerelease: true
# release zip archive # release zip archive
- name: Package release zip archive - name: Package release zip archive
if: github.event.inputs.release_tag != '' if: github.event.inputs.release_tag != ''
run: | run: |
chmod 755 package-release-zip.sh chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64 ./package-release-zip.sh "$OutputArch" "$OutputPath64"
./package-release-zip.sh $OutputArchArm $OutputPathArm64 ./package-release-zip.sh "$OutputArchArm" "$OutputPathArm64"
- name: Upload zip archive to release - name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
@@ -100,36 +90,62 @@ jobs:
file_glob: true file_glob: true
prerelease: true prerelease: true
# release RHEL package rpm:
- name: Package RPM (RHEL-family) needs: build
if: github.event.inputs.release_tag != '' if: |
(github.event_name == 'workflow_dispatch' && github.event.inputs.release_tag != '') ||
(github.event_name == 'push' && startsWith(github.ref, 'refs/tags/'))
runs-on: ubuntu-24.04
container:
image: quay.io/almalinuxorg/10-base:latest
options: --platform=linux/amd64/v2
env:
RELEASE_TAG: ${{ github.event.inputs.release_tag != '' && github.event.inputs.release_tag || github.ref_name }}
steps:
- name: Prepare tools (Red Hat)
run: | run: |
chmod 755 package-rhel.sh dnf -y makecache
# Build for both x86_64 and aarch64 in one go (explicit version passed; no --buildfrom) dnf -y install epel-release
./package-rhel.sh "${{ github.event.inputs.release_tag }}" --arch all dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
- name: Checkout repo (for scripts)
uses: actions/checkout@v5.0.0
with:
submodules: 'recursive'
fetch-depth: '0'
- name: Restore build artifacts
uses: actions/download-artifact@v6
with:
name: v2rayN-linux
path: ${{ github.workspace }}/v2rayN/Release
- name: Ensure script permissions
run: chmod 755 package-rhel.sh
- name: Package RPM (RHEL-family)
run: ./package-rhel.sh "${RELEASE_TAG}" --arch all
- name: Collect RPMs into workspace - name: Collect RPMs into workspace
if: github.event.inputs.release_tag != ''
run: | run: |
mkdir -p "${{ github.workspace }}/dist/rpm" mkdir -p "$GITHUB_WORKSPACE/dist/rpm"
rsync -av "$HOME/rpmbuild/RPMS/" "${{ github.workspace }}/dist/rpm/" rsync -av "$HOME/rpmbuild/RPMS/" "$GITHUB_WORKSPACE/dist/rpm/" || true
# Rename to requested filenames find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.x86_64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-64.rpm" \; || true
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.x86_64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-x64.rpm" \; || true find "$GITHUB_WORKSPACE/dist/rpm" -name "v2rayN-*-1*.aarch64.rpm" -exec mv {} "$GITHUB_WORKSPACE/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true
find "${{ github.workspace }}/dist/rpm" -name "v2rayN-*-1.aarch64.rpm" -exec mv {} "${{ github.workspace }}/dist/rpm/v2rayN-linux-rhel-arm64.rpm" \; || true echo "==== Dist tree ===="
ls -R "$GITHUB_WORKSPACE/dist/rpm" || true
- name: Upload RPM artifacts - name: Upload RPM artifacts
if: github.event.inputs.release_tag != '' uses: actions/upload-artifact@v5.0.0
uses: actions/upload-artifact@v4.6.2
with: with:
name: v2rayN-rpm name: v2rayN-rpm
path: | path: dist/rpm/**/*.rpm
${{ github.workspace }}/dist/rpm/**/*.rpm
- name: Upload RPMs to release - name: Upload RPMs to release
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with: with:
file: ${{ github.workspace }}/dist/rpm/**/*.rpm file: dist/rpm/**/*.rpm
tag: ${{ github.event.inputs.release_tag }} tag: ${{ env.RELEASE_TAG }}
file_glob: true file_glob: true
prerelease: true prerelease: true

View File

@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r osx-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v5.0.0
with: with:
name: v2rayN-macos name: v2rayN-macos
path: | path: |

View File

@@ -45,7 +45,7 @@ jobs:
dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64 dotnet publish ./AmazTool/AmazTool.csproj -c Release -r win-arm64 --self-contained=true -p:EnableWindowsTargeting=true -p:PublishTrimmed=true -o $OutputPathArm64
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v5.0.0
with: with:
name: v2rayN-windows-desktop name: v2rayN-windows-desktop
path: | path: |

View File

@@ -46,7 +46,7 @@ jobs:
- name: Upload build artifacts - name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2 uses: actions/upload-artifact@v5.0.0
with: with:
name: v2rayN-windows name: v2rayN-windows
path: | path: |

View File

@@ -1,67 +0,0 @@
#!/bin/bash
set -euo pipefail
# Install deps
sudo apt update -y
sudo apt install -y libfuse2 wget file
# Get tools
wget -qO appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
chmod +x appimagetool
# x86_64 AppDir
APPDIR_X64="AppDir-x86_64"
rm -rf "$APPDIR_X64"
mkdir -p "$APPDIR_X64/usr/lib/v2rayN" "$APPDIR_X64/usr/bin" "$APPDIR_X64/usr/share/applications" "$APPDIR_X64/usr/share/pixmaps"
cp -rf "$OutputPath64"/* "$APPDIR_X64/usr/lib/v2rayN" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_X64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_X64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_X64/AppRun"
chmod +x "$APPDIR_X64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_X64/usr/bin/v2rayN"
cat > "$APPDIR_X64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
install -Dm644 "$APPDIR_X64/v2rayN.desktop" "$APPDIR_X64/usr/share/applications/v2rayN.desktop"
ARCH=x86_64 ./appimagetool "$APPDIR_X64" "v2rayN-${OutputArch}.AppImage"
file "v2rayN-${OutputArch}.AppImage" | grep -q 'x86-64'
# aarch64 AppDir
APPDIR_ARM64="AppDir-aarch64"
rm -rf "$APPDIR_ARM64"
mkdir -p "$APPDIR_ARM64/usr/lib/v2rayN" "$APPDIR_ARM64/usr/bin" "$APPDIR_ARM64/usr/share/applications" "$APPDIR_ARM64/usr/share/pixmaps"
cp -rf "$OutputPathArm64"/* "$APPDIR_ARM64/usr/lib/v2rayN" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/usr/share/pixmaps/v2rayN.png" || true
[ -f "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" ] && cp "$APPDIR_ARM64/usr/lib/v2rayN/v2rayN.png" "$APPDIR_ARM64/v2rayN.png" || true
printf '%s\n' '#!/bin/sh' 'HERE="$(dirname "$(readlink -f "$0")")"' 'cd "$HERE/usr/lib/v2rayN"' 'exec "$HERE/usr/lib/v2rayN/v2rayN" "$@"' > "$APPDIR_ARM64/AppRun"
chmod +x "$APPDIR_ARM64/AppRun"
ln -sf usr/lib/v2rayN/v2rayN "$APPDIR_ARM64/usr/bin/v2rayN"
cat > "$APPDIR_ARM64/v2rayN.desktop" <<EOF
[Desktop Entry]
Name=v2rayN
Comment=A GUI client for Windows and Linux, support Xray core and sing-box-core and others
Exec=v2rayN
Icon=v2rayN
Terminal=false
Type=Application
Categories=Network;
EOF
install -Dm644 "$APPDIR_ARM64/v2rayN.desktop" "$APPDIR_ARM64/usr/share/applications/v2rayN.desktop"
# aarch64 runtime
wget -qO runtime-aarch64 https://github.com/AppImage/AppImageKit/releases/download/continuous/runtime-aarch64
chmod +x runtime-aarch64
# build aarch64 AppImage
ARCH=aarch64 ./appimagetool --runtime-file ./runtime-aarch64 "$APPDIR_ARM64" "v2rayN-${OutputArchArm}.AppImage"
file "v2rayN-${OutputArchArm}.AppImage" | grep -q 'ARM aarch64'

View File

@@ -28,7 +28,7 @@ Package: v2rayN
Version: $Version Version: $Version
Architecture: $Arch2 Architecture: $Arch2
Maintainer: https://github.com/2dust/v2rayN Maintainer: https://github.com/2dust/v2rayN
Depends: desktop-file-utils, xdg-utils Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1)
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF EOF

View File

@@ -19,6 +19,23 @@ else
exit 1 exit 1
fi fi
# ======================== Kernel version check (require >= 6.11) =======================
MIN_KERNEL_MAJOR=6
MIN_KERNEL_MINOR=11
KERNEL_FULL=$(uname -r)
KERNEL_MAJOR=$(echo "$KERNEL_FULL" | cut -d. -f1)
KERNEL_MINOR=$(echo "$KERNEL_FULL" | cut -d. -f2)
echo "[INFO] Detected kernel version: $KERNEL_FULL"
if (( KERNEL_MAJOR < MIN_KERNEL_MAJOR )) || { (( KERNEL_MAJOR == MIN_KERNEL_MAJOR )) && (( KERNEL_MINOR < MIN_KERNEL_MINOR )); }; then
echo "[ERROR] Kernel $KERNEL_FULL is too old. Requires Linux >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
echo "Please upgrade your system or use a newer container (e.g. Fedora 42+, RHEL 10+, Debian 13+)."
exit 1
fi
echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
# ===== Config & Parse arguments ========================================================= # ===== Config & Parse arguments =========================================================
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
WITH_CORE="both" # Default: bundle both xray+sing-box WITH_CORE="both" # Default: bundle both xray+sing-box
@@ -614,8 +631,13 @@ ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz Source0: __PKGROOT__.tar.gz
# Runtime dependencies (Avalonia / X11 / Fonts / GL) # Runtime dependencies (Avalonia / X11 / Fonts / GL)
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils Requires: glibc >= 2.34
Requires: fontconfig >= 2.13.1
Requires: desktop-file-utils >= 0.26
Requires: xdg-utils >= 1.1.3
Requires: coreutils >= 8.32
Requires: bash >= 5.1
%description %description
v2rayN Linux for Red Hat Enterprise Linux v2rayN Linux for Red Hat Enterprise Linux

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.15.6</Version> <Version>7.15.7</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -35,9 +35,13 @@ public class JsonUtils
/// <typeparam name="T"></typeparam> /// <typeparam name="T"></typeparam>
/// <param name="obj"></param> /// <param name="obj"></param>
/// <returns></returns> /// <returns></returns>
public static T DeepCopy<T>(T obj) public static T? DeepCopy<T>(T? obj)
{ {
return Deserialize<T>(Serialize(obj, false))!; if (obj is null)
{
return default;
}
return Deserialize<T>(Serialize(obj, false));
} }
/// <summary> /// <summary>
@@ -67,7 +71,7 @@ public class JsonUtils
/// </summary> /// </summary>
/// <param name="strJson"></param> /// <param name="strJson"></param>
/// <returns></returns> /// <returns></returns>
public static JsonNode? ParseJson(string strJson) public static JsonNode? ParseJson(string? strJson)
{ {
try try
{ {
@@ -116,7 +120,7 @@ public class JsonUtils
/// <param name="obj"></param> /// <param name="obj"></param>
/// <param name="options"></param> /// <param name="options"></param>
/// <returns></returns> /// <returns></returns>
public static string Serialize(object? obj, JsonSerializerOptions options) public static string Serialize(object? obj, JsonSerializerOptions? options)
{ {
var result = string.Empty; var result = string.Empty;
try try
@@ -125,7 +129,7 @@ public class JsonUtils
{ {
return result; return result;
} }
result = JsonSerializer.Serialize(obj, options); result = JsonSerializer.Serialize(obj, options ?? _defaultSerializeOptions);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@@ -963,13 +963,13 @@ public class Utils
#region Platform #region Platform
public static bool IsWindows() => RuntimeInformation.IsOSPlatform(OSPlatform.Windows); public static bool IsWindows() => OperatingSystem.IsWindows();
public static bool IsLinux() => RuntimeInformation.IsOSPlatform(OSPlatform.Linux); public static bool IsLinux() => OperatingSystem.IsLinux();
public static bool IsOSX() => RuntimeInformation.IsOSPlatform(OSPlatform.OSX); public static bool IsOSX() => OperatingSystem.IsMacOS();
public static bool IsNonWindows() => !RuntimeInformation.IsOSPlatform(OSPlatform.Windows); public static bool IsNonWindows() => !OperatingSystem.IsWindows();
public static string GetExeName(string name) public static string GetExeName(string name)
{ {
@@ -994,11 +994,6 @@ public class Utils
return false; return false;
} }
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
{
return true;
}
var exePath = GetExePath(); var exePath = GetExePath();
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? ""; var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
var p = baseDir.Replace('\\', '/'); var p = baseDir.Replace('\\', '/');
@@ -1008,11 +1003,6 @@ public class Utils
return false; return false;
} }
if (p.Contains("/.mount_", StringComparison.Ordinal))
{
return true;
}
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase)) if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
{ {
return true; return true;

View File

@@ -34,7 +34,7 @@ public class CoreAdminManager
StringBuilder sb = new(); StringBuilder sb = new();
sb.AppendLine("#!/bin/bash"); sb.AppendLine("#!/bin/bash");
var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}"; var cmdLine = $"{fileName.AppendQuotes()} {string.Format(coreInfo.Arguments, Utils.GetBinConfigPath(configPath).AppendQuotes())}";
sb.AppendLine($"sudo -S {cmdLine}"); sb.AppendLine($"exec sudo -S -- {cmdLine}");
var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true); var shFilePath = await FileManager.CreateLinuxShellFile("run_as_sudo.sh", sb.ToString(), true);
var procService = new ProcessService( var procService = new ProcessService(

View File

@@ -472,7 +472,7 @@
<value>Langue (redémarrage requis)</value> <value>Langue (redémarrage requis)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>Importer des liens depuis le presse-papiers (Ctrl+V)</value> <value>Importer liens depuis le presse-papiers (Ctrl+V)</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>Scanner le QR code à lécran (Ctrl+S)</value> <value>Scanner le QR code à lécran (Ctrl+S)</value>
@@ -619,7 +619,7 @@
<value>Sécurité couche transport (TLS)</value> <value>Sécurité couche transport (TLS)</value>
</data> </data>
<data name="TipNetwork" xml:space="preserve"> <data name="TipNetwork" xml:space="preserve">
<value>*tcp par défaut ; un mauvais choix empêchera la connexion</value> <value>*tcp par défaut ; un mauvais choix bloque la connexion</value>
</data> </data>
<data name="TbCoreType" xml:space="preserve"> <data name="TbCoreType" xml:space="preserve">
<value>Type de Core</value> <value>Type de Core</value>
@@ -652,7 +652,7 @@
<value>Port Socks</value> <value>Port Socks</value>
</data> </data>
<data name="TipPreSocksPort" xml:space="preserve"> <data name="TipPreSocksPort" xml:space="preserve">
<value>*Valeur du port Socks pour la configuration personnalisée (facultatif). Si défini, Xray/sing-box (Tun) démarrera un service Socks en amont supplémentaire pour fournir le routage sélectif et laffichage de la vitesse.</value> <value>*Valeur du port Socks (config perso, optionnelle). Si défini, Xray/sing-box (Tun) démarre un service Socks en amont supplémentaire pour fournir le routage sélectif et laffichage de la vitesse.</value>
</data> </data>
<data name="TbBrowse" xml:space="preserve"> <data name="TbBrowse" xml:space="preserve">
<value>Parcourir</value> <value>Parcourir</value>
@@ -670,7 +670,7 @@
<value>Masquer la fenêtre au démarrage</value> <value>Masquer la fenêtre au démarrage</value>
</data> </data>
<data name="TbSettingsAutoUpdateInterval" xml:space="preserve"> <data name="TbSettingsAutoUpdateInterval" xml:space="preserve">
<value>Intervalle de mise à jour automatique des fichiers Geo (heures)</value> <value>Intervalle de mise à jour auto des fichiers Geo (heures)</value>
</data> </data>
<data name="TbSettingsCore" xml:space="preserve"> <data name="TbSettingsCore" xml:space="preserve">
<value>Core : paramètres de base</value> <value>Core : paramètres de base</value>
@@ -691,7 +691,7 @@
<value>domainStrategy de Freedom (sortant)</value> <value>domainStrategy de Freedom (sortant)</value>
</data> </data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve"> <data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>Auto-ajuster la largeur des colonnes après maj. des abonnements</value> <value>Auto-ajuster la largeur des colonnes après maj. abonnements</value>
</data> </data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve"> <data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>Vérifier les mises à jour pré-version (à activer avec prudence)</value> <value>Vérifier les mises à jour pré-version (à activer avec prudence)</value>
@@ -724,7 +724,7 @@
<value>Mot de passe dauthentification</value> <value>Mot de passe dauthentification</value>
</data> </data>
<data name="TbSettingsRemoteDNS" xml:space="preserve"> <data name="TbSettingsRemoteDNS" xml:space="preserve">
<value>DNS personnalisés (plusieurs possibles, séparés par des virgules)</value> <value>DNS perso (plusieurs configurables, séparés par virgules)</value>
</data> </data>
<data name="TbSettingsSetUWP" xml:space="preserve"> <data name="TbSettingsSetUWP" xml:space="preserve">
<value>Lever la restriction de proxy en boucle locale pour les applications Win10 UWP</value> <value>Lever la restriction de proxy en boucle locale pour les applications Win10 UWP</value>
@@ -865,7 +865,7 @@
<value>Documentation détaillée des règles</value> <value>Documentation détaillée des règles</value>
</data> </data>
<data name="TbDnsObjectDoc" xml:space="preserve"> <data name="TbDnsObjectDoc" xml:space="preserve">
<value>Saisie de DnsObject prise en charge (format JSON), cliquer pour voir la documentation</value> <value>Saisie DnsObject prise en charge (format JSON), cliquer pour doc</value>
</data> </data>
<data name="SubUrlTips" xml:space="preserve"> <data name="SubUrlTips" xml:space="preserve">
<value>Laissez vide pour les groupes ordinaires</value> <value>Laissez vide pour les groupes ordinaires</value>
@@ -898,7 +898,7 @@
<value>Filtrage par alias (regex)</value> <value>Filtrage par alias (regex)</value>
</data> </data>
<data name="TbDisplayLog" xml:space="preserve"> <data name="TbDisplayLog" xml:space="preserve">
<value>Afficher les journaux</value> <value>Afficher les logs</value>
</data> </data>
<data name="TbEnableTunAs" xml:space="preserve"> <data name="TbEnableTunAs" xml:space="preserve">
<value>Activer Tun</value> <value>Activer Tun</value>
@@ -979,7 +979,7 @@
<value>En attente du test (appuyer sur Échap pour arrêter)...</value> <value>En attente du test (appuyer sur Échap pour arrêter)...</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Désactivez ceci en cas de coupures anormales</value> <value>Désactiver cette option si coupure anormale</value>
</data> </data>
<data name="MsgSkipSubscriptionUpdate" xml:space="preserve"> <data name="MsgSkipSubscriptionUpdate" xml:space="preserve">
<value>Mise à jour désactivée, abonnement ignoré</value> <value>Mise à jour désactivée, abonnement ignoré</value>
@@ -1009,10 +1009,10 @@
<value>DNS personnalisé sing-box</value> <value>DNS personnalisé sing-box</value>
</data> </data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve"> <data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>Veuillez renseigner la structure JSON DNS ; cliquer pour voir la documentation</value> <value>Saisissez la structure JSON DNS ; cliquez pour voir la doc.</value>
</data> </data>
<data name="TbSettingDnsImportDefConfig" xml:space="preserve"> <data name="TbSettingDnsImportDefConfig" xml:space="preserve">
<value>Cliquez pour importer la configuration DNS par défaut</value> <value>Cliquez pour importer la config DNS par défaut</value>
</data> </data>
<data name="TbdomainStrategy4Singbox" xml:space="preserve"> <data name="TbdomainStrategy4Singbox" xml:space="preserve">
<value>Stratégie résolution domaine (sing-box)</value> <value>Stratégie résolution domaine (sing-box)</value>
@@ -1045,10 +1045,10 @@
<value>Algo contrôle congestion</value> <value>Algo contrôle congestion</value>
</data> </data>
<data name="LvPrevProfile" xml:space="preserve"> <data name="LvPrevProfile" xml:space="preserve">
<value>Alias de configuration du proxy amont</value> <value>Alias de config du proxy amont</value>
</data> </data>
<data name="LvNextProfile" xml:space="preserve"> <data name="LvNextProfile" xml:space="preserve">
<value>Alias de configuration du proxy aval</value> <value>Alias de config du proxy aval</value>
</data> </data>
<data name="LvPrevProfileTip" xml:space="preserve"> <data name="LvPrevProfileTip" xml:space="preserve">
<value>Assurez-vous que lalias config existe et est unique</value> <value>Assurez-vous que lalias config existe et est unique</value>
@@ -1114,7 +1114,7 @@
<value>Activer le fragmentation (Fragment)</value> <value>Activer le fragmentation (Fragment)</value>
</data> </data>
<data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve"> <data name="TbSettingsEnableCacheFile4Sbox" xml:space="preserve">
<value>Activer le fichier de cache de sing-box (fichiers de reˋgles)</value> <value>Activer le fichier de cache de sing-box (fichiers gles)</value>
</data> </data>
<data name="LvCustomRulesetPath4Singbox" xml:space="preserve"> <data name="LvCustomRulesetPath4Singbox" xml:space="preserve">
<value>Set de règles sing-box perso</value> <value>Set de règles sing-box perso</value>
@@ -1207,7 +1207,7 @@
<value>Stratégie de résolution par défaut des sortants</value> <value>Stratégie de résolution par défaut des sortants</value>
</data> </data>
<data name="TbSettingsMainGirdOrientation" xml:space="preserve"> <data name="TbSettingsMainGirdOrientation" xml:space="preserve">
<value>Orientation de la mise en page principale (redémarrage requis)</value> <value>Orientation mise en page principale (redémarrage requis)</value>
</data> </data>
<data name="TbSettingsDomainDNSAddress" xml:space="preserve"> <data name="TbSettingsDomainDNSAddress" xml:space="preserve">
<value>Adresse de résolution de domaine pour sortants</value> <value>Adresse de résolution de domaine pour sortants</value>
@@ -1330,7 +1330,7 @@
<value>JSON brut XHTTP Extra, format : { XHTTPObject }</value> <value>JSON brut XHTTP Extra, format : { XHTTPObject }</value>
</data> </data>
<data name="TbSettingsHide2TrayWhenClose" xml:space="preserve"> <data name="TbSettingsHide2TrayWhenClose" xml:space="preserve">
<value>Masquer dans la barre détat lors de la fermeture de la fenêtre</value> <value>Masquer dans la barre détat à la fermeture de la fenêtre</value>
</data> </data>
<data name="TbSettingsMixedConcurrencyCount" xml:space="preserve"> <data name="TbSettingsMixedConcurrencyCount" xml:space="preserve">
<value>Niveau de concurrence lors des tests multithread</value> <value>Niveau de concurrence lors des tests multithread</value>
@@ -1351,7 +1351,7 @@
<value>Thème</value> <value>Thème</value>
</data> </data>
<data name="menuCopyProxyCmdToClipboard" xml:space="preserve"> <data name="menuCopyProxyCmdToClipboard" xml:space="preserve">
<value>Copier la commande proxy terminal vers le presse-papiers</value> <value>Copier la cmd proxy terminal dans le presse-papiers</value>
</data> </data>
<data name="SpeedtestingTestFailedPart" xml:space="preserve"> <data name="SpeedtestingTestFailedPart" xml:space="preserve">
<value>Recommencer le test des éléments échoués, {0} restants. Appuyez sur Échap pour arrêter...</value> <value>Recommencer le test des éléments échoués, {0} restants. Appuyez sur Échap pour arrêter...</value>
@@ -1369,7 +1369,7 @@
<value>Plage de ports sautés</value> <value>Plage de ports sautés</value>
</data> </data>
<data name="TbPorts7Tips" xml:space="preserve"> <data name="TbPorts7Tips" xml:space="preserve">
<value>Écrase le port ; pour plusieurs groupes, séparez par des virgules (,)</value> <value>Écrase le port ; pour plusieurs groupes, séparer par virgules (,)</value>
</data> </data>
<data name="menuGenGroupMultipleServer" xml:space="preserve"> <data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Générer un groupe de stratégie depuis plusieurs profils</value> <value>Générer un groupe de stratégie depuis plusieurs profils</value>
@@ -1447,7 +1447,7 @@
<value>Valider les IP des domaines de la région concernée</value> <value>Valider les IP des domaines de la région concernée</value>
</data> </data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve"> <data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>Après configuration, les IP renvoyées pour les domaines régionaux (p. ex. geosite:cn) seront validées ; seules les IP attendues seront retournées</value> <value>Après config, les IP renvoyées des domaines régionaux (ex. geosite:cn) seront vérifiées ; seules les IP attendues seront retournées.</value>
</data> </data>
<data name="TbCustomDNSEnable" xml:space="preserve"> <data name="TbCustomDNSEnable" xml:space="preserve">
<value>Activer le DNS personnalisé</value> <value>Activer le DNS personnalisé</value>
@@ -1471,7 +1471,7 @@
<value>Modèle de configuration complet v2ray</value> <value>Modèle de configuration complet v2ray</value>
</data> </data>
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve"> <data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
<value>Ajoute seulement la configuration sortante, routing.balancers et routing.rules.outboundTag. Voir la documentation.</value> <value>Ajoute seulement la config sortante, routing.balancers et routing.rules.outboundTag. Voir la doc.</value>
</data> </data>
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve"> <data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
<value>Najoutez pas de sorties pour protocoles non-proxy.</value> <value>Najoutez pas de sorties pour protocoles non-proxy.</value>
@@ -1492,10 +1492,10 @@
<value>Début de lanalyse et du traitement du contenu dabonnement</value> <value>Début de lanalyse et du traitement du contenu dabonnement</value>
</data> </data>
<data name="TbSelectProfile" xml:space="preserve"> <data name="TbSelectProfile" xml:space="preserve">
<value>Sélectionner une configuration</value> <value>Choisir une config.</value>
</data> </data>
<data name="TbFakeIPTips" xml:space="preserve"> <data name="TbFakeIPTips" xml:space="preserve">
<value>Actif globalement par défaut, avec filtrage FakeIP intégré ; ne fonctionne que dans sing-box</value> <value>Actif globalement par défaut, avec filtre FakeIP intégré ; ne fonctionne que dans sing-box</value>
</data> </data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve"> <data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Veuillez ajouter au moins une configuration</value> <value>Veuillez ajouter au moins une configuration</value>

View File

@@ -427,7 +427,7 @@
<value>路由设置</value> <value>路由设置</value>
</data> </data>
<data name="menuServers" xml:space="preserve"> <data name="menuServers" xml:space="preserve">
<value>配置文件</value> <value>配置</value>
</data> </data>
<data name="menuSetting" xml:space="preserve"> <data name="menuSetting" xml:space="preserve">
<value>设置</value> <value>设置</value>

View File

@@ -47,6 +47,18 @@ echo "============================================"
echo "Starting termination of process $PID and all its children" echo "Starting termination of process $PID and all its children"
echo "============================================" echo "============================================"
# Try graceful termination first
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
kill -15 "$PID" 2>/dev/null || true
sleep 1
# If still running, fall back to kill_children
if ps -p $PID > /dev/null; then
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its children and itself"
else
echo "Process $PID exited cleanly after SIGTERM"
exit 0
fi
# Find and kill all child processes # Find and kill all child processes
kill_children "$PID" kill_children "$PID"

View File

@@ -42,6 +42,20 @@ echo "============================================"
echo "Starting termination of process $PID and all its descendants" echo "Starting termination of process $PID and all its descendants"
echo "============================================" echo "============================================"
# Try graceful termination first
echo "Attempting graceful termination (SIGTERM) of PID: $PID"
kill -15 "$PID" 2>/dev/null || true
sleep 1
# If still running, fall back to kill_descendants
# Use the macOS-native 'kill -0' check
if kill -0 $PID 2>/dev/null; then
echo "Process $PID did not exit after SIGTERM; proceeding with forced termination of its descendants and itself"
else
echo "Process $PID exited cleanly after SIGTERM"
exit 0
fi
# Find and kill all descendant processes # Find and kill all descendant processes
kill_descendants "$PID" kill_descendants "$PID"

View File

@@ -10,15 +10,17 @@ public partial class App : Application
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
DataContext = StatusBarViewModel.Instance;
} }
public override void OnFrameworkInitializationCompleted() public override void OnFrameworkInitializationCompleted()
{ {
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{ {
AppManager.Instance.InitComponents(); if (!Design.IsDesignMode)
{
AppManager.Instance.InitComponents();
DataContext = StatusBarViewModel.Instance;
}
desktop.Exit += OnExit; desktop.Exit += OnExit;
desktop.MainWindow = new MainWindow(); desktop.MainWindow = new MainWindow();

View File

@@ -54,12 +54,19 @@ internal class Program
// Avalonia configuration, don't remove; also used by visual designer. // Avalonia configuration, don't remove; also used by visual designer.
public static AppBuilder BuildAvaloniaApp() public static AppBuilder BuildAvaloniaApp()
{ {
return AppBuilder.Configure<App>() var builder = AppBuilder.Configure<App>()
.UsePlatformDetect() .UsePlatformDetect()
//.WithInterFont() //.WithInterFont()
.WithFontByDefault() .WithFontByDefault()
.LogToTrace() .LogToTrace()
.UseReactiveUI() .UseReactiveUI();
.With(new MacOSPlatformOptions { ShowInDock = AppManager.Instance.Config.UiItem.MacOSShowInDock });
if (OperatingSystem.IsMacOS())
{
var showInDock = Design.IsDesignMode || AppManager.Instance.Config.UiItem.MacOSShowInDock;
builder = builder.With(new MacOSPlatformOptions { ShowInDock = showInDock });
}
return builder;
} }
} }

View File

@@ -168,7 +168,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
} }
menuAddServerViaScan.IsVisible = false; menuAddServerViaScan.IsVisible = false;
if (_config.UiItem.AutoHideStartup) if (_config.UiItem.AutoHideStartup && Utils.IsWindows())
{ {
this.WindowState = WindowState.Minimized; this.WindowState = WindowState.Minimized;
} }
@@ -402,9 +402,9 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
public void ShowHideWindow(bool? blShow) public void ShowHideWindow(bool? blShow)
{ {
var bl = blShow ?? var bl = blShow ??
Utils.IsLinux() (Utils.IsLinux()
? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized)) ? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
: !_config.UiItem.ShowInTaskbar; : !_config.UiItem.ShowInTaskbar);
if (bl) if (bl)
{ {
this.Show(); this.Show();

View File

@@ -75,6 +75,15 @@
<PathIcon Data="{StaticResource SemiIconBolt}" /> <PathIcon Data="{StaticResource SemiIconBolt}" />
</Button.Content> </Button.Content>
</Button> </Button>
<Button
x:Name="menuMixedTestServer"
Margin="{StaticResource MarginLr4}"
Classes="IconButton Success"
ToolTip.Tip="{x:Static resx:ResUI.menuMixedTestServer}">
<Button.Content>
<PathIcon Data="{StaticResource building_ping}" />
</Button.Content>
</Button>
</WrapPanel> </WrapPanel>
<DataGrid <DataGrid
@@ -99,15 +108,12 @@
<MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" /> <MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" />
<MenuItem x:Name="menuRemoveServer" Header="{x:Static resx:ResUI.menuRemoveServer}" /> <MenuItem x:Name="menuRemoveServer" Header="{x:Static resx:ResUI.menuRemoveServer}" />
<MenuItem x:Name="menuRemoveDuplicateServer" Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" /> <MenuItem x:Name="menuRemoveDuplicateServer" Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator /> <Separator />
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />
<MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" /> <MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" />
<MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" /> <MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" />
<MenuItem x:Name="menuSpeedServer" Header="{x:Static resx:ResUI.menuSpeedServer}" /> <MenuItem x:Name="menuSpeedServer" Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}"> <MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<Separator /> <Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}"> <MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem> <MenuItem>

View File

@@ -26,11 +26,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub Action", "GitHub Act
..\.github\workflows\build-osx.yml = ..\.github\workflows\build-osx.yml ..\.github\workflows\build-osx.yml = ..\.github\workflows\build-osx.yml
..\.github\workflows\build-windows-desktop.yml = ..\.github\workflows\build-windows-desktop.yml ..\.github\workflows\build-windows-desktop.yml = ..\.github\workflows\build-windows-desktop.yml
..\.github\workflows\build-windows.yml = ..\.github\workflows\build-windows.yml ..\.github\workflows\build-windows.yml = ..\.github\workflows\build-windows.yml
..\package-appimage.sh = ..\package-appimage.sh
..\package-debian.sh = ..\package-debian.sh ..\package-debian.sh = ..\package-debian.sh
..\package-osx.sh = ..\package-osx.sh ..\package-osx.sh = ..\package-osx.sh
..\package-release-zip.sh = ..\package-release-zip.sh ..\package-release-zip.sh = ..\package-release-zip.sh
..\pkg2appimage.yml = ..\pkg2appimage.yml
..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml ..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml
EndProjectSection EndProjectSection
EndProject EndProject

View File

@@ -6,11 +6,9 @@
<File Path="../.github/workflows/build-windows-desktop.yml" /> <File Path="../.github/workflows/build-windows-desktop.yml" />
<File Path="../.github/workflows/build-windows.yml" /> <File Path="../.github/workflows/build-windows.yml" />
<File Path="../.github/workflows/winget-publish.yml" /> <File Path="../.github/workflows/winget-publish.yml" />
<File Path="../package-appimage.sh" />
<File Path="../package-debian.sh" /> <File Path="../package-debian.sh" />
<File Path="../package-osx.sh" /> <File Path="../package-osx.sh" />
<File Path="../package-release-zip.sh" /> <File Path="../package-release-zip.sh" />
<File Path="../pkg2appimage.yml" />
</Folder> </Folder>
<Folder Name="/Solution Files/"> <Folder Name="/Solution Files/">
<File Path="Directory.Build.props" /> <File Path="Directory.Build.props" />

View File

@@ -2,9 +2,9 @@ using System.Drawing;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
namespace v2rayN; namespace v2rayN.Common;
public class QRCodeUtils public class QRCodeWindowsUtils
{ {
public static ImageSource? GetQRCode(string? strContent) public static ImageSource? GetQRCode(string? strContent)
{ {
@@ -14,7 +14,7 @@ public class QRCodeUtils
} }
try try
{ {
var qrCodeImage = ServiceLib.Common.QRCodeUtils.GenQRCode(strContent); var qrCodeImage = QRCodeUtils.GenQRCode(strContent);
return qrCodeImage is null ? null : ByteToImage(qrCodeImage); return qrCodeImage is null ? null : ByteToImage(qrCodeImage);
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -31,3 +31,4 @@ global using ServiceLib.Manager;
global using ServiceLib.Models; global using ServiceLib.Models;
global using ServiceLib.Resx; global using ServiceLib.Resx;
global using ServiceLib.ViewModels; global using ServiceLib.ViewModels;
global using v2rayN.Common;

View File

@@ -328,7 +328,7 @@ public partial class MainWindow
if (Application.Current?.MainWindow is Window window) if (Application.Current?.MainWindow is Window window)
{ {
var bytes = QRCodeUtils.CaptureScreen(window); var bytes = QRCodeWindowsUtils.CaptureScreen(window);
await ViewModel?.ScanScreenResult(bytes); await ViewModel?.ScanScreenResult(bytes);
} }

View File

@@ -87,6 +87,15 @@
ToolTip="{x:Static resx:ResUI.menuFastRealPing}"> ToolTip="{x:Static resx:ResUI.menuFastRealPing}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="LightningBolt" /> <materialDesign:PackIcon VerticalAlignment="Center" Kind="LightningBolt" />
</Button> </Button>
<Button
x:Name="menuMixedTestServer"
Width="30"
Height="30"
Margin="{StaticResource MarginLeftRight4}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuMixedTestServer}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Speedometer" />
</Button>
</WrapPanel> </WrapPanel>
<DataGrid <DataGrid
x:Name="lstProfiles" x:Name="lstProfiles"
@@ -131,11 +140,11 @@
x:Name="menuRemoveDuplicateServer" x:Name="menuRemoveDuplicateServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" /> Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<Separator />
<MenuItem <MenuItem
x:Name="menuMixedTestServer" x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMixedTestServer}" /> Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator />
<MenuItem <MenuItem
x:Name="menuTcpingServer" x:Name="menuTcpingServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -148,16 +157,10 @@
x:Name="menuSpeedServer" x:Name="menuSpeedServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSpeedServer}" /> Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}"> <MenuItem
<MenuItem x:Name="menuSortServerResult"
x:Name="menuSortServerResult" Height="{StaticResource MenuItemHeight}"
Height="{StaticResource MenuItemHeight}" Header="{x:Static resx:ResUI.menuSortServerResult}" />
Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem
x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<Separator /> <Separator />
<MenuItem <MenuItem
x:Name="menuMoveToGroup" x:Name="menuMoveToGroup"

View File

@@ -170,7 +170,7 @@ public partial class ProfilesView
public async void ShareServer(string url) public async void ShareServer(string url)
{ {
var img = QRCodeUtils.GetQRCode(url); var img = QRCodeWindowsUtils.GetQRCode(url);
var dialog = new QrcodeView() var dialog = new QrcodeView()
{ {
imgQrcode = { Source = img }, imgQrcode = { Source = img },

View File

@@ -64,7 +64,7 @@ public partial class SubSettingWindow
{ {
return; return;
} }
var img = QRCodeUtils.GetQRCode(url); var img = QRCodeWindowsUtils.GetQRCode(url);
var dialog = new QrcodeView() var dialog = new QrcodeView()
{ {
imgQrcode = { Source = img }, imgQrcode = { Source = img },