Compare commits

..

28 Commits

Author SHA1 Message Date
2dust
d727ff40bb Code clean 2025-10-31 20:25:54 +08:00
2dust
1b5069a933 Code clean 2025-10-31 20:25:50 +08:00
2dust
18ea6fdc00 Code clean 2025-10-31 20:25:45 +08:00
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
DHR60
d3b1810eab Update Directory.Packages.props (#8191) 2025-10-25 10:27:31 +08:00
JieXu
51409a3e28 Add French support | Ajouter le support du français | 添加法语支持 (#8186)
* Add files via upload

* Add files via upload

* Update Global.cs

* Add French resource file to project

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Update ResUI.fr.resx

* Delete v2rayN/AmazTool/Resx/Resource.fr.resx
2025-10-24 19:38:40 +08:00
2dust
1a0f50a41e up 7.15.6
Some checks failed
release Linux / build (Release) (push) Has been cancelled
release Windows / build (Release) (push) Has been cancelled
release Windows desktop (Avalonia UI) / build (Release) (push) Has been cancelled
release macOS / build (Release) (push) Has been cancelled
2025-10-24 14:22:35 +08:00
2dust
83d4a9c18e Update Directory.Packages.props 2025-10-24 14:22:05 +08:00
2dust
b4c20e7b81 Bug fix
https://github.com/2dust/v2rayN/discussions/8168
2025-10-23 19:03:16 +08:00
DHR60
7c76308c93 Fix (#8180) 2025-10-23 17:58:02 +08:00
DHR60
f28fa31c14 Fix tcp dns (#8179) 2025-10-23 17:57:47 +08:00
DHR60
bbedc4dbb1 Fix (#8175) 2025-10-23 09:10:21 +08:00
DHR60
ecf42cb85d Fix dns (#8174) 2025-10-23 09:09:26 +08:00
2dust
e4701d6703 Add one-click test of real connection delay 2025-10-22 19:54:24 +08:00
mlds23
54a47d00a3 更新繁體中文翻譯 (#8166) 2025-10-22 17:00:19 +08:00
DHR60
964572817b Fix (#8161) 2025-10-22 09:07:53 +08:00
96 changed files with 2257 additions and 553 deletions

View File

@@ -9,6 +9,12 @@ on:
push:
branches:
- master
tags:
- 'v*'
- 'V*'
permissions:
contents: write
env:
OutputArch: "linux-64"
@@ -21,7 +27,6 @@ jobs:
strategy:
matrix:
configuration: [Release]
runs-on: ubuntu-24.04
steps:
@@ -31,21 +36,21 @@ jobs:
submodules: 'recursive'
fetch-depth: '0'
- name: Setup
- name: Setup .NET
uses: actions/setup-dotnet@v5.0.0
with:
dotnet-version: '8.0.x'
- name: Build
run: |
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-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-arm64 --self-contained=true -p:PublishTrimmed=true -o $OutputPathArm64
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-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-arm64 --self-contained=true -p:PublishTrimmed=true -o "$OutputPathArm64"
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-linux
path: |
@@ -56,8 +61,8 @@ jobs:
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-debian.sh
./package-debian.sh $OutputArch $OutputPath64 ${{ github.event.inputs.release_tag }}
./package-debian.sh $OutputArchArm $OutputPathArm64 ${{ 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 }}"
- name: Upload deb to release
uses: svenstaro/upload-release-action@v2
@@ -68,28 +73,13 @@ jobs:
file_glob: 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
- name: Package release zip archive
if: github.event.inputs.release_tag != ''
run: |
chmod 755 package-release-zip.sh
./package-release-zip.sh $OutputArch $OutputPath64
./package-release-zip.sh $OutputArchArm $OutputPathArm64
./package-release-zip.sh "$OutputArch" "$OutputPath64"
./package-release-zip.sh "$OutputArchArm" "$OutputPathArm64"
- name: Upload zip archive to release
uses: svenstaro/upload-release-action@v2
@@ -100,36 +90,62 @@ jobs:
file_glob: true
prerelease: true
# release RHEL package
- name: Package RPM (RHEL-family)
if: github.event.inputs.release_tag != ''
rpm:
needs: build
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: |
chmod 755 package-rhel.sh
# Build for both x86_64 and aarch64 in one go (explicit version passed; no --buildfrom)
./package-rhel.sh "${{ github.event.inputs.release_tag }}" --arch all
dnf -y makecache
dnf -y install epel-release
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
if: github.event.inputs.release_tag != ''
run: |
mkdir -p "${{ github.workspace }}/dist/rpm"
rsync -av "$HOME/rpmbuild/RPMS/" "${{ github.workspace }}/dist/rpm/"
# Rename to requested filenames
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
mkdir -p "$GITHUB_WORKSPACE/dist/rpm"
rsync -av "$HOME/rpmbuild/RPMS/" "$GITHUB_WORKSPACE/dist/rpm/" || true
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*.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
if: github.event.inputs.release_tag != ''
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-rpm
path: |
${{ github.workspace }}/dist/rpm/**/*.rpm
path: dist/rpm/**/*.rpm
- name: Upload RPMs to release
uses: svenstaro/upload-release-action@v2
if: github.event.inputs.release_tag != ''
with:
file: ${{ github.workspace }}/dist/rpm/**/*.rpm
tag: ${{ github.event.inputs.release_tag }}
file: dist/rpm/**/*.rpm
tag: ${{ env.RELEASE_TAG }}
file_glob: 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
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-macos
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
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-windows-desktop
path: |

View File

@@ -46,7 +46,7 @@ jobs:
- name: Upload build artifacts
uses: actions/upload-artifact@v4.6.2
uses: actions/upload-artifact@v5.0.0
with:
name: v2rayN-windows
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
Architecture: $Arch2
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
EOF

View File

@@ -19,6 +19,23 @@ else
exit 1
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 =========================================================
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
WITH_CORE="both" # Default: bundle both xray+sing-box
@@ -614,8 +631,13 @@ ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz
# Runtime dependencies (Avalonia / X11 / Fonts / GL)
Requires: libX11, libXrandr, libXcursor, libXi, libXext, libxcb, libXrender, libXfixes, libXinerama, libxkbcommon
Requires: fontconfig, freetype, cairo, pango, mesa-libEGL, mesa-libGL, xdg-utils
Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL
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
v2rayN Linux for Red Hat Enterprise Linux

View File

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

View File

@@ -6,19 +6,19 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.AvaloniaEdit" Version="11.3.0" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.7" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.7" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.7" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.3.7" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.3.8" />
<PackageVersion Include="Avalonia.Desktop" Version="11.3.8" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.3.8" />
<PackageVersion Include="ReactiveUI.Avalonia" Version="11.3.8" />
<PackageVersion Include="CliWrap" Version="3.9.0" />
<PackageVersion Include="Downloader" Version="4.0.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.1" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.2" />
<PackageVersion Include="MaterialDesignThemes" Version="5.3.0" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.7.0" />
<PackageVersion Include="ReactiveUI" Version="20.4.1" />
<PackageVersion Include="ReactiveUI" Version="22.2.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.4.1" />
<PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" />

View File

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

View File

@@ -424,7 +424,7 @@ public class Utils
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
if (authority.StartsWith("[") && authority.Contains("]"))
{
int closingBracketIndex = authority.LastIndexOf(']');
var closingBracketIndex = authority.LastIndexOf(']');
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')
{
// Port exists
@@ -963,13 +963,13 @@ public class Utils
#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)
{
@@ -994,11 +994,6 @@ public class Utils
return false;
}
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("APPIMAGE")))
{
return true;
}
var exePath = GetExePath();
var baseDir = string.IsNullOrEmpty(exePath) ? StartupPath() : Path.GetDirectoryName(exePath) ?? "";
var p = baseDir.Replace('\\', '/');
@@ -1008,11 +1003,6 @@ public class Utils
return false;
}
if (p.Contains("/.mount_", StringComparison.Ordinal))
{
return true;
}
if (p.StartsWith("/opt/v2rayN", StringComparison.OrdinalIgnoreCase))
{
return true;

View File

@@ -1,6 +1,3 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace ServiceLib.Common;
/*
* See:

View File

@@ -5,5 +5,6 @@ public enum ESpeedActionType
Tcping,
Realping,
Speedtest,
Mixedtest
Mixedtest,
FastRealping
}

View File

@@ -427,6 +427,7 @@ public class Global
"zh-Hant",
"en",
"fa-Ir",
"fr",
"ru",
"hu"
];

View File

@@ -447,13 +447,13 @@ public static class ConfigHandler
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> MoveServer(Config config, List<ProfileItem> lstProfile, int index, EMove eMove, int pos = -1)
{
int count = lstProfile.Count;
var count = lstProfile.Count;
if (index < 0 || index > lstProfile.Count - 1)
{
return -1;
}
for (int i = 0; i < lstProfile.Count; i++)
for (var i = 0; i < lstProfile.Count; i++)
{
ProfileExManager.Instance.SetSort(lstProfile[i].IndexId, (i + 1) * 10);
}
@@ -527,7 +527,7 @@ public static class ConfigHandler
return -1;
}
var ext = Path.GetExtension(fileName);
string newFileName = $"{Utils.GetGuid()}{ext}";
var newFileName = $"{Utils.GetGuid()}{ext}";
//newFileName = Path.Combine(Utile.GetTempPath(), newFileName);
try
@@ -1221,6 +1221,7 @@ public static class ConfigHandler
CoreType = coreType,
ConfigType = EConfigType.PolicyGroup,
Remarks = remark,
IsSub = false
};
if (!subId.IsNullOrEmpty())
{
@@ -1355,7 +1356,7 @@ public static class ConfigHandler
}
continue;
}
var profileItem = FmtHandler.ResolveConfig(str, out string msg);
var profileItem = FmtHandler.ResolveConfig(str, out var msg);
if (profileItem is null)
{
continue;
@@ -1439,7 +1440,7 @@ public static class ConfigHandler
{
await RemoveServersViaSubid(config, subid, isSub);
}
int count = 0;
var count = 0;
foreach (var it in lstProfiles)
{
it.Subid = subid;
@@ -1529,7 +1530,7 @@ public static class ConfigHandler
var lstSsServer = ShadowsocksFmt.ResolveSip008(strData);
if (lstSsServer?.Count > 0)
{
int counter = 0;
var counter = 0;
foreach (var ssItem in lstSsServer)
{
ssItem.Subid = subid;
@@ -1704,7 +1705,7 @@ public static class ConfigHandler
var maxSort = 0;
if (await SQLiteHelper.Instance.TableAsync<SubItem>().CountAsync() > 0)
{
var lstSubs = (await AppManager.Instance.SubItems());
var lstSubs = await AppManager.Instance.SubItems();
maxSort = lstSubs.LastOrDefault()?.Sort ?? 0;
}
item.Sort = maxSort + 1;
@@ -1866,7 +1867,7 @@ public static class ConfigHandler
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> MoveRoutingRule(List<RulesItem> rules, int index, EMove eMove, int pos = -1)
{
int count = rules.Count;
var count = rules.Count;
if (index < 0 || index > rules.Count - 1)
{
return -1;

View File

@@ -58,7 +58,7 @@ public static class CoreConfigHandler
File.Delete(fileName);
}
string addressFileName = node.Address;
var addressFileName = node.Address;
if (!File.Exists(addressFileName))
{
addressFileName = Utils.GetConfigPath(addressFileName);

View File

@@ -37,7 +37,7 @@ public class FmtHandler
try
{
string str = config.TrimEx();
var str = config.TrimEx();
if (str.IsNullOrEmpty())
{
msg = ResUI.FailedReadConfiguration;

View File

@@ -33,9 +33,9 @@ public class Hysteria2Fmt : BaseFmt
{
if (item == null)
return null;
string url = string.Empty;
var url = string.Empty;
string remark = string.Empty;
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);

View File

@@ -85,7 +85,7 @@ public class ActionPrecheckManager(Config config)
break;
case EConfigType.VLESS:
if (item.Id.IsNullOrEmpty() || !Utils.IsGuidByParse(item.Id) && item.Id.Length > 30)
if (item.Id.IsNullOrEmpty() || (!Utils.IsGuidByParse(item.Id) && item.Id.Length > 30))
errors.Add(string.Format(ResUI.InvalidProperty, "Id"));
if (!Global.Flows.Contains(item.Flow))
errors.Add(string.Format(ResUI.InvalidProperty, "Flow"));

View File

@@ -34,7 +34,7 @@ public class CoreAdminManager
StringBuilder sb = new();
sb.AppendLine("#!/bin/bash");
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 procService = new ProcessService(

View File

@@ -28,7 +28,7 @@ public class ProfileItem : ReactiveObject
public string GetSummary()
{
var summary = $"[{(ConfigType).ToString()}] ";
var summary = $"[{ConfigType.ToString()}] ";
if (IsComplex())
{
summary += $"[{CoreType.ToString()}]{Remarks}";

View File

@@ -215,6 +215,7 @@ public class Dns4Ray
public class DnsServer4Ray
{
public string? address { get; set; }
public int? port { get; set; }
public List<string>? domains { get; set; }
public bool? skipFallback { get; set; }
public List<string>? expectedIPs { get; set; }

View File

@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "18.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI {
@@ -1023,6 +1023,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Test real delay 的本地化字符串。
/// </summary>
public static string menuFastRealPing {
get {
return ResourceManager.GetString("menuFastRealPing", resourceCulture);
}
}
/// <summary>
/// 查找类似 Full Config Template Setting 的本地化字符串。
/// </summary>

View File

@@ -1596,4 +1596,7 @@
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

File diff suppressed because it is too large Load Diff

View File

@@ -1596,4 +1596,7 @@
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -1596,4 +1596,7 @@
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -1596,4 +1596,7 @@
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>Test real delay</value>
</data>
</root>

View File

@@ -427,7 +427,7 @@
<value>路由设置</value>
</data>
<data name="menuServers" xml:space="preserve">
<value>配置文件</value>
<value>配置</value>
</data>
<data name="menuSetting" xml:space="preserve">
<value>设置</value>
@@ -1593,4 +1593,7 @@
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>解析 DNS 服务器域名,需指定为 IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>一键测试真连接延迟</value>
</data>
</root>

View File

@@ -121,7 +121,7 @@
<value>匯出分享連結至剪貼簿成功</value>
</data>
<data name="CheckServerSettings" xml:space="preserve">
<value>請先檢查設定檔設定</value>
<value>請先檢查設定</value>
</data>
<data name="ConfigurationFormatIncorrect" xml:space="preserve">
<value>設定格式不正確</value>
@@ -133,7 +133,7 @@
<value>下載開始...</value>
</data>
<data name="FailedConversionConfiguration" xml:space="preserve">
<value>轉換設定失敗</value>
<value>轉換設定失敗</value>
</data>
<data name="FailedGenDefaultConfiguration" xml:space="preserve">
<value>生成預設設定檔失敗</value>
@@ -142,10 +142,10 @@
<value>獲取預設設定失敗</value>
</data>
<data name="FailedImportedCustomServer" xml:space="preserve">
<value>匯入自訂設定設定檔失敗</value>
<value>匯入自訂設定失敗</value>
</data>
<data name="FailedReadConfiguration" xml:space="preserve">
<value>讀取設定失敗</value>
<value>讀取設定失敗</value>
</data>
<data name="FillCorrectServerPort" xml:space="preserve">
<value>請填寫正確格式的埠</value>
@@ -265,13 +265,13 @@
<value>請選擇協定</value>
</data>
<data name="PleaseSelectServer" xml:space="preserve">
<value>請先選擇設定</value>
<value>請先選擇設定</value>
</data>
<data name="RemoveDuplicateServerResult" xml:space="preserve">
<value>設定檔去重完成。原數量: {0},現數量: {1}。</value>
<value>去重完成。原數量: {0},現數量: {1}。</value>
</data>
<data name="RemoveServer" xml:space="preserve">
<value>是否確定移除設定檔</value>
<value>是否確定移除?</value>
</data>
<data name="SaveClientConfigurationIn" xml:space="preserve">
<value>用戶端設定檔儲存在:{0}</value>
@@ -283,10 +283,10 @@
<value>設定成功。{0}</value>
</data>
<data name="SuccessfullyImportedCustomServer" xml:space="preserve">
<value>成功匯入自訂設定設定檔</value>
<value>成功匯入自訂節點</value>
</data>
<data name="SuccessfullyImportedServerViaClipboard" xml:space="preserve">
<value>成功從剪貼簿匯入 {0} 個設定檔</value>
<value>成功從剪貼簿匯入 {0} 個節點</value>
</data>
<data name="SuccessfullyImportedServerViaScan" xml:space="preserve">
<value>掃描匯入分享連結成功</value>
@@ -385,7 +385,7 @@
<value>所有</value>
</data>
<data name="FillServerAddressCustom" xml:space="preserve">
<value>請瀏覽匯入設定檔設定</value>
<value>請瀏覽匯入設定</value>
</data>
<data name="Speedtesting" xml:space="preserve">
<value>測試中...</value>
@@ -397,7 +397,7 @@
<value>本機</value>
</data>
<data name="MsgServerTitle" xml:space="preserve">
<value>設定檔過濾,按 Enter 執行</value>
<value>過濾,按 Enter 執行</value>
</data>
<data name="menuCheckUpdate" xml:space="preserve">
<value>檢查更新</value>
@@ -478,55 +478,55 @@
<value>掃描螢幕上的二維碼 (Ctrl+S)</value>
</data>
<data name="menuCopyServer" xml:space="preserve">
<value>複製所選設定檔</value>
<value>複製所選</value>
</data>
<data name="menuRemoveDuplicateServer" xml:space="preserve">
<value>移除重複的設定檔</value>
<value>移除重複</value>
</data>
<data name="menuRemoveServer" xml:space="preserve">
<value>移除所選設定檔 (多選) (Delete)</value>
<value>移除所選 (多選) (Delete)</value>
</data>
<data name="menuSetDefaultServer" xml:space="preserve">
<value>設為活動設定檔 (Enter)</value>
<value>設為活動 (Enter)</value>
</data>
<data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服務統計資料</value>
</data>
<data name="menuRealPingServer" xml:space="preserve">
<value>測試設定檔真連線延遲 (多選) (Ctrl+R)</value>
<value>測試真連線延遲 (多選) (Ctrl+R)</value>
</data>
<data name="menuSortServerResult" xml:space="preserve">
<value>按測試結果排序</value>
</data>
<data name="menuSpeedServer" xml:space="preserve">
<value>測試設定檔速度 (多選) (Ctrl+T)</value>
<value>測試速度 (多選) (Ctrl+T)</value>
</data>
<data name="menuTcpingServer" xml:space="preserve">
<value>測試設定檔延遲 Tcping (多選) (Ctrl+O)</value>
<value>測試延遲 Tcping (多選) (Ctrl+O)</value>
</data>
<data name="menuExport2ClientConfig" xml:space="preserve">
<value>匯出所選設定檔完整設定</value>
<value>匯出所選完整設定</value>
</data>
<data name="menuExport2ShareUrl" xml:space="preserve">
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value>
</data>
<data name="menuAddCustomServer" xml:space="preserve">
<value>新增自訂設定設定檔</value>
<value>新增自訂節點</value>
</data>
<data name="menuAddShadowsocksServer" xml:space="preserve">
<value>新增 [Shadowsocks] 設定檔</value>
<value>新增 [Shadowsocks] 節點</value>
</data>
<data name="menuAddSocksServer" xml:space="preserve">
<value>新增 [SOCKS] 設定檔</value>
<value>新增 [SOCKS] 節點</value>
</data>
<data name="menuAddTrojanServer" xml:space="preserve">
<value>新增 [Trojan] 設定檔</value>
<value>新增 [Trojan] 節點</value>
</data>
<data name="menuAddVlessServer" xml:space="preserve">
<value>新增 [VLESS] 設定檔</value>
<value>新增 [VLESS] 節點</value>
</data>
<data name="menuAddVmessServer" xml:space="preserve">
<value>新增 [VMess] 設定檔</value>
<value>新增 [VMess] 節點</value>
</data>
<data name="menuSelectAll" xml:space="preserve">
<value>全選 (Ctrl+A)</value>
@@ -676,7 +676,7 @@
<value>Core: 基礎設定</value>
</data>
<data name="TbCustomDnsRay" xml:space="preserve">
<value>V2ray Custom DNS</value>
<value>v2ray 自訂 DNS</value>
</data>
<data name="TbSettingsCoreKcp" xml:space="preserve">
<value>Core: KCP 設定</value>
@@ -691,7 +691,7 @@
<value>Outbound Freedom domainStrategy</value>
</data>
<data name="TbSettingsEnableAutoAdjustMainLvColWidth" xml:space="preserve">
<value>在更新訂閱後自動調整設定檔列寬</value>
<value>在更新訂閱後自動調整列寬</value>
</data>
<data name="TbSettingsEnableCheckPreReleaseUpdate" xml:space="preserve">
<value>檢查 Pre-Release 更新 (請謹慎啟用)</value>
@@ -700,7 +700,7 @@
<value>例外</value>
</data>
<data name="TbSettingsExceptionTip" xml:space="preserve">
<value>例外:對於下列字元開頭的位址,不使用代理設定檔。使用分號 (;) 分隔。</value>
<value>例外:對於下列字元開頭的位址,不使用代理。使用分號 (;) 分隔。</value>
</data>
<data name="TbSettingsDisplayRealTimeSpeed" xml:space="preserve">
<value>顯示即時速度(需重啟)</value>
@@ -748,7 +748,7 @@
<value>系統代理設定</value>
</data>
<data name="TbSettingsTrayMenuServersLimit" xml:space="preserve">
<value>工具列右鍵選單設定展示數量限制</value>
<value>工具列右鍵選單設定展示數量限制</value>
</data>
<data name="TbSettingsUdpEnabled" xml:space="preserve">
<value>開啟 UDP</value>
@@ -781,7 +781,7 @@
<value>PAC 模式</value>
</data>
<data name="menuShareServer" xml:space="preserve">
<value>分享設定檔 (Ctrl+F)</value>
<value>分享 (Ctrl+F)</value>
</data>
<data name="menuRouting" xml:space="preserve">
<value>路由</value>
@@ -883,7 +883,7 @@
<value>請勿將代理伺服器用於本機Intranet位址</value>
</data>
<data name="menuMixedTestServer" xml:space="preserve">
<value>一鍵多執行緒測試延遲速度 (Ctrl+E)</value>
<value>一鍵延遲速度測試 (Ctrl+E)</value>
</data>
<data name="LvTestDelay" xml:space="preserve">
<value>延遲 (ms)</value>
@@ -913,7 +913,7 @@
<value>移至訂閱分組</value>
</data>
<data name="TbSettingsEnableDragDropSort" xml:space="preserve">
<value>啟動設定檔拖放排序 (需重啟)</value>
<value>啟拖放排序 (需重啟)</value>
</data>
<data name="TbAutoRefresh" xml:space="preserve">
<value>自動重新整理</value>
@@ -922,10 +922,10 @@
<value>跳過測試</value>
</data>
<data name="menuEditServer" xml:space="preserve">
<value>編輯設定檔 (Ctrl+D)</value>
<value>編輯 (Ctrl+D)</value>
</data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主介面輕按兩下設為活動設定檔</value>
<value>主介面輕按兩下設為活動</value>
</data>
<data name="SpeedtestingCompleted" xml:space="preserve">
<value>測試完成</value>
@@ -943,7 +943,7 @@
<value>目前字型 (需重啟)</value>
</data>
<data name="TbSettingsCurrentFontFamilyTip" xml:space="preserve">
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts啟設定</value>
<value>複製字型 TTF/TTC 檔案到目錄 guiFonts新啟動後生效</value>
</data>
<data name="TbSettingsSocksPortTip" xml:space="preserve">
<value>Pac 連接埠 = +3Xray API 連接埠 = +4mihomo API 連接埠 = +5</value>
@@ -1003,10 +1003,10 @@
<value>不需要轉換時請留空</value>
</data>
<data name="menuDNSSetting" xml:space="preserve">
<value>DNS 設定</value>
<value>DNS設定</value>
</data>
<data name="TbCustomDnsSingbox" xml:space="preserve">
<value>sing-box Custom DNS</value>
<value>sing-box 自訂 DNS</value>
</data>
<data name="TbDnsSingboxObjectDoc" xml:space="preserve">
<value>請填寫 DNS JSON 結構,點擊查看檔案</value>
@@ -1030,7 +1030,7 @@
<value>Domain</value>
</data>
<data name="menuAddHysteria2Server" xml:space="preserve">
<value>添加 [Hysteria2] 設定檔</value>
<value>新增 [Hysteria2] 節點</value>
</data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria 最大頻寬 (Up/Dw)</value>
@@ -1039,19 +1039,19 @@
<value>使用系統 hosts</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>新增 [TUIC] 設定檔</value>
<value>新增 [TUIC] 節點</value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>擁塞控制算法</value>
</data>
<data name="LvPrevProfile" xml:space="preserve">
<value>前置代理設定檔別名</value>
<value>前置代理節點別名</value>
</data>
<data name="LvNextProfile" xml:space="preserve">
<value>落地代理設定檔別名</value>
<value>落地代理節點別名</value>
</data>
<data name="LvPrevProfileTip" xml:space="preserve">
<value>請確保設定檔別名存在並且唯一</value>
<value>請確保節點別名存在並且唯一</value>
</data>
<data name="TbSettingsTunAutoRoute" xml:space="preserve">
<value>自動路由</value>
@@ -1072,7 +1072,7 @@
<value>啟用 IPv6</value>
</data>
<data name="menuAddWireguardServer" xml:space="preserve">
<value>添加 [WireGuard] 設定檔</value>
<value>新增 [WireGuard] 節點</value>
</data>
<data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value>
@@ -1105,7 +1105,7 @@
<value>*grpc Authority</value>
</data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>新增 [HTTP] 設定檔</value>
<value>新增 [HTTP] 節點</value>
</data>
<data name="TbSettingsEnableFragmentTips" xml:space="preserve">
<value>和分組前置代理衝突</value>
@@ -1219,13 +1219,13 @@
<value>匯出分享連結至剪貼簿 (多選) Base64 編碼</value>
</data>
<data name="menuExport2ClientConfigClipboard" xml:space="preserve">
<value>匯出所選設定檔完整設定至剪貼簿</value>
<value>匯出所選完整設定至剪貼簿</value>
</data>
<data name="menuShowOrHideMainWindow" xml:space="preserve">
<value>顯示或隱藏主介面</value>
</data>
<data name="TbPreSocksPort4Sub" xml:space="preserve">
<value>自訂設定的 Socks 連接埠</value>
<value>自訂 Socks 連接埠</value>
</data>
<data name="menuBackupAndRestore" xml:space="preserve">
<value>備份和還原</value>
@@ -1309,7 +1309,7 @@
<value>請不要使用不安全的 HTTP 協定訂閱位址</value>
</data>
<data name="TbSettingsCurrentFontFamilyLinuxTip" xml:space="preserve">
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動設定</value>
<value>安裝字體到系統中,選擇或填入字體名稱,重新啟動後生效</value>
</data>
<data name="menuExitTips" xml:space="preserve">
<value>是否確定退出?</value>
@@ -1336,7 +1336,7 @@
<value>多執行緒測試時的並發數量</value>
</data>
<data name="TbSettingsExceptionTip2" xml:space="preserve">
<value>例外:對於下列位址不使用代理設定檔,使用逗號 (,) 分隔。</value>
<value>例外:對於下列位址不使用代理,使用逗號 (,) 分隔。</value>
</data>
<data name="TbSettingsDestOverride" xml:space="preserve">
<value>流量探測類型</value>
@@ -1372,31 +1372,31 @@
<value>會覆蓋埠,多組時用逗號 (,) 隔開</value>
</data>
<data name="menuGenGroupMultipleServer" xml:space="preserve">
<value>Generate Policy Group from Multiple Profiles</value>
<value>多選生成策略組</value>
</data>
<data name="menuGenGroupMultipleServerXrayRandom" xml:space="preserve">
<value>多設定檔隨機 Xray</value>
<value>多隨機 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayRoundRobin" xml:space="preserve">
<value>多設定檔負載平衡 Xray</value>
<value>多負載平衡 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastPing" xml:space="preserve">
<value>多設定檔最低延遲 Xray</value>
<value>多最低延遲 Xray</value>
</data>
<data name="menuGenGroupMultipleServerXrayLeastLoad" xml:space="preserve">
<value>多設定檔最穩定 Xray</value>
<value>多最穩定 Xray</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxLeastPing" xml:space="preserve">
<value>多設定檔最低延遲 sing-box</value>
<value>多最低延遲 sing-box</value>
</data>
<data name="menuExportConfig" xml:space="preserve">
<value>匯出設定檔</value>
<value>匯出</value>
</data>
<data name="TbSettingsIPAPIUrl" xml:space="preserve">
<value>目前連接資訊測試地址</value>
</data>
<data name="TbRuleOutboundTagTip" xml:space="preserve">
<value>可以填寫設定檔別名,請確保存在並唯一</value>
<value>可以填寫節點別名,請確保存在並唯一</value>
</data>
<data name="SudoIncorrectPasswordTip" xml:space="preserve">
<value>密碼錯誤,請重試。</value>
@@ -1405,192 +1405,195 @@
<value>Mldsa65Verify</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>新增 [Anytls] 設定檔</value>
<value>新增 [Anytls] 節點</value>
</data>
<data name="TbRemoteDNS" xml:space="preserve">
<value>Remote DNS</value>
<value>遠程 DNS</value>
</data>
<data name="TbDomesticDNS" xml:space="preserve">
<value>Domestic DNS</value>
<value>直連 DNS</value>
</data>
<data name="TbRemoteDNSTips" xml:space="preserve">
<value>Via proxy — please ensure remote availability</value>
<value>通过代理,请确保远程可用</value>
</data>
<data name="TbXrayFreedomStrategy" xml:space="preserve">
<value>xray Freedom Resolution Strategy</value>
<value>xray freedom 解析策略</value>
</data>
<data name="TbSBDirectResolveStrategy" xml:space="preserve">
<value>sing-box Direct Resolution Strategy</value>
<value>sing-box 直連解析策略</value>
</data>
<data name="TbSBRemoteResolveStrategy" xml:space="preserve">
<value>sing-box Remote Resolution Strategy</value>
<value>sing-box 遠程解析策略</value>
</data>
<data name="TbAddCommonDNSHosts" xml:space="preserve">
<value>Add Common DNS Hosts</value>
<value>新增常用 DNS Hosts</value>
</data>
<data name="TbFakeIP" xml:space="preserve">
<value>FakeIP</value>
</data>
<data name="TbBlockSVCBHTTPSQueries" xml:space="preserve">
<value>Block SVCB and HTTPS Queries</value>
<value>阻止 SVCB HTTPS 查詢</value>
</data>
<data name="TbDNSHostsConfig" xml:space="preserve">
<value>DNS Hosts: ("domain1 ip1 ip2" per line)</value>
<value>DNS Hosts“網域名稱1 ip1 ip2” 一行一個)</value>
</data>
<data name="ThBasicDNSSettings" xml:space="preserve">
<value>Basic DNS Settings</value>
<value>DNS 基礎設定</value>
</data>
<data name="ThAdvancedDNSSettings" xml:space="preserve">
<value>Advanced DNS Settings</value>
<value>DNS 進階設定</value>
</data>
<data name="TbValidateDirectExpectedIPs" xml:space="preserve">
<value>Validate Regional Domain IPs</value>
<value>校驗相應地區域名 IP</value>
</data>
<data name="TbValidateDirectExpectedIPsDesc" xml:space="preserve">
<value>When configured, validates IPs returned for regional domains (e.g., geosite:cn), returning only expected IPs</value>
<value>配置後,會對相應地區域名(如 geosite:cn的返回 IP 進行校驗,僅返回期望 IP</value>
</data>
<data name="TbCustomDNSEnable" xml:space="preserve">
<value>Enable Custom DNS</value>
<value>啟用自訂 DNS</value>
</data>
<data name="TbCustomDNSEnabledPageInvalid" xml:space="preserve">
<value>Custom DNS Enabled, This Page's Settings Invalid</value>
<value>自訂 DNS 已啟用,此頁面配置將無效</value>
</data>
<data name="TbBlockSVCBHTTPSQueriesTips" xml:space="preserve">
<value>Block ECH and HTTP/3 availability checks when enabled</value>
<value>開啟後將阻止 ECH HTTP/3 可用性查詢</value>
</data>
<data name="FillCorrectConfigTemplateText" xml:space="preserve">
<value>Please fill in the correct config template</value>
<value>請填寫正確的配置範本</value>
</data>
<data name="menuFullConfigTemplate" xml:space="preserve">
<value>Full Config Template Setting</value>
<value>完整配置範本設定</value>
</data>
<data name="TbFullConfigTemplateEnable" xml:space="preserve">
<value>Enable Full Config Template</value>
<value>啟用完整配置範本</value>
</data>
<data name="TbRayFullConfigTemplate" xml:space="preserve">
<value>v2ray Full Config Template</value>
<value>v2ray 完整配置範本</value>
</data>
<data name="TbRayFullConfigTemplateDesc" xml:space="preserve">
<value>Add Outbound Config Only, routing.balancers and routing.rules.outboundTag, Click to view the document</value>
<value>僅添加出站配置,routing.balancers routing.rules.outboundTag,點擊查看文檔</value>
</data>
<data name="TbAddProxyProtocolOutboundOnly" xml:space="preserve">
<value>Do Not Add Non-Proxy Protocol Outbound</value>
<value>不添加非代理協定出站</value>
</data>
<data name="TbSetUpstreamProxyDetour" xml:space="preserve">
<value>Set Upstream Proxy Tag</value>
<value>設定上游代理 tag</value>
</data>
<data name="TbSBFullConfigTemplate" xml:space="preserve">
<value>sing-box Full Config Template</value>
<value>sing-box 完整配置範本</value>
</data>
<data name="TbSBFullConfigTemplateDesc" xml:space="preserve">
<value>Add Outbound and Endpoint Config Only, Click to view the document</value>
<value>僅添加出站和端點配置,點擊查看文檔</value>
</data>
<data name="TbFullConfigTemplateDesc" xml:space="preserve">
<value>This feature is intended for advanced users and those with special requirements. Once enabled, it will ignore the Core's basic settings, DNS settings, and routing settings. You must ensure that the system proxy port, traffic statistics, and other related configurations are set correctly — everything will be configured by you.</value>
<value>此功能供高級用戶和有特殊需求的用戶使用。 啟用此功能後,將忽略 Core 的基礎設定DNS 設定 ,路由設定。你需要保證系統代理的埠和流量統計等功能的配置正確,一切都由你來設定。</value>
</data>
<data name="MsgStartParsingSubscription" xml:space="preserve">
<value>開始解析和處理訂閱內容</value>
</data>
<data name="TbSelectProfile" xml:space="preserve">
<value>Select Profile</value>
<value>選擇節點</value>
</data>
<data name="TbFakeIPTips" xml:space="preserve">
<value>Applies globally by default, with built-in FakeIP filtering (sing-box only).</value>
<value>默認全域生效,內置 FakeIP 過濾,僅在 sing-box 中生效</value>
</data>
<data name="PleaseAddAtLeastOneServer" xml:space="preserve">
<value>Please Add At Least One Configuration</value>
<value>請至少添加一個節點</value>
</data>
<data name="TbConfigTypePolicyGroup" xml:space="preserve">
<value>Policy Group</value>
<value>策略組</value>
</data>
<data name="TbConfigTypeProxyChain" xml:space="preserve">
<value>Proxy Chain</value>
<value>鏈式代理</value>
</data>
<data name="TbLeastPing" xml:space="preserve">
<value>Lowest Latency</value>
<value>最低延遲</value>
</data>
<data name="TbRandom" xml:space="preserve">
<value>Random</value>
<value>隨機</value>
</data>
<data name="TbRoundRobin" xml:space="preserve">
<value>Round Robin</value>
<value>負載均衡</value>
</data>
<data name="TbLeastLoad" xml:space="preserve">
<value>Most Stable</value>
<value>最穩定</value>
</data>
<data name="TbPolicyGroupType" xml:space="preserve">
<value>Policy Group Type</value>
<value>策略組類型</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group Configuration</value>
<value>添加策略組</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain Configuration</value>
<value>添加鏈式代理</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child Configuration</value>
<value>添加子項</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child Configuration</value>
<value>刪除子項</value>
</data>
<data name="menuServerList" xml:space="preserve">
<value>Server List</value>
<value>子項清單</value>
</data>
<data name="TbFallback" xml:space="preserve">
<value>Fallback</value>
<value>容錯移轉</value>
</data>
<data name="menuGenGroupMultipleServerSingBoxFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by sing-box</value>
<value>多選容錯移轉 sing-box</value>
</data>
<data name="menuGenGroupMultipleServerXrayFallback" xml:space="preserve">
<value>Multi-Configuration Fallback by Xray</value>
<value>多選容錯移轉 Xray</value>
</data>
<data name="CoreNotSupportNetwork" xml:space="preserve">
<value>Core '{0}' does not support network type '{1}'.</value>
<value>核心 '{0}' 不支援網路類型 '{1}'.</value>
</data>
<data name="CoreNotSupportProtocolTransport" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}' when using transport '{2}'.</value>
<value>核心 '{0}' 在使用傳輸方式 '{2}' 時不支援協定 '{1}'.</value>
</data>
<data name="CoreNotSupportProtocol" xml:space="preserve">
<value>Core '{0}' does not support protocol '{1}'.</value>
<value>核心 '{0}' 不支援協定 '{1}'.</value>
</data>
<data name="ProxyChainedPrefix" xml:space="preserve">
<value>Proxy chained: </value>
<value>代理鏈: </value>
</data>
<data name="RoutingRuleOutboundPrefix" xml:space="preserve">
<value>Routing rule outbound: </value>
<value>路由規則出站: </value>
</data>
<data name="PolicyGroupPrefix" xml:space="preserve">
<value>Policy group: </value>
<value>策略組: </value>
</data>
<data name="NodeTagNotExist" xml:space="preserve">
<value>Node alias '{0}' does not exist.</value>
<value>別名 '{0}' 不存在。</value>
</data>
<data name="GroupEmpty" xml:space="preserve">
<value>Group '{0}' is empty. Please add at least one node.</value>
<value>組“{0}”為空.請至少添加一個配置。</value>
</data>
<data name="InvalidProperty" xml:space="preserve">
<value>The {0} property is invalid, please check.</value>
<value>{0}屬性無效,請檢查</value>
</data>
<data name="GroupSelfReference" xml:space="preserve">
<value>{0} 分組不能引用自身或循環引用</value>
</data>
<data name="NotSupportProtocol" xml:space="preserve">
<value>Not support protocol '{0}'.</value>
<value>不支援協定 '{0}'.</value>
</data>
<data name="TbSettingsHide2TrayWhenCloseTip" xml:space="preserve">
<value>如果系統沒有盤功能,請不要開啟</value>
<value>如果系統沒有盤功能,請不要開啟</value>
</data>
<data name="TbRuleType" xml:space="preserve">
<value>规则类型</value>
<value>規則類型</value>
</data>
<data name="TbRuleTypeTips" xml:space="preserve">
<value>可 Routing 和 DNS 单独设定规则ALL 都生效</value>
<value>可 Routing 和 DNS 單獨設定規則ALL 都生效</value>
</data>
<data name="TbBootstrapDNS" xml:space="preserve">
<value>Bootstrap DNS</value>
</data>
<data name="TbBootstrapDNSTips" xml:space="preserve">
<value>Resolve DNS server domains, requires IP</value>
<value>解析 DNS 伺服器網域名稱,需指定為 IP</value>
</data>
<data name="menuFastRealPing" xml:space="preserve">
<value>一鍵測試真連線延遲</value>
</data>
</root>

View File

@@ -28,15 +28,15 @@ fi
kill_children() {
local parent=$1
local children=$(ps -o pid --no-headers --ppid "$parent")
# Output information about processes being terminated
echo "Processing children of PID: $parent..."
# Process each child
for child in $children; do
# Recursively find and kill child's children first
kill_children "$child"
# Force kill the child process
echo "Terminating child process: $child"
kill -9 "$child" 2>/dev/null || true
@@ -47,6 +47,18 @@ echo "============================================"
echo "Starting termination of process $PID and all its children"
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
kill_children "$PID"

View File

@@ -42,6 +42,20 @@ echo "============================================"
echo "Starting termination of process $PID and all its descendants"
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
kill_descendants "$PID"

View File

@@ -57,6 +57,9 @@
<SubType>Designer</SubType>
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.fr.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
<EmbeddedResource Update="Resx\ResUI.hu.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
</EmbeddedResource>
@@ -79,4 +82,4 @@
</EmbeddedResource>
</ItemGroup>
</Project>
</Project>

View File

@@ -470,14 +470,16 @@ public partial class CoreConfigSingboxService
{
// udp dns
server.type = "udp";
server.server = addressFirst;
return server;
}
else
{
// server.type = scheme.ToLower();
// remove "+local" suffix
// TODO: "+local" suffix decide server.detour = "direct" ?
server.type = scheme.Replace("+local", "", StringComparison.OrdinalIgnoreCase).ToLower();
}
//server.type = scheme.ToLower();
// remove "+local" suffix
// TODO: "+local" suffix decide server.detour = "direct" ?
server.type = scheme.Replace("+local", "", StringComparison.OrdinalIgnoreCase).ToLower();
server.server = domain;
if (port != 0)
{

View File

@@ -294,7 +294,7 @@ public partial class CoreConfigSingboxService
var tls = new Tls4Sbox()
{
enabled = true,
record_fragment = _config.CoreBasicItem.EnableFragment,
record_fragment = _config.CoreBasicItem.EnableFragment ? true : null,
server_name = server_name,
insecure = Utils.ToBool(node.AllowInsecure.IsNullOrEmpty() ? _config.CoreBasicItem.DefAllowInsecure.ToString().ToLower() : node.AllowInsecure),
alpn = node.GetAlpn(),

View File

@@ -94,8 +94,8 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;
@@ -200,8 +200,8 @@ public partial class CoreConfigV2rayService(Config config)
ret.Msg = ResUI.InitialConfiguration;
string result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
string txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
var result = EmbedUtils.GetEmbedText(Global.V2raySampleClient);
var txtOutbound = EmbedUtils.GetEmbedText(Global.V2raySampleOutbound);
if (result.IsNullOrEmpty() || txtOutbound.IsNullOrEmpty())
{
ret.Msg = ResUI.FailedGetDefaultConfiguration;

View File

@@ -86,8 +86,8 @@ public partial class CoreConfigV2rayService
}
}
// Handle outbounds - append instead of override
var customOutboundsNode = fullConfigTemplateNode["outbounds"] is JsonArray outbounds ? outbounds : new JsonArray();
var customOutboundsNode = new JsonArray();
foreach (var outbound in v2rayConfig.outbounds)
{
if (outbound.protocol.ToLower() is "blackhole" or "dns" or "freedom")
@@ -97,14 +97,30 @@ public partial class CoreConfigV2rayService
continue;
}
}
else if ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() == true) && (!fullConfigTemplate.ProxyDetour.IsNullOrEmpty()) && !(Utils.IsPrivateNetwork(outbound.settings?.servers?.FirstOrDefault()?.address ?? string.Empty) || Utils.IsPrivateNetwork(outbound.settings?.vnext?.FirstOrDefault()?.address ?? string.Empty)))
else if ((!fullConfigTemplate.ProxyDetour.IsNullOrEmpty())
&& ((outbound.streamSettings?.sockopt?.dialerProxy.IsNullOrEmpty() ?? true) == true))
{
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
var outboundAddress = outbound.settings?.servers?.FirstOrDefault()?.address
?? outbound.settings?.vnext?.FirstOrDefault()?.address
?? string.Empty;
if (!Utils.IsPrivateNetwork(outboundAddress))
{
outbound.streamSettings ??= new StreamSettings4Ray();
outbound.streamSettings.sockopt ??= new Sockopt4Ray();
outbound.streamSettings.sockopt.dialerProxy = fullConfigTemplate.ProxyDetour;
}
}
customOutboundsNode.Add(JsonUtils.DeepCopy(outbound));
}
if (fullConfigTemplateNode["outbounds"] is JsonArray templateOutbounds)
{
foreach (var outbound in templateOutbounds)
{
customOutboundsNode.Add(outbound?.DeepClone());
}
}
fullConfigTemplateNode["outbounds"] = customOutboundsNode;
return await Task.FromResult(JsonUtils.Serialize(fullConfigTemplateNode));

View File

@@ -79,9 +79,23 @@ public partial class CoreConfigV2rayService
static object CreateDnsServer(string dnsAddress, List<string> domains, List<string>? expectedIPs = null)
{
var (domain, scheme, port, path) = Utils.ParseUrl(dnsAddress);
var domainFinal = dnsAddress;
int? portFinal = null;
if (scheme.IsNullOrEmpty() || scheme.StartsWith("udp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = domain;
portFinal = port > 0 ? port : null;
}
else if (scheme.StartsWith("tcp", StringComparison.OrdinalIgnoreCase))
{
domainFinal = scheme + "://" + domain;
portFinal = port > 0 ? port : null;
}
var dnsServer = new DnsServer4Ray
{
address = dnsAddress,
address = domainFinal,
port = portFinal,
skipFallback = true,
domains = domains.Count > 0 ? domains : null,
expectedIPs = expectedIPs?.Count > 0 ? expectedIPs : null
@@ -333,8 +347,8 @@ public partial class CoreConfigV2rayService
if (obj is null)
{
List<string> servers = [];
string[] arrDNS = normalDNS.Split(',');
foreach (string str in arrDNS)
var arrDNS = normalDNS.Split(',');
foreach (var str in arrDNS)
{
servers.Add(str);
}

View File

@@ -48,7 +48,7 @@ public partial class CoreConfigV2rayService
private Inbounds4Ray GetInbound(InItem inItem, EInboundProtocol protocol, bool bSocks)
{
string result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
var result = EmbedUtils.GetEmbedText(Global.V2raySampleInbound);
if (result.IsNullOrEmpty())
{
return new();

View File

@@ -453,16 +453,16 @@ public partial class CoreConfigV2rayService
};
//request Host
string request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
string[] arrHost = host.Split(',');
string host2 = string.Join(",".AppendQuotes(), arrHost);
var request = EmbedUtils.GetEmbedText(Global.V2raySampleHttpRequestFileName);
var arrHost = host.Split(',');
var host2 = string.Join(",".AppendQuotes(), arrHost);
request = request.Replace("$requestHost$", $"{host2.AppendQuotes()}");
request = request.Replace("$requestUserAgent$", $"{useragent.AppendQuotes()}");
//Path
string pathHttp = @"/";
var pathHttp = @"/";
if (path.IsNotEmpty())
{
string[] arrPath = path.Split(',');
var arrPath = path.Split(',');
pathHttp = string.Join(",".AppendQuotes(), arrPath);
}
request = request.Replace("$requestPath$", $"{pathHttp.AppendQuotes()}");
@@ -623,10 +623,10 @@ public partial class CoreConfigV2rayService
// Cache for chain proxies to avoid duplicate generation
var nextProxyCache = new Dictionary<string, Outbounds4Ray?>();
var prevProxyTags = new Dictionary<string, string?>(); // Map from profile name to tag
int prevIndex = 0; // Index for prev outbounds
var prevIndex = 0; // Index for prev outbounds
// Process nodes
int index = 0;
var index = 0;
foreach (var node in nodes)
{
index++;

View File

@@ -6,7 +6,7 @@ public partial class CoreConfigV2rayService
{
if (_config.GuiItem.EnableStatistics || _config.GuiItem.DisplayRealTimeSpeed)
{
string tag = EInboundProtocol.api.ToString();
var tag = EInboundProtocol.api.ToString();
Metrics4Ray apiObj = new();
Policy4Ray policyObj = new();
SystemPolicy4Ray policySystemSetting = new();

View File

@@ -167,7 +167,7 @@ public class UpdateService
try
{
var coreInfo = CoreInfoManager.Instance.GetCoreInfo(type);
string filePath = string.Empty;
var filePath = string.Empty;
foreach (var name in coreInfo.CoreExes)
{
var vName = Utils.GetBinPath(Utils.GetExeName(name), coreInfo.CoreType.ToString());
@@ -180,14 +180,14 @@ public class UpdateService
if (!File.Exists(filePath))
{
string msg = string.Format(ResUI.NotFoundCore, @"", "", "");
var msg = string.Format(ResUI.NotFoundCore, @"", "", "");
//ShowMsg(true, msg);
return new SemanticVersion("");
}
var result = await Utils.GetCliWrapOutput(filePath, coreInfo.VersionArg);
var echo = result ?? "";
string version = string.Empty;
var version = string.Empty;
switch (type)
{
case ECoreType.v2fly:

View File

@@ -211,7 +211,7 @@ public class ClashProxiesViewModel : MyReactiveObject
}
//from api
foreach (KeyValuePair<string, ProxiesItem> kv in _proxies)
foreach (var kv in _proxies)
{
if (!Global.allowSelectType.Contains(kv.Value.type.ToLower()))
{
@@ -319,7 +319,7 @@ public class ClashProxiesViewModel : MyReactiveObject
//from providers
if (_providers != null)
{
foreach (KeyValuePair<string, ProvidersItem> kv in _providers)
foreach (var kv in _providers)
{
if (Global.proxyVehicleType.Contains(kv.Value.vehicleType.ToLower()))
{

View File

@@ -273,12 +273,12 @@ public class OptionSettingViewModel : MyReactiveObject
NoticeManager.Instance.Enqueue(ResUI.FillLocalListeningPort);
return;
}
var needReboot = (EnableStatistics != _config.GuiItem.EnableStatistics
var needReboot = EnableStatistics != _config.GuiItem.EnableStatistics
|| DisplayRealTimeSpeed != _config.GuiItem.DisplayRealTimeSpeed
|| EnableDragDropSort != _config.UiItem.EnableDragDropSort
|| EnableHWA != _config.GuiItem.EnableHWA
|| CurrentFontFamily != _config.UiItem.CurrentFontFamily
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation);
|| MainGirdOrientation != (int)_config.UiItem.MainGirdOrientation;
//if (Utile.IsNullOrEmpty(Kcpmtu.ToString()) || !Utile.IsNumeric(Kcpmtu.ToString())
// || Utile.IsNullOrEmpty(Kcptti.ToString()) || !Utile.IsNumeric(Kcptti.ToString())
@@ -375,7 +375,7 @@ public class OptionSettingViewModel : MyReactiveObject
private async Task SaveCoreType()
{
for (int k = 1; k <= _config.CoreTypeItem.Count; k++)
for (var k = 1; k <= _config.CoreTypeItem.Count; k++)
{
var item = _config.CoreTypeItem[k - 1];
var type = string.Empty;

View File

@@ -66,6 +66,7 @@ public class ProfilesViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> SpeedServerCmd { get; }
public ReactiveCommand<Unit, Unit> SortServerResultCmd { get; }
public ReactiveCommand<Unit, Unit> RemoveInvalidServerResultCmd { get; }
public ReactiveCommand<Unit, Unit> FastRealPingCmd { get; }
//servers export
public ReactiveCommand<Unit, Unit> Export2ClientConfigCmd { get; }
@@ -179,6 +180,10 @@ public class ProfilesViewModel : MyReactiveObject
}, canEditRemove);
//servers ping
FastRealPingCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.FastRealping);
});
MixedTestServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await ServerSpeedtest(ESpeedActionType.Mixedtest);
@@ -653,7 +658,7 @@ public class ProfilesViewModel : MyReactiveObject
}
_dicHeaderSort.TryAdd(colName, true);
_dicHeaderSort.TryGetValue(colName, out bool asc);
_dicHeaderSort.TryGetValue(colName, out var asc);
if (await ConfigHandler.SortServers(_config, _config.SubIndexId, colName, asc) != 0)
{
return;
@@ -729,6 +734,12 @@ public class ProfilesViewModel : MyReactiveObject
{
SelectedProfiles = ProfileItems;
}
else if (actionType == ESpeedActionType.FastRealping)
{
SelectedProfiles = ProfileItems;
actionType = ESpeedActionType.Realping;
}
var lstSelected = await GetProfileItems(false);
if (lstSelected == null)
{

View File

@@ -215,7 +215,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
private async Task SaveRoutingAsync()
{
string remarks = SelectedRouting.Remarks;
var remarks = SelectedRouting.Remarks;
if (remarks.IsNullOrEmpty())
{
NoticeManager.Instance.Enqueue(ResUI.PleaseFillRemarks);
@@ -286,7 +286,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
return;
}
DownloadService downloadHandle = new DownloadService();
var downloadHandle = new DownloadService();
var result = await downloadHandle.TryDownloadString(url, true, "");
var ret = await AddBatchRoutingRulesAsync(SelectedRouting, result);
if (ret == 0)
@@ -298,7 +298,7 @@ public class RoutingRuleSettingViewModel : MyReactiveObject
private async Task<int> AddBatchRoutingRulesAsync(RoutingItem routingItem, string? clipboardData)
{
bool blReplace = false;
var blReplace = false;
if (await _updateView?.Invoke(EViewAction.AddBatchRoutingRulesYesNo, null) == false)
{
blReplace = true;

View File

@@ -313,10 +313,10 @@ public class StatusBarViewModel : MyReactiveObject
}
BlServers = true;
for (int k = 0; k < lstModel.Count; k++)
for (var k = 0; k < lstModel.Count; k++)
{
ProfileItem it = lstModel[k];
string name = it.GetSummary();
var name = it.GetSummary();
var item = new ComboItem() { ID = it.IndexId, Text = name };
Servers.Add(item);
@@ -394,10 +394,10 @@ public class StatusBarViewModel : MyReactiveObject
{
await SysProxyHandler.UpdateSysProxy(_config, false);
BlSystemProxyClear = (type == ESysProxyType.ForcedClear);
BlSystemProxySet = (type == ESysProxyType.ForcedChange);
BlSystemProxyNothing = (type == ESysProxyType.Unchanged);
BlSystemProxyPac = (type == ESysProxyType.Pac);
BlSystemProxyClear = type == ESysProxyType.ForcedClear;
BlSystemProxySet = type == ESysProxyType.ForcedChange;
BlSystemProxyNothing = type == ESysProxyType.Unchanged;
BlSystemProxyPac = type == ESysProxyType.Pac;
if (blChange)
{

View File

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

View File

@@ -3,7 +3,7 @@ global using System.Collections.Generic;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Reactive.Disposables;
global using System.Reactive.Disposables.Fluent;
global using System.Reactive.Linq;
global using System.Text;
global using System.Threading;
@@ -17,13 +17,13 @@ global using Avalonia.Markup.Xaml;
global using Avalonia.Media;
global using Avalonia.Media.Imaging;
global using Avalonia.Platform;
global using Avalonia.ReactiveUI;
global using Avalonia.Styling;
global using Avalonia.Threading;
global using ReactiveUI;
global using ReactiveUI.Fody.Helpers;
global using DynamicData;
global using MsBox.Avalonia.Enums;
global using ReactiveUI;
global using ReactiveUI.Avalonia;
global using ReactiveUI.Fody.Helpers;
global using ServiceLib;
global using ServiceLib.Base;
global using ServiceLib.Common;

View File

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

View File

@@ -13,8 +13,8 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
lstChild.SelectionChanged += LstChild_SelectionChanged;
ViewModel = new AddGroupServerViewModel(profileItem, UpdateViewHandler);
@@ -32,11 +32,11 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
switch (profileItem.ConfigType)
{
case EConfigType.PolicyGroup:
this.Title = ResUI.TbConfigTypePolicyGroup;
Title = ResUI.TbConfigTypePolicyGroup;
break;
case EConfigType.ProxyChain:
this.Title = ResUI.TbConfigTypeProxyChain;
Title = ResUI.TbConfigTypeProxyChain;
gridPolicyGroup.IsVisible = false;
break;
}
@@ -64,7 +64,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
menuSelectAllChild.Click += (s, e) => lstChild.SelectAll();
// Keyboard shortcuts when focus is within grid
this.AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
AddHandler(KeyDownEvent, AddGroupServerWindow_KeyDown, RoutingStrategies.Tunnel);
lstChild.LoadingRow += LstChild_LoadingRow;
}
@@ -78,7 +78,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -14,8 +14,8 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
@@ -39,7 +39,7 @@ public partial class AddServer2Window : WindowBase<AddServer2ViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.BrowseServer:

View File

@@ -13,8 +13,8 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
@@ -196,7 +196,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
this.Title = $"{profileItem.ConfigType}";
Title = $"{profileItem.ConfigType}";
}
private async Task<bool> UpdateViewHandler(EViewAction action, object? obj)
@@ -204,7 +204,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -7,7 +7,7 @@ public partial class ClashProxiesView : ReactiveUserControl<ClashProxiesViewMode
InitializeComponent();
ViewModel = new ClashProxiesViewModel(UpdateViewHandler);
lstProxyDetails.DoubleTapped += LstProxyDetails_DoubleTapped;
this.KeyDown += ClashProxiesView_KeyDown;
KeyDown += ClashProxiesView_KeyDown;
this.WhenActivated(disposables =>
{

View File

@@ -12,7 +12,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
_config = AppManager.Instance.Config;
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
cmbRayFreedomDNSStrategy.ItemsSource = Global.DomainStrategy4Freedoms;
@@ -77,7 +77,7 @@ public partial class DNSSettingWindow : WindowBase<DNSSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -12,7 +12,7 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
_config = AppManager.Instance.Config;
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
this.WhenActivated(disposables =>
@@ -36,7 +36,7 @@ public partial class FullConfigTemplateWindow : WindowBase<FullConfigTemplateVie
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -17,8 +17,8 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
HotkeyManager.Instance.IsPause = true;
Loaded += Window_Loaded;
this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
btnCancel.Click += (s, e) => this.Close();
Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
btnCancel.Click += (s, e) => Close();
this.WhenActivated(disposables =>
{
@@ -34,7 +34,7 @@ public partial class GlobalHotkeySettingWindow : WindowBase<GlobalHotkeySettingV
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -21,7 +21,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
_config = AppManager.Instance.Config;
_manager = new WindowNotificationManager(TopLevel.GetTopLevel(this)) { MaxItems = 3, Position = NotificationPosition.TopRight };
this.KeyDown += MainWindow_KeyDown;
KeyDown += MainWindow_KeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuCheckUpdate.Click += MenuCheckUpdate_Click;
@@ -153,14 +153,14 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
if (Utils.IsWindows())
{
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
ThreadPool.RegisterWaitForSingleObject(Program.ProgramStarted, OnProgramStarted, null, -1, false);
HotkeyManager.Instance.Init(_config, OnHotkeyHandler);
}
else
{
this.Title = $"{Utils.GetVersion()}";
Title = $"{Utils.GetVersion()}";
menuRebootAsAdmin.IsVisible = false;
menuSettingsSetUWP.IsVisible = false;
@@ -168,9 +168,9 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
}
menuAddServerViaScan.IsVisible = false;
if (_config.UiItem.AutoHideStartup)
if (_config.UiItem.AutoHideStartup && Utils.IsWindows())
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
}
AddHelpMenuItem();
@@ -402,32 +402,32 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
public void ShowHideWindow(bool? blShow)
{
var bl = blShow ??
Utils.IsLinux()
(Utils.IsLinux()
? (!_config.UiItem.ShowInTaskbar ^ (WindowState == WindowState.Minimized))
: !_config.UiItem.ShowInTaskbar;
: !_config.UiItem.ShowInTaskbar);
if (bl)
{
this.Show();
if (this.WindowState == WindowState.Minimized)
Show();
if (WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
WindowState = WindowState.Normal;
}
this.Activate();
this.Focus();
Activate();
Focus();
}
else
{
if (Utils.IsLinux() && _config.UiItem.Hide2TrayWhenClose == false)
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
return;
}
foreach (var ownedWindow in this.OwnedWindows)
foreach (var ownedWindow in OwnedWindows)
{
ownedWindow.Close();
}
this.Hide();
Hide();
}
_config.UiItem.ShowInTaskbar = bl;

View File

@@ -11,7 +11,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
InitializeComponent();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
_config = AppManager.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -153,7 +153,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.InitSettingFont:

View File

@@ -49,6 +49,13 @@
</Button.Content>
</Button>
<TextBox
x:Name="txtServerFilter"
Width="200"
Margin="{StaticResource MarginLr4}"
VerticalContentAlignment="Center"
Watermark="{x:Static resx:ResUI.MsgServerTitle}" />
<Button
x:Name="btnAutofitColumnWidth"
Margin="{StaticResource MarginLr4}"
@@ -59,12 +66,25 @@
</Button.Content>
</Button>
<TextBox
x:Name="txtServerFilter"
Width="200"
<Button
x:Name="btnFastRealPing"
Margin="{StaticResource MarginLr4}"
VerticalContentAlignment="Center"
Watermark="{x:Static resx:ResUI.MsgServerTitle}" />
Classes="IconButton Success"
ToolTip.Tip="{x:Static resx:ResUI.menuFastRealPing}">
<Button.Content>
<PathIcon Data="{StaticResource SemiIconBolt}" />
</Button.Content>
</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>
<DataGrid
x:Name="lstProfiles"
@@ -88,15 +108,12 @@
<MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" />
<MenuItem x:Name="menuRemoveServer" Header="{x:Static resx:ResUI.menuRemoveServer}" />
<MenuItem x:Name="menuRemoveDuplicateServer" Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator />
<MenuItem x:Name="menuMixedTestServer" Header="{x:Static resx:ResUI.menuMixedTestServer}" />
<MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" />
<MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" />
<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="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
<MenuItem>
@@ -127,7 +144,8 @@
<Separator />
<MenuItem x:Name="menuExport2ShareUrl" Header="{x:Static resx:ResUI.menuExport2ShareUrl}" />
<MenuItem x:Name="menuExport2ShareUrlBase64" Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" />
</MenuItem><Separator />
</MenuItem>
<Separator />
<MenuItem Header="{x:Static resx:ResUI.menuGenGroupMultipleServer}">
<MenuItem x:Name="menuGenGroupMultipleServerXrayRandom" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRandom}" />
<MenuItem x:Name="menuGenGroupMultipleServerXrayRoundRobin" Header="{x:Static resx:ResUI.menuGenGroupMultipleServerXrayRoundRobin}" />

View File

@@ -80,6 +80,7 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.FastRealPingCmd, v => v.btnFastRealPing).DisposeWith(disposables);
//servers export
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);

View File

@@ -13,8 +13,8 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
@@ -61,7 +61,7 @@ public partial class RoutingRuleDetailsWindow : WindowBase<RoutingRuleDetailsVie
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -14,9 +14,9 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
{
InitializeComponent();
this.Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingRuleSettingWindow_KeyDown;
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => Close();
KeyDown += RoutingRuleSettingWindow_KeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.DoubleTapped += LstRules_DoubleTapped;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
@@ -64,7 +64,7 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.ShowYesNo:

View File

@@ -12,9 +12,9 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
InitializeComponent();
Loaded += Window_Loaded;
this.Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => this.Close();
this.KeyDown += RoutingSettingWindow_KeyDown;
Closing += RoutingSettingWindow_Closing;
btnCancel.Click += (s, e) => Close();
KeyDown += RoutingSettingWindow_KeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.DoubleTapped += LstRoutings_DoubleTapped;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
@@ -48,7 +48,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
case EViewAction.ShowYesNo:
@@ -116,7 +116,7 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
private void btnCancel_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
Close(ViewModel?.IsModified);
}
private void RoutingSettingWindow_Closing(object? sender, WindowClosingEventArgs e)

View File

@@ -14,7 +14,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
InitializeComponent();
Loaded += Window_Loaded;
btnCancel.Click += (s, e) => this.Close();
btnCancel.Click += (s, e) => Close();
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
@@ -45,7 +45,7 @@ public partial class SubEditWindow : WindowBase<SubEditViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close(true);
Close(true);
break;
}
return await Task.FromResult(true);

View File

@@ -14,8 +14,8 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
menuClose.Click += menuClose_Click;
Loaded += Window_Loaded;
this.Closing += SubSettingWindow_Closing;
this.KeyDown += SubSettingWindow_KeyDown;
Closing += SubSettingWindow_Closing;
KeyDown += SubSettingWindow_KeyDown;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
lstSubscription.DoubleTapped += LstSubscription_DoubleTapped;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
@@ -37,7 +37,7 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
switch (action)
{
case EViewAction.CloseWindow:
this.Close();
Close();
break;
case EViewAction.ShowYesNo:
@@ -89,7 +89,7 @@ public partial class SubSettingWindow : WindowBase<SubSettingViewModel>
private void menuClose_Click(object? sender, RoutedEventArgs e)
{
_manualClose = true;
this.Close(ViewModel?.IsModified);
Close(ViewModel?.IsModified);
}
private void SubSettingWindow_Closing(object? sender, WindowClosingEventArgs e)

View File

@@ -9,7 +9,7 @@ public partial class SudoPasswordInputView : UserControl
{
InitializeComponent();
this.Loaded += (s, e) => txtPassword.Focus();
Loaded += (s, e) => txtPassword.Focus();
btnSave.Click += async (_, _) => await SavePasswordAsync();

View File

@@ -15,7 +15,7 @@
</PackageReference>
<PackageReference Include="Avalonia.Desktop" />
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" />
<PackageReference Include="Avalonia.ReactiveUI" />
<PackageReference Include="ReactiveUI.Avalonia" />
<PackageReference Include="MessageBox.Avalonia" />
<PackageReference Include="Semi.Avalonia" />
<PackageReference Include="Semi.Avalonia.AvaloniaEdit" />

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-windows-desktop.yml = ..\.github\workflows\build-windows-desktop.yml
..\.github\workflows\build-windows.yml = ..\.github\workflows\build-windows.yml
..\package-appimage.sh = ..\package-appimage.sh
..\package-debian.sh = ..\package-debian.sh
..\package-osx.sh = ..\package-osx.sh
..\package-release-zip.sh = ..\package-release-zip.sh
..\pkg2appimage.yml = ..\pkg2appimage.yml
..\.github\workflows\winget-publish.yml = ..\.github\workflows\winget-publish.yml
EndProjectSection
EndProject

View File

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

View File

@@ -9,7 +9,7 @@ public partial class App : Application
public App()
{
this.DispatcherUnhandledException += App_DispatcherUnhandledException;
DispatcherUnhandledException += App_DispatcherUnhandledException;
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
}
@@ -23,7 +23,7 @@ public partial class App : Application
var exePathKey = Utils.GetMd5(Utils.GetExePath());
var rebootas = (e.Args ?? Array.Empty<string>()).Any(t => t == Global.RebootAs);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out bool bCreatedNew);
ProgramStarted = new EventWaitHandle(false, EventResetMode.AutoReset, exePathKey, out var bCreatedNew);
if (!rebootas && !bCreatedNew)
{
ProgramStarted.Set();

View File

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

View File

@@ -1,6 +1,6 @@
using Microsoft.Win32;
namespace v2rayN;
namespace v2rayN.Common;
internal class UI
{

View File

@@ -3,7 +3,7 @@ using System.Windows.Media;
using System.Windows.Media.Imaging;
using Microsoft.Win32;
namespace v2rayN;
namespace v2rayN.Common;
internal static class WindowsUtils
{
@@ -40,13 +40,13 @@ internal static class WindowsUtils
}
[DllImport("dwmapi.dll")]
public static extern int DwmSetWindowAttribute(IntPtr hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
public static extern int DwmSetWindowAttribute(nint hwnd, DWMWINDOWATTRIBUTE attribute, ref int attributeValue, uint attributeSize);
public static ImageSource IconToImageSource(Icon icon)
{
return Imaging.CreateBitmapSourceFromHIcon(
icon.Handle,
new System.Windows.Int32Rect(0, 0, icon.Width, icon.Height),
new Int32Rect(0, 0, icon.Width, icon.Height),
BitmapSizeOptions.FromEmptyOptions());
}
@@ -65,9 +65,9 @@ internal static class WindowsUtils
private static void SetDarkBorder(Window window, bool dark)
{
// Make sure the handle is created before the window is shown
IntPtr hWnd = new WindowInteropHelper(window).EnsureHandle();
int attribute = dark ? 1 : 0;
uint attributeSize = (uint)Marshal.SizeOf(attribute);
var hWnd = new WindowInteropHelper(window).EnsureHandle();
var attribute = dark ? 1 : 0;
var attributeSize = (uint)Marshal.SizeOf(attribute);
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE_BEFORE_20H1, ref attribute, attributeSize);
DwmSetWindowAttribute(hWnd, DWMWINDOWATTRIBUTE.DWMWA_USE_IMMERSIVE_DARK_MODE, ref attribute, attributeSize);
}

View File

@@ -6,7 +6,7 @@ global using System.Diagnostics;
global using System.Globalization;
global using System.IO;
global using System.Linq;
global using System.Reactive.Disposables;
global using System.Reactive.Disposables.Fluent;
global using System.Reactive.Linq;
global using System.Runtime.InteropServices;
global using System.Text;
@@ -31,3 +31,4 @@ global using ServiceLib.Manager;
global using ServiceLib.Models;
global using ServiceLib.Resx;
global using ServiceLib.ViewModels;
global using v2rayN.Common;

View File

@@ -43,7 +43,7 @@ public sealed class HotkeyManager
modifiers |= KeyModifiers.Alt;
}
key = key << 16 | (int)modifiers;
key = (key << 16) | (int)modifiers;
if (!_hotkeyTriggerDic.ContainsKey(key))
{
_hotkeyTriggerDic.Add(key, new() { item.EGlobalHotkey });
@@ -103,7 +103,7 @@ public sealed class HotkeyManager
private (int fsModifiers, int vKey, string hotkeyStr, List<string> Names) GetHotkeyInfo(int hotkeyCode)
{
var fsModifiers = hotkeyCode & 0xffff;
var vKey = hotkeyCode >> 16 & 0xffff;
var vKey = (hotkeyCode >> 16) & 0xffff;
var hotkeyStr = new StringBuilder();
var names = new List<string>();

View File

@@ -60,18 +60,18 @@ public sealed class WindowsManager
return null;
}
Color color = ColorTranslator.FromHtml("#3399CC");
int index = (int)config.SystemProxyItem.SysProxyType;
var color = ColorTranslator.FromHtml("#3399CC");
var index = (int)config.SystemProxyItem.SysProxyType;
if (index > 0)
{
color = (new[] { Color.Red, Color.Purple, Color.DarkGreen, Color.Orange, Color.DarkSlateBlue, Color.RoyalBlue })[index - 1];
}
int width = 128;
int height = 128;
var width = 128;
var height = 128;
Bitmap bitmap = new(width, height);
Graphics graphics = Graphics.FromImage(bitmap);
var graphics = Graphics.FromImage(bitmap);
SolidBrush drawBrush = new(color);
graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
@@ -79,7 +79,7 @@ public sealed class WindowsManager
graphics.DrawImage(new Bitmap(item.CustomIcon), 0, 0, width, height);
graphics.FillEllipse(drawBrush, width / 2, width / 2, width / 2, width / 2);
Icon createdIcon = Icon.FromHandle(bitmap.GetHicon());
var createdIcon = Icon.FromHandle(bitmap.GetHicon());
drawBrush.Dispose();
graphics.Dispose();

View File

@@ -6,9 +6,9 @@ public partial class AddGroupServerWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
this.PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
PreviewKeyDown += AddGroupServerWindow_PreviewKeyDown;
lstChild.SelectionChanged += LstChild_SelectionChanged;
menuSelectAllChild.Click += MenuSelectAllChild_Click;
@@ -27,11 +27,11 @@ public partial class AddGroupServerWindow
switch (profileItem.ConfigType)
{
case EConfigType.PolicyGroup:
this.Title = ResUI.TbConfigTypePolicyGroup;
Title = ResUI.TbConfigTypePolicyGroup;
break;
case EConfigType.ProxyChain:
this.Title = ResUI.TbConfigTypeProxyChain;
Title = ResUI.TbConfigTypeProxyChain;
gridPolicyGroup.Visibility = Visibility.Collapsed;
break;
}
@@ -61,7 +61,7 @@ public partial class AddGroupServerWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -6,8 +6,8 @@ public partial class AddServer2Window
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
ViewModel = new AddServer2ViewModel(profileItem, UpdateViewHandler);
cmbCoreType.ItemsSource = Utils.GetEnumNames<ECoreType>().Where(t => t != ECoreType.v2rayN.ToString()).ToList().AppendEmpty();
@@ -32,11 +32,11 @@ public partial class AddServer2Window
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.BrowseServer:
if (UI.OpenFileDialog(out string fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
if (UI.OpenFileDialog(out var fileName, "Config|*.json|YAML|*.yaml;*.yml|All|*.*") != true)
{
return false;
}

View File

@@ -8,8 +8,8 @@ public partial class AddServerWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
cmbNetwork.SelectionChanged += CmbNetwork_SelectionChanged;
cmbStreamSecurity.SelectionChanged += CmbStreamSecurity_SelectionChanged;
btnGUID.Click += btnGUID_Click;
@@ -191,7 +191,7 @@ public partial class AddServerWindow
this.BindCommand(ViewModel, vm => vm.SaveCmd, v => v.btnSave).DisposeWith(disposables);
});
this.Title = $"{profileItem.ConfigType}";
Title = $"{profileItem.ConfigType}";
WindowsUtils.SetDarkBorder(this, AppManager.Instance.Config.UiItem.CurrentTheme);
}
@@ -200,7 +200,7 @@ public partial class AddServerWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -28,7 +28,7 @@ public partial class BackupAndRestoreView
private void MenuLocalBackup_Click(object sender, RoutedEventArgs e)
{
if (UI.SaveFileDialog(out string fileName, "Zip|*.zip") != true)
if (UI.SaveFileDialog(out var fileName, "Zip|*.zip") != true)
{
return;
}
@@ -37,7 +37,7 @@ public partial class BackupAndRestoreView
private void MenuLocalRestore_Click(object sender, RoutedEventArgs e)
{
if (UI.OpenFileDialog(out string fileName, "Zip|*.zip|All|*.*") != true)
if (UI.OpenFileDialog(out var fileName, "Zip|*.zip|All|*.*") != true)
{
return;
}

View File

@@ -8,7 +8,7 @@ public partial class DNSSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new DNSSettingViewModel(UpdateViewHandler);
@@ -78,7 +78,7 @@ public partial class DNSSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -8,7 +8,7 @@ public partial class FullConfigTemplateWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new FullConfigTemplateViewModel(UpdateViewHandler);
@@ -35,7 +35,7 @@ public partial class FullConfigTemplateWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -11,14 +11,14 @@ public partial class GlobalHotkeySettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
ViewModel = new GlobalHotkeySettingViewModel(UpdateViewHandler);
btnReset.Click += btnReset_Click;
HotkeyManager.Instance.IsPause = true;
this.Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
Closing += (s, e) => HotkeyManager.Instance.IsPause = false;
this.WhenActivated(disposables =>
{
@@ -35,7 +35,7 @@ public partial class GlobalHotkeySettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -19,8 +19,8 @@ public partial class MainWindow
ThreadPool.RegisterWaitForSingleObject(App.ProgramStarted, OnProgramStarted, null, -1, false);
App.Current.SessionEnding += Current_SessionEnding;
this.Closing += MainWindow_Closing;
this.PreviewKeyDown += MainWindow_PreviewKeyDown;
Closing += MainWindow_Closing;
PreviewKeyDown += MainWindow_PreviewKeyDown;
menuSettingsSetUWP.Click += menuSettingsSetUWP_Click;
menuPromotion.Click += menuPromotion_Click;
menuClose.Click += menuClose_Click;
@@ -150,10 +150,10 @@ public partial class MainWindow
.DisposeWith(disposables);
});
this.Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
Title = $"{Utils.GetVersion()} - {(Utils.IsAdministrator() ? ResUI.RunAsAdmin : ResUI.NotRunAsAdmin)}";
if (_config.UiItem.AutoHideStartup)
{
this.WindowState = WindowState.Minimized;
WindowState = WindowState.Minimized;
}
if (!_config.GuiItem.EnableHWA)
@@ -187,35 +187,35 @@ public partial class MainWindow
case EViewAction.AddServerWindow:
if (obj is null)
return false;
return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddServer2Window:
if (obj is null)
return false;
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddGroupServerWindow:
if (obj is null)
return false;
return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.DNSSettingWindow:
return (new DNSSettingWindow().ShowDialog() ?? false);
return new DNSSettingWindow().ShowDialog() ?? false;
case EViewAction.RoutingSettingWindow:
return (new RoutingSettingWindow().ShowDialog() ?? false);
return new RoutingSettingWindow().ShowDialog() ?? false;
case EViewAction.OptionSettingWindow:
return (new OptionSettingWindow().ShowDialog() ?? false);
return new OptionSettingWindow().ShowDialog() ?? false;
case EViewAction.FullConfigTemplateWindow:
return (new FullConfigTemplateWindow().ShowDialog() ?? false);
return new FullConfigTemplateWindow().ShowDialog() ?? false;
case EViewAction.GlobalHotkeySettingWindow:
return (new GlobalHotkeySettingWindow().ShowDialog() ?? false);
return new GlobalHotkeySettingWindow().ShowDialog() ?? false;
case EViewAction.SubSettingWindow:
return (new SubSettingWindow().ShowDialog() ?? false);
return new SubSettingWindow().ShowDialog() ?? false;
case EViewAction.ScanScreenTask:
await ScanScreenTaskAsync();
@@ -328,7 +328,7 @@ public partial class MainWindow
if (Application.Current?.MainWindow is Window window)
{
var bytes = QRCodeUtils.CaptureScreen(window);
var bytes = QRCodeWindowsUtils.CaptureScreen(window);
await ViewModel?.ScanScreenResult(bytes);
}
@@ -372,7 +372,7 @@ public partial class MainWindow
this?.Show();
if (this?.WindowState == WindowState.Minimized)
{
this.WindowState = WindowState.Normal;
WindowState = WindowState.Normal;
}
this?.Activate();
this?.Focus();

View File

@@ -31,10 +31,10 @@ public partial class MsgView
case EViewAction.DispatcherShowMsg:
if (obj is null)
return false;
Application.Current?.Dispatcher.Invoke((() =>
Application.Current?.Dispatcher.Invoke(() =>
{
ShowMsg(obj);
}), DispatcherPriority.ApplicationIdle);
}, DispatcherPriority.ApplicationIdle);
break;
}
return await Task.FromResult(true);

View File

@@ -10,7 +10,7 @@ public partial class OptionSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
_config = AppManager.Instance.Config;
ViewModel = new OptionSettingViewModel(UpdateViewHandler);
@@ -136,7 +136,7 @@ public partial class OptionSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.InitSettingFont:
@@ -168,12 +168,12 @@ public partial class OptionSettingWindow
foreach (var ttf in files)
{
var families = Fonts.GetFontFamilies(Utils.GetFontsPath(ttf));
foreach (FontFamily family in families)
foreach (var family in families)
{
var typefaces = family.GetTypefaces();
foreach (Typeface typeface in typefaces)
foreach (var typeface in typefaces)
{
typeface.TryGetGlyphTypeface(out GlyphTypeface glyph);
typeface.TryGetGlyphTypeface(out var glyph);
//var fontFace = glyph.Win32FaceNames[new CultureInfo("en-us")];
//if (!fontFace.Equals("Regular") && !fontFace.Equals("Normal"))
//{

View File

@@ -71,7 +71,7 @@ public partial class ProfilesSelectWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -57,16 +57,6 @@
<materialDesign:PackIcon VerticalAlignment="Center" Kind="Plus" />
</Button>
<Button
x:Name="btnAutofitColumnWidth"
Width="30"
Height="30"
Margin="{StaticResource MarginLeftRight8}"
AutomationProperties.Name="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
</Button>
<TextBox
x:Name="txtServerFilter"
Width="200"
@@ -76,6 +66,36 @@
materialDesign:TextFieldAssist.HasClearButton="True"
AutomationProperties.Name="{x:Static resx:ResUI.MsgServerTitle}"
Style="{StaticResource DefTextBox}" />
<Button
x:Name="btnAutofitColumnWidth"
Width="30"
Height="30"
Margin="{StaticResource MarginLeftRight4}"
AutomationProperties.Name="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuProfileAutofitColumnWidth}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="ArrowSplitVertical" />
</Button>
<Button
x:Name="btnFastRealPing"
Width="30"
Height="30"
Margin="{StaticResource MarginLeftRight4}"
Style="{StaticResource MaterialDesignFloatingActionMiniLightButton}"
ToolTip="{x:Static resx:ResUI.menuFastRealPing}">
<materialDesign:PackIcon VerticalAlignment="Center" Kind="LightningBolt" />
</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>
<DataGrid
x:Name="lstProfiles"
@@ -120,11 +140,11 @@
x:Name="menuRemoveDuplicateServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<Separator />
<MenuItem
x:Name="menuMixedTestServer"
x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMixedTestServer}" />
Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator />
<MenuItem
x:Name="menuTcpingServer"
Height="{StaticResource MenuItemHeight}"
@@ -137,16 +157,10 @@
x:Name="menuSpeedServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSpeedServer}" />
<MenuItem Header="{x:Static resx:ResUI.menuTestServerResult}">
<MenuItem
x:Name="menuSortServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSortServerResult}" />
<MenuItem
x:Name="menuRemoveInvalidServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
</MenuItem>
<MenuItem
x:Name="menuSortServerResult"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator />
<MenuItem
x:Name="menuMoveToGroup"

View File

@@ -77,6 +77,7 @@ public partial class ProfilesView
this.BindCommand(ViewModel, vm => vm.SpeedServerCmd, v => v.menuSpeedServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.SortServerResultCmd, v => v.menuSortServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.RemoveInvalidServerResultCmd, v => v.menuRemoveInvalidServerResult).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.FastRealPingCmd, v => v.btnFastRealPing).DisposeWith(disposables);
//servers export
this.BindCommand(ViewModel, vm => vm.Export2ClientConfigCmd, v => v.menuExport2ClientConfig).DisposeWith(disposables);
@@ -126,7 +127,7 @@ public partial class ProfilesView
case EViewAction.SaveFileDialog:
if (obj is null)
return false;
if (UI.SaveFileDialog(out string fileName, "Config|*.json") != true)
if (UI.SaveFileDialog(out var fileName, "Config|*.json") != true)
{
return false;
}
@@ -136,17 +137,17 @@ public partial class ProfilesView
case EViewAction.AddServerWindow:
if (obj is null)
return false;
return (new AddServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddServer2Window:
if (obj is null)
return false;
return (new AddServer2Window((ProfileItem)obj)).ShowDialog() ?? false;
return new AddServer2Window((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.AddGroupServerWindow:
if (obj is null)
return false;
return (new AddGroupServerWindow((ProfileItem)obj)).ShowDialog() ?? false;
return new AddGroupServerWindow((ProfileItem)obj).ShowDialog() ?? false;
case EViewAction.ShareServer:
if (obj is null)
@@ -157,7 +158,7 @@ public partial class ProfilesView
case EViewAction.SubEditWindow:
if (obj is null)
return false;
return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
return new SubEditWindow((SubItem)obj).ShowDialog() ?? false;
case EViewAction.DispatcherRefreshServersBiz:
Application.Current?.Dispatcher.Invoke(RefreshServersBiz, DispatcherPriority.Normal);
@@ -169,7 +170,7 @@ public partial class ProfilesView
public async void ShareServer(string url)
{
var img = QRCodeUtils.GetQRCode(url);
var img = QRCodeWindowsUtils.GetQRCode(url);
var dialog = new QrcodeView()
{
imgQrcode = { Source = img },
@@ -414,8 +415,8 @@ public partial class ProfilesView
private void LstProfiles_MouseMove(object sender, MouseEventArgs e)
{
// Get the current mouse position
Point mousePos = e.GetPosition(null);
Vector diff = startPoint - mousePos;
var mousePos = e.GetPosition(null);
var diff = startPoint - mousePos;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
@@ -428,7 +429,7 @@ public partial class ProfilesView
if (listViewItem == null)
return; // Abort
// Find the data behind the ListViewItem
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null)
return; // Abort
// Initialize the drag & drop operation
@@ -461,7 +462,7 @@ public partial class ProfilesView
return;
}
// Find the data behind the Item
ProfileItemModel item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
var item = (ProfileItemModel)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
if (item == null)
return;
// Move item into observable collection

View File

@@ -6,8 +6,8 @@ public partial class RoutingRuleDetailsWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
clbProtocol.SelectionChanged += ClbProtocol_SelectionChanged;
clbInboundTag.SelectionChanged += ClbInboundTag_SelectionChanged;
@@ -54,7 +54,7 @@ public partial class RoutingRuleDetailsWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -6,9 +6,9 @@ public partial class RoutingRuleSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
this.PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
PreviewKeyDown += RoutingRuleSettingWindow_PreviewKeyDown;
lstRules.SelectionChanged += lstRules_SelectionChanged;
lstRules.MouseDoubleClick += LstRules_MouseDoubleClick;
menuRuleSelectAll.Click += menuRuleSelectAll_Click;
@@ -57,7 +57,7 @@ public partial class RoutingRuleSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -80,11 +80,11 @@ public partial class RoutingRuleSettingWindow
if (obj is null)
return false;
return (new RoutingRuleDetailsWindow((RulesItem)obj)).ShowDialog() ?? false;
return new RoutingRuleDetailsWindow((RulesItem)obj).ShowDialog() ?? false;
case EViewAction.ImportRulesFromFile:
if (UI.OpenFileDialog(out string fileName, "Rules|*.json|All|*.*") != true)
if (UI.OpenFileDialog(out var fileName, "Rules|*.json|All|*.*") != true)
{
return false;
}
@@ -174,7 +174,7 @@ public partial class RoutingRuleSettingWindow
private void btnBrowseCustomIcon_Click(object sender, System.Windows.RoutedEventArgs e)
{
if (UI.OpenFileDialog(out string fileName,
if (UI.OpenFileDialog(out var fileName,
"PNG,ICO|*.png;*.ico") != true)
{
return;
@@ -185,7 +185,7 @@ public partial class RoutingRuleSettingWindow
private void btnBrowseCustomRulesetPath4Singbox_Click(object sender, RoutedEventArgs e)
{
if (UI.OpenFileDialog(out string fileName,
if (UI.OpenFileDialog(out var fileName,
"Config|*.json|All|*.*") != true)
{
return;

View File

@@ -6,9 +6,9 @@ public partial class RoutingSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Closing += RoutingSettingWindow_Closing;
this.PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
Owner = Application.Current.MainWindow;
Closing += RoutingSettingWindow_Closing;
PreviewKeyDown += RoutingSettingWindow_PreviewKeyDown;
lstRoutings.SelectionChanged += lstRoutings_SelectionChanged;
lstRoutings.MouseDoubleClick += LstRoutings_MouseDoubleClick;
menuRoutingAdvancedSelectAll.Click += menuRoutingAdvancedSelectAll_Click;
@@ -44,7 +44,7 @@ public partial class RoutingSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -58,7 +58,7 @@ public partial class RoutingSettingWindow
if (obj is null)
return false;
return (new RoutingRuleSettingWindow((RoutingItem)obj)).ShowDialog() ?? false;
return new RoutingRuleSettingWindow((RoutingItem)obj).ShowDialog() ?? false;
}
return await Task.FromResult(true);
}
@@ -67,7 +67,7 @@ public partial class RoutingSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
}
@@ -122,11 +122,11 @@ public partial class RoutingSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
else
{
this.Close();
Close();
}
}
}

View File

@@ -71,11 +71,11 @@ public partial class StatusBarView
switch (action)
{
case EViewAction.DispatcherRefreshIcon:
Application.Current?.Dispatcher.Invoke((async () =>
Application.Current?.Dispatcher.Invoke(async () =>
{
tbNotify.Icon = await WindowsManager.Instance.GetNotifyIcon(_config);
Application.Current.MainWindow.Icon = WindowsManager.Instance.GetAppIcon(_config);
}), DispatcherPriority.Normal);
}, DispatcherPriority.Normal);
break;
case EViewAction.SetClipboardData:

View File

@@ -6,8 +6,8 @@ public partial class SubEditWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
this.Loaded += Window_Loaded;
Owner = Application.Current.MainWindow;
Loaded += Window_Loaded;
ViewModel = new SubEditViewModel(subItem, UpdateViewHandler);
@@ -39,7 +39,7 @@ public partial class SubEditWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
}
return await Task.FromResult(true);

View File

@@ -8,10 +8,10 @@ public partial class SubSettingWindow
{
InitializeComponent();
this.Owner = Application.Current.MainWindow;
Owner = Application.Current.MainWindow;
ViewModel = new SubSettingViewModel(UpdateViewHandler);
this.Closing += SubSettingWindow_Closing;
Closing += SubSettingWindow_Closing;
lstSubscription.MouseDoubleClick += LstSubscription_MouseDoubleClick;
lstSubscription.SelectionChanged += LstSubscription_SelectionChanged;
menuClose.Click += menuClose_Click;
@@ -34,7 +34,7 @@ public partial class SubSettingWindow
switch (action)
{
case EViewAction.CloseWindow:
this.DialogResult = true;
DialogResult = true;
break;
case EViewAction.ShowYesNo:
@@ -47,7 +47,7 @@ public partial class SubSettingWindow
case EViewAction.SubEditWindow:
if (obj is null)
return false;
return (new SubEditWindow((SubItem)obj)).ShowDialog() ?? false;
return new SubEditWindow((SubItem)obj).ShowDialog() ?? false;
case EViewAction.ShareSub:
if (obj is null)
@@ -64,7 +64,7 @@ public partial class SubSettingWindow
{
return;
}
var img = QRCodeUtils.GetQRCode(url);
var img = QRCodeWindowsUtils.GetQRCode(url);
var dialog = new QrcodeView()
{
imgQrcode = { Source = img },
@@ -78,7 +78,7 @@ public partial class SubSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
}
@@ -99,11 +99,11 @@ public partial class SubSettingWindow
{
if (ViewModel?.IsModified == true)
{
this.DialogResult = true;
DialogResult = true;
}
else
{
this.Close();
Close();
}
}
}