Compare commits
28 Commits
7.15.5
...
d727ff40bb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d727ff40bb | ||
|
|
1b5069a933 | ||
|
|
18ea6fdc00 | ||
|
|
67494108ad | ||
|
|
38b2a7d2ca | ||
|
|
bf3703bca1 | ||
|
|
554632cc07 | ||
|
|
12fc3e9566 | ||
|
|
c2ef3a4a8c | ||
|
|
86eb8297dd | ||
|
|
c63d4e83f9 | ||
|
|
bf1fb0f92e | ||
|
|
3c4865982b | ||
|
|
22c233f0cd | ||
|
|
b2d6282755 | ||
|
|
c8d89e3dce | ||
|
|
d3b1810eab | ||
|
|
51409a3e28 | ||
|
|
1a0f50a41e | ||
|
|
83d4a9c18e | ||
|
|
b4c20e7b81 | ||
|
|
7c76308c93 | ||
|
|
f28fa31c14 | ||
|
|
bbedc4dbb1 | ||
|
|
ecf42cb85d | ||
|
|
e4701d6703 | ||
|
|
54a47d00a3 | ||
|
|
964572817b |
108
.github/workflows/build-linux.yml
vendored
108
.github/workflows/build-linux.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/build-osx.yml
vendored
2
.github/workflows/build-osx.yml
vendored
@@ -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: |
|
||||
|
||||
2
.github/workflows/build-windows-desktop.yml
vendored
2
.github/workflows/build-windows-desktop.yml
vendored
@@ -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: |
|
||||
|
||||
2
.github/workflows/build-windows.yml
vendored
2
.github/workflows/build-windows.yml
vendored
@@ -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: |
|
||||
|
||||
@@ -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'
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project>
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>7.15.5</Version>
|
||||
<Version>7.15.7</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,6 +1,3 @@
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace ServiceLib.Common;
|
||||
/*
|
||||
* See:
|
||||
|
||||
@@ -5,5 +5,6 @@ public enum ESpeedActionType
|
||||
Tcping,
|
||||
Realping,
|
||||
Speedtest,
|
||||
Mixedtest
|
||||
Mixedtest,
|
||||
FastRealping
|
||||
}
|
||||
|
||||
@@ -427,6 +427,7 @@ public class Global
|
||||
"zh-Hant",
|
||||
"en",
|
||||
"fa-Ir",
|
||||
"fr",
|
||||
"ru",
|
||||
"hu"
|
||||
];
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -37,7 +37,7 @@ public class FmtHandler
|
||||
|
||||
try
|
||||
{
|
||||
string str = config.TrimEx();
|
||||
var str = config.TrimEx();
|
||||
if (str.IsNullOrEmpty())
|
||||
{
|
||||
msg = ResUI.FailedReadConfiguration;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"));
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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}";
|
||||
|
||||
@@ -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; }
|
||||
|
||||
11
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
11
v2rayN/ServiceLib/Resx/ResUI.Designer.cs
generated
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
1599
v2rayN/ServiceLib/Resx/ResUI.fr.resx
Normal file
1599
v2rayN/ServiceLib/Resx/ResUI.fr.resx
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 連接埠 = +3;Xray API 連接埠 = +4;mihomo 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>
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()))
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 =>
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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}" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
@@ -1,6 +1,6 @@
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace v2rayN;
|
||||
namespace v2rayN.Common;
|
||||
|
||||
internal class UI
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>();
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"))
|
||||
//{
|
||||
|
||||
@@ -71,7 +71,7 @@ public partial class ProfilesSelectWindow
|
||||
switch (action)
|
||||
{
|
||||
case EViewAction.CloseWindow:
|
||||
this.DialogResult = true;
|
||||
DialogResult = true;
|
||||
break;
|
||||
}
|
||||
return await Task.FromResult(true);
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user