Compare commits

..

19 Commits

Author SHA1 Message Date
2dust
30e663cd4f up 7.16.3 2025-11-19 17:15:35 +08:00
JieXu
054efeb32c [PR]改进Emoji字体兼容性 (#8346)
* Update AppBuilderExtension.cs

* Update package-rhel.sh

* Update package-debian.sh

* Update AppBuilderExtension.cs

* Update AppBuilderExtension.cs

* Update AppBuilderExtension.cs

* Withdraw

* Update AppBuilderExtension.cs
2025-11-19 16:47:00 +08:00
2dust
2ebd2b28a8 Support Backspace for remove actions in UI lists
Changed key handling and menu input gestures to allow Backspace (in addition to Delete) for removing items in server, profile, and routing rule lists. This improves usability and consistency across both Avalonia and WPF views.
2025-11-18 16:54:42 +08:00
dependabot[bot]
84f812c8ee Bump actions/checkout from 5.0.0 to 5.0.1 (#8341)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5.0.0 to 5.0.1.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5.0.0...v5.0.1)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-18 16:15:59 +08:00
2dust
b6ee40ab8d Update Directory.Packages.props 2025-11-18 16:15:19 +08:00
2dust
7f24f4a15f Remove shortcut hints from menu translations
Shortcut key hints (e.g., '(Ctrl+C)', '(Delete)') were removed from various menu item translations in resource files for all supported languages. This improves consistency and clarity in UI text across the application.
2025-11-18 16:00:02 +08:00
2dust
0d307671d1 Bug fix
https://github.com/2dust/v2rayN/issues/8267
2025-11-17 17:44:39 +08:00
Harry Huang
8ea5a57988 Optimize speedtest (#8325)
* Optimize stop-speedtest tip display

* Fix speedtest termination latency
2025-11-16 14:58:55 +08:00
Harry Huang
4fb41aeca1 Remove redundant string operation (#8324) 2025-11-16 14:21:34 +08:00
2dust
3f0bcf7b83 up 7.16.2 2025-11-14 19:46:54 +08:00
2dust
7e712fcdeb Refactor menu layouts in window views 2025-11-14 19:44:09 +08:00
2dust
e634e6dae3 Code clean 2025-11-13 20:31:02 +08:00
2dust
24f8d767b1 Update routing version prefix to V4 2025-11-12 19:34:04 +08:00
2dust
31a8ddef23 Update Directory.Packages.props 2025-11-12 19:33:32 +08:00
MkQtS
30e9e64fd5 Simplify sing-box rules for domain_suffix (#8306)
adapt to new domain_suffix behavior since sing-box 1.9.0
2025-11-12 19:08:57 +08:00
MkQtS
f677934257 Proxy all Google domains (#8287)
* Proxy all Google domains

Default geosite-cn(dat/srs) used by v2rayN contains google@cn, which performs poorly in certain user environments.

* Resolve all Google domains via remote server

* fix typo

* Add google to default geofiles
2025-11-12 19:08:36 +08:00
JieXu
df7ca81837 Update package-osx.sh (#8303) 2025-11-11 19:31:06 +08:00
tt2563
53bd03dea2 更新繁體中文翻譯 (#8302)
Co-authored-by: tes2511 <tes2511@user.user>
2025-11-11 19:30:41 +08:00
JieXu
1f8dd1a52d Update ResUI.fr.resx (#8297) 2025-11-10 19:59:29 +08:00
58 changed files with 1289 additions and 903 deletions

View File

@@ -9,10 +9,6 @@ end_of_line = crlf
trim_trailing_whitespace = true trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
[*.sh]
end_of_line = lf
indent_size = 2
[*.{yml,yaml}] [*.{yml,yaml}]
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
@@ -161,14 +157,14 @@ dotnet_naming_rule.non_field_members_should_be_pascal.symbols = non_field_member
dotnet_naming_rule.non_field_members_should_be_pascal.style = pascal dotnet_naming_rule.non_field_members_should_be_pascal.style = pascal
dotnet_naming_symbols.interface.applicable_kinds = interface dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = * dotnet_naming_symbols.interface.applicable_accessibilities = *
dotnet_naming_symbols.interface.required_modifiers = dotnet_naming_symbols.interface.required_modifiers =
dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = * dotnet_naming_symbols.types.applicable_accessibilities = *
dotnet_naming_symbols.types.required_modifiers = dotnet_naming_symbols.types.required_modifiers =
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = * dotnet_naming_symbols.non_field_members.applicable_accessibilities = *
dotnet_naming_symbols.non_field_members.required_modifiers = dotnet_naming_symbols.non_field_members.required_modifiers =
dotnet_naming_style.pascal.required_prefix = dotnet_naming_style.pascal.required_prefix =
dotnet_naming_style.pascal.required_suffix = dotnet_naming_style.pascal.required_suffix =
dotnet_naming_style.pascal.word_separator = dotnet_naming_style.pascal.word_separator =
dotnet_naming_style.pascal.capitalization = pascal_case dotnet_naming_style.pascal.capitalization = pascal_case

View File

@@ -31,7 +31,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.1
with: with:
submodules: 'recursive' submodules: 'recursive'
fetch-depth: '0' fetch-depth: '0'
@@ -110,7 +110,7 @@ jobs:
dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which dnf -y install sudo git rpm-build rpmdevtools dnf-plugins-core rsync findutils tar gzip unzip which
- name: Checkout repo (for scripts) - name: Checkout repo (for scripts)
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.1
with: with:
submodules: 'recursive' submodules: 'recursive'
fetch-depth: '0' fetch-depth: '0'

View File

@@ -26,7 +26,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.1
with: with:
submodules: 'recursive' submodules: 'recursive'
fetch-depth: '0' fetch-depth: '0'

View File

@@ -26,7 +26,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.1
with: with:
submodules: 'recursive' submodules: 'recursive'
fetch-depth: '0' fetch-depth: '0'

View File

@@ -27,7 +27,7 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v5.0.0 uses: actions/checkout@v5.0.1
- name: Setup - name: Setup
uses: actions/setup-dotnet@v5.0.0 uses: actions/setup-dotnet@v5.0.0

View File

@@ -1,47 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
# Root directory = the script's location
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
Version="$1"
PackagePath="v2rayn-unofficial-repo"
mkdir -p "${PackagePath}/DEBIAN"
mkdir -p "${PackagePath}"/etc/apt/{keyrings,sources.list.d}
curl -fsSLo "${PackagePath}/etc/apt/keyrings/v2rayn-unofficial.asc" "https://git.vlyaii.ru/api/packages/voronin9032/debian/repository.key"
# basic
cat >"${PackagePath}/DEBIAN/control" <<-EOF
Package: v2rayn-unofficial-repo
Version: $Version
Maintainer: Vlyaii <voronin9032n3@gmail.com>
Homepage: https://git.vlyaii.ru/voronin9032/v2rayN
Architecture: all
Depends: ca-certificates
Description: v2rayn-unofficial repository configuration
EOF
cat >"${PackagePath}/etc/apt/sources.list.d/v2rayn-unofficial.sources" <<-EOF
Types: deb
URIs: https://git.vlyaii.ru/api/packages/voronin9032/debian
Suites: debian
Components: stable
Architectures: amd64 all
Signed-By: /etc/apt/keyrings/v2rayn-unofficial.asc
EOF
# Patch
# set owner to root:root
sudo chown -R root:root "${PackagePath}"
# set all directories to 755 (readable & traversable by all users)
sudo find "${PackagePath}/etc" -type d -exec chmod 755 {} +
# set all regular files to 644 (readable by all users)
sudo find "${PackagePath}/etc" -type f -exec chmod 644 {} +
# ensure main binaries are 755 (executable by all users)
# build deb package
sudo dpkg-deb -Zzstd -z19 --build "$PackagePath"
sudo mv "${PackagePath}.deb" "v2rayn-unofficial-repo_${Version}_all.deb"

61
package-debian.sh Executable file → Normal file
View File

@@ -1,58 +1,38 @@
#!/usr/bin/env bash #!/bin/bash
set -euo pipefail
# Root directory = the script's location Arch="$1"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" OutputPath="$2"
cd "$SCRIPT_DIR" Version="$3"
source ./utils.sh
Arch="linux-64" FileName="v2rayN-${Arch}.zip"
OutputPath="$(mktemp -d)" wget -nv -O $FileName "https://github.com/2dust/v2rayN-core-bin/raw/refs/heads/master/$FileName"
Version="$1" 7z x $FileName
cp -rf v2rayN-${Arch}/* $OutputPath
PROJ="./v2rayN/v2rayN.Desktop/v2rayN.Desktop.csproj" PackagePath="v2rayN-Package-${Arch}"
dotnet restore "$PROJ"
sudo rm -rf "$(dirname "$PROJ")/bin/Release/net8.0"
dotnet publish "${PROJ}" -c Release -r "linux-x64" --self-contained -p:StripSymbols=true -o "$OutputPath"
PROJ="./v2rayN/AmazTool/AmazTool.csproj"
dotnet restore "$PROJ"
sudo rm -rf "$(dirname "$PROJ")/bin/Release/net8.0"
dotnet publish "${PROJ}" -c Release -r "linux-x64" --self-contained -p:StripSymbols=true -p:PublishTrimmed=true -o "$OutputPath"
export RID_DIR="linux-x64"
download_xray "$OutputPath/bin/xray"
download_singbox "$OutputPath/bin/sing_box"
download_geo_assets "$OutputPath"
PackagePath="v2rayn-unofficial"
mkdir -p "${PackagePath}/DEBIAN" mkdir -p "${PackagePath}/DEBIAN"
mkdir -p "${PackagePath}/opt" mkdir -p "${PackagePath}/opt"
cp -rf "$OutputPath" "${PackagePath}/opt/v2rayN" cp -rf $OutputPath "${PackagePath}/opt/v2rayN"
echo "When this file exists, app will not store configs under this folder" >"${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt" echo "When this file exists, app will not store configs under this folder" > "${PackagePath}/opt/v2rayN/NotStoreConfigHere.txt"
sudo find "${PackagePath}/opt/v2rayN" -type f -name "*.so" -exec strip {} +
if [ "$Arch" = "linux-64" ]; then if [ $Arch = "linux-64" ]; then
Arch2="amd64" Arch2="amd64"
else else
Arch2="arm64" Arch2="arm64"
fi fi
echo $Arch2
# basic # basic
cat >"${PackagePath}/DEBIAN/control" <<-EOF cat >"${PackagePath}/DEBIAN/control" <<-EOF
Package: v2rayn-unofficial Package: v2rayN
Version: $Version Version: $Version
Maintainer: Vlyaii <voronin9032n3@gmail.com>
Homepage: https://git.vlyaii.ru/voronin9032/v2rayN
Architecture: $Arch2 Architecture: $Arch2
Replaces: v2rayn Maintainer: https://github.com/2dust/v2rayN
Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1) Depends: libc6 (>= 2.34), fontconfig (>= 2.13.1), desktop-file-utils (>= 0.26), xdg-utils (>= 1.1.3), coreutils (>= 8.32), bash (>= 5.1), libfreetype6 (>= 2.11)
Breaks: v2rayn
Conflicts: v2rayn
Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others Description: A GUI client for Windows and Linux, support Xray core and sing-box-core and others
EOF EOF
cat >"${PackagePath}/DEBIAN/postinst" <<-EOF cat >"${PackagePath}/DEBIAN/postinst" <<-EOF
#!/bin/sh
if [ ! -s /usr/share/applications/v2rayN.desktop ]; then if [ ! -s /usr/share/applications/v2rayN.desktop ]; then
cat >/usr/share/applications/v2rayN.desktop<<-END cat >/usr/share/applications/v2rayN.desktop<<-END
[Desktop Entry] [Desktop Entry]
@@ -85,6 +65,5 @@ sudo chmod 755 "${PackagePath}/opt/v2rayN/v2rayN" 2>/dev/null || true
sudo chmod 755 "${PackagePath}/opt/v2rayN/AmazTool" 2>/dev/null || true sudo chmod 755 "${PackagePath}/opt/v2rayN/AmazTool" 2>/dev/null || true
# build deb package # build deb package
sudo dpkg-deb -Zzstd -z19 --build "$PackagePath" sudo dpkg-deb -Zxz --build $PackagePath
sudo mv "${PackagePath}.deb" "v2rayn-unofficial_${Version}_${Arch2}.deb" sudo mv "${PackagePath}.deb" "v2rayN-${Arch}.deb"
sudo rm -rf "$OutputPath"

View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
Arch="$1" Arch="$1"
OutputPath="$2" OutputPath="$2"
@@ -13,7 +13,7 @@ PackagePath="v2rayN-Package-${Arch}"
mkdir -p "$PackagePath/v2rayN.app/Contents/Resources" mkdir -p "$PackagePath/v2rayN.app/Contents/Resources"
cp -rf "$OutputPath" "$PackagePath/v2rayN.app/Contents/MacOS" cp -rf "$OutputPath" "$PackagePath/v2rayN.app/Contents/MacOS"
cp -f "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.icns" "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns" cp -f "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN.icns" "$PackagePath/v2rayN.app/Contents/Resources/AppIcon.icns"
echo "When this file exists, app will not store configs under this folder" >"$PackagePath/v2rayN.app/Contents/MacOS/NotStoreConfigHere.txt" echo "When this file exists, app will not store configs under this folder" > "$PackagePath/v2rayN.app/Contents/MacOS/NotStoreConfigHere.txt"
chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN" chmod +x "$PackagePath/v2rayN.app/Contents/MacOS/v2rayN"
cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
@@ -43,16 +43,18 @@ cat >"$PackagePath/v2rayN.app/Contents/Info.plist" <<-EOF
<true/> <true/>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>
<true/> <true/>
<key>LSMinimumSystemVersion</key>
<string>12.7</string>
</dict> </dict>
</plist> </plist>
EOF EOF
create-dmg \ create-dmg \
--volname "v2rayN Installer" \ --volname "v2rayN Installer" \
--window-size 700 420 \ --window-size 700 420 \
--icon-size 100 \ --icon-size 100 \
--icon "v2rayN.app" 160 185 \ --icon "v2rayN.app" 160 185 \
--hide-extension "v2rayN.app" \ --hide-extension "v2rayN.app" \
--app-drop-link 500 185 \ --app-drop-link 500 185 \
"v2rayN-${Arch}.dmg" \ "v2rayN-${Arch}.dmg" \
"$PackagePath/v2rayN.app" "$PackagePath/v2rayN.app"

4
package-release-zip.sh Executable file → Normal file
View File

@@ -1,4 +1,4 @@
#!/usr/bin/env bash #!/bin/bash
Arch="$1" Arch="$1"
OutputPath="$2" OutputPath="$2"
@@ -12,4 +12,4 @@ ZipPath64="./$OutputArch"
mkdir $ZipPath64 mkdir $ZipPath64
cp -rf $OutputPath "$ZipPath64/$OutputArch" cp -rf $OutputPath "$ZipPath64/$OutputArch"
7z a -tZip $FileName "$ZipPath64/$OutputArch" -mx1 7z a -tZip $FileName "$ZipPath64/$OutputArch" -mx1

672
package-rhel.sh Executable file → Normal file
View File

@@ -1,18 +1,18 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu == # == Require Red Hat Enterprise Linux/FedoraLinux/RockyLinux/AlmaLinux/CentOS OR Ubuntu/Debian ==
if [[ -r /etc/os-release ]]; then if [[ -r /etc/os-release ]]; then
source /etc/os-release . /etc/os-release
case "$ID" in case "$ID" in
rhel | rocky | almalinux | fedora | centos | ubuntu) rhel|rocky|almalinux|fedora|centos|ubuntu|debian)
echo "[OK] Detected supported system: $NAME $VERSION_ID" echo "[OK] Detected supported system: $NAME $VERSION_ID"
;; ;;
*) *)
echo "[ERROR] Unsupported system: $NAME ($ID)." echo "[ERROR] Unsupported system: $NAME ($ID)."
echo "This script only supports Red Hat Enterprise Linux/RockyLinux/AlmaLinux/CentOS or Ubuntu." echo "This script only supports Red Hat Enterprise Linux/RockyLinux/AlmaLinux/CentOS or Ubuntu/Debian."
exit 1 exit 1
;; ;;
esac esac
else else
echo "[ERROR] Cannot detect system (missing /etc/os-release)." echo "[ERROR] Cannot detect system (missing /etc/os-release)."
@@ -37,9 +37,12 @@ fi
echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}." echo "[OK] Kernel version >= ${MIN_KERNEL_MAJOR}.${MIN_KERNEL_MINOR}."
# ===== Config & Parse arguments ========================================================= # ===== Config & Parse arguments =========================================================
VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty VERSION_ARG="${1:-}" # Pass version number like 7.13.8, or leave empty
WITH_CORE="both" # Default: bundle both xray+sing-box WITH_CORE="both" # Default: bundle both xray+sing-box
ARCH_OVERRIDE="" # --arch x64|arm64|all (optional compile target) AUTOSTART=0 # 1 = enable system-wide autostart (/etc/xdg/autostart)
FORCE_NETCORE=0 # --netcore => skip archive bundle, use separate downloads
ARCH_OVERRIDE="" # --arch x64|arm64|all (optional compile target)
BUILD_FROM="" # --buildfrom 1|2|3 to select channel non-interactively
# If the first argument starts with --, do not treat it as a version number # If the first argument starts with --, do not treat it as a version number
if [[ "${VERSION_ARG:-}" == --* ]]; then if [[ "${VERSION_ARG:-}" == --* ]]; then
@@ -51,86 +54,97 @@ if [[ -n "${VERSION_ARG:-}" ]]; then shift || true; fi
# Parse remaining optional arguments # Parse remaining optional arguments
while [[ $# -gt 0 ]]; do while [[ $# -gt 0 ]]; do
case "$1" in case "$1" in
--with-core) --with-core) WITH_CORE="${2:-both}"; shift 2;;
WITH_CORE="${2:-both}" --autostart) AUTOSTART=1; shift;;
shift 2 --xray-ver) XRAY_VER="${2:-}"; shift 2;;
;; --singbox-ver) SING_VER="${2:-}"; shift 2;;
--xray-ver) --netcore) FORCE_NETCORE=1; shift;;
XRAY_VER="${2:-}" --arch) ARCH_OVERRIDE="${2:-}"; shift 2;;
shift 2 --buildfrom) BUILD_FROM="${2:-}"; shift 2;;
;; *)
--singbox-ver) if [[ -z "${VERSION_ARG:-}" ]]; then VERSION_ARG="$1"; fi
SING_VER="${2:-}" shift;;
shift 2
;;
--arch)
ARCH_OVERRIDE="${2:-}"
shift 2
;;
--release)
RPM_RELEASE="${2:-}"
shift 2
;;
*)
if [[ -z "${VERSION_ARG:-}" ]]; then VERSION_ARG="$1"; fi
shift
;;
esac esac
done done
if [[ -z "${RPM_RELEASE:-}" ]]; then # Conflict: version number AND --buildfrom cannot be used together
echo "--release is required" if [[ -n "${VERSION_ARG:-}" && -n "${BUILD_FROM:-}" ]]; then
echo "[ERROR] You cannot specify both an explicit version and --buildfrom at the same time."
echo " Provide either a version (e.g. 7.14.0) OR --buildfrom 1|2|3."
exit 1 exit 1
fi fi
# ===== Environment check + Dependencies ======================================== # ===== Environment check + Dependencies ========================================
host_arch="$(uname -m)" host_arch="$(uname -m)"
if ! [[ "$host_arch" == "aarch64" || "$host_arch" == "x86_64" ]]; then [[ "$host_arch" == "aarch64" || "$host_arch" == "x86_64" ]] || { echo "Only supports aarch64 / x86_64"; exit 1; }
echo "Only supports aarch64 / x86_64"
exit 1
fi
install_ok=0 install_ok=0
case "$ID" in case "$ID" in
# ------------------------------ RHEL family (UNCHANGED) ------------------------------ # ------------------------------ RHEL family (UNCHANGED) ------------------------------
rhel | rocky | almalinux | centos) rhel|rocky|almalinux|centos)
if command -v dnf >/dev/null 2>&1; then if command -v dnf >/dev/null 2>&1; then
sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || sudo dnf -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
sudo dnf -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync sudo dnf -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync
install_ok=1 install_ok=1
elif command -v yum >/dev/null 2>&1; then elif command -v yum >/dev/null 2>&1; then
sudo yum -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || sudo yum -y install dotnet-sdk-8.0 rpm-build rpmdevtools curl unzip tar rsync || \
sudo yum -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync sudo yum -y install dotnet-sdk rpm-build rpmdevtools curl unzip tar rsync
install_ok=1 install_ok=1
fi fi
;; ;;
# ------------------------------ Ubuntu ---------------------------------------------- # ------------------------------ Ubuntu ----------------------------------------------
ubuntu) ubuntu)
sudo apt-get update
# Ensure 'universe' (Ubuntu) to get 'rpm'
if ! apt-cache policy | grep -q '^500 .*ubuntu.com/ubuntu.* universe'; then
sudo apt-get -y install software-properties-common || true
sudo add-apt-repository -y universe || true
sudo apt-get update sudo apt-get update
fi # Ensure 'universe' (Ubuntu) to get 'rpm'
# Base tools + rpm (provides rpmbuild) if ! apt-cache policy | grep -q '^500 .*ubuntu.com/ubuntu.* universe'; then
sudo apt-get -y install curl unzip tar rsync rpm || true sudo apt-get -y install software-properties-common || true
# Cross-arch binutils so strip matches target arch + objdump for brp scripts sudo add-apt-repository -y universe || true
sudo apt-get -y install binutils binutils-x86-64-linux-gnu binutils-aarch64-linux-gnu || true sudo apt-get update
# rpmbuild presence check fi
if ! command -v rpmbuild >/dev/null 2>&1; then # Base tools + rpm (provides rpmbuild)
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'." sudo apt-get -y install curl unzip tar rsync rpm || true
echo " Please ensure the 'rpm' package is available from your repos (universe on Ubuntu)." # Cross-arch binutils so strip matches target arch + objdump for brp scripts
exit 1 sudo apt-get -y install binutils binutils-x86-64-linux-gnu binutils-aarch64-linux-gnu || true
fi # rpmbuild presence check
# .NET SDK 8 (best effort via apt) if ! command -v rpmbuild >/dev/null 2>&1; then
if ! command -v dotnet >/dev/null 2>&1; then echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
sudo apt-get -y install dotnet-sdk-8.0 || true echo " Please ensure the 'rpm' package is available from your repos (universe on Ubuntu)."
sudo apt-get -y install dotnet-sdk-8 || true exit 1
sudo apt-get -y install dotnet-sdk || true fi
fi # .NET SDK 8 (best effort via apt)
install_ok=1 if ! command -v dotnet >/dev/null 2>&1; then
;; sudo apt-get -y install dotnet-sdk-8.0 || true
sudo apt-get -y install dotnet-sdk-8 || true
sudo apt-get -y install dotnet-sdk || true
fi
install_ok=1
;;
# ------------------------------ Debian (KEEP, with local dotnet install) ------------
debian)
sudo apt-get update
# Base tools + rpm (provides rpmbuild on Debian) + objdump/strip
sudo apt-get -y install curl unzip tar rsync rpm binutils || true
# rpmbuild presence check
if ! command -v rpmbuild >/dev/null 2>&1; then
echo "[ERROR] 'rpmbuild' not found after installing 'rpm'."
echo " Please ensure 'rpm' is available from Debian repos."
exit 1
fi
# Try apt for dotnet; fallback to official installer into $HOME/.dotnet
if ! command -v dotnet >/dev/null 2>&1; then
echo "[INFO] 'dotnet' not found. Installing .NET 8 SDK locally to \$HOME/.dotnet ..."
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fsSL https://dot.net/v1/dotnet-install.sh -o "$tmp/dotnet-install.sh"
bash "$tmp/dotnet-install.sh" --channel 8.0 --install-dir "$HOME/.dotnet"
export PATH="$HOME/.dotnet:$HOME/.dotnet/tools:$PATH"
export DOTNET_ROOT="$HOME/.dotnet"
if ! command -v dotnet >/dev/null 2>&1; then
echo "[ERROR] dotnet installation failed."
exit 1
fi
fi
install_ok=1
;;
esac esac
if [[ "$install_ok" -ne 1 ]]; then if [[ "$install_ok" -ne 1 ]]; then
@@ -144,8 +158,6 @@ command -v curl >/dev/null
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR" cd "$SCRIPT_DIR"
source ./utils.sh
# Git submodules (best effort) # Git submodules (best effort)
if [[ -f .gitmodules ]]; then if [[ -f .gitmodules ]]; then
git submodule sync --recursive || true git submodule sync --recursive || true
@@ -157,16 +169,348 @@ PROJECT="v2rayN.Desktop/v2rayN.Desktop.csproj"
if [[ ! -f "$PROJECT" ]]; then if [[ ! -f "$PROJECT" ]]; then
PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)" PROJECT="$(find . -maxdepth 3 -name 'v2rayN.Desktop.csproj' | head -n1 || true)"
fi fi
[[ -f "$PROJECT" ]] || { [[ -f "$PROJECT" ]] || { echo "v2rayN.Desktop.csproj not found"; exit 1; }
echo "v2rayN.Desktop.csproj not found"
exit 1 # ===== Resolve GUI version & auto checkout ============================================
VERSION=""
choose_channel() {
# If --buildfrom provided, map it directly and skip interaction.
if [[ -n "${BUILD_FROM:-}" ]]; then
case "$BUILD_FROM" in
1) echo "latest"; return 0;;
2) echo "prerelease"; return 0;;
3) echo "keep"; return 0;;
*) echo "[ERROR] Invalid --buildfrom value: ${BUILD_FROM}. Use 1|2|3." >&2; exit 1;;
esac
fi
# Print menu to stderr and read from /dev/tty so stdout only carries the token.
local ch="latest" sel=""
if [[ -t 0 ]]; then
echo "[?] Choose v2rayN release channel:" >&2
echo " 1) Latest (stable) [default]" >&2
echo " 2) Pre-release (preview)" >&2
echo " 3) Keep current (do nothing)" >&2
printf "Enter 1, 2 or 3 [default 1]: " >&2
if read -r sel </dev/tty; then
case "${sel:-}" in
2) ch="prerelease" ;;
3) ch="keep" ;;
*) ch="latest" ;;
esac
else
ch="latest"
fi
else
ch="latest"
fi
echo "$ch"
} }
VERSION="$VERSION_ARG" get_latest_tag_latest() {
# Resolve /releases/latest → tag_name
curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases/latest" \
| grep -Eo '"tag_name":\s*"v?[^"]+"' \
| head -n1 \
| sed -E 's/.*"tag_name":\s*"v?([^"]+)".*/\1/'
}
get_latest_tag_prerelease() {
# Resolve newest prerelease=true tag; prefer jq, fallback to sed/grep (no awk)
local json tag
json="$(curl -fsSL "https://api.github.com/repos/2dust/v2rayN/releases?per_page=20")" || return 1
# 1) Use jq if present
if command -v jq >/dev/null 2>&1; then
tag="$(printf '%s' "$json" \
| jq -r '[.[] | select(.prerelease==true)][0].tag_name' 2>/dev/null \
| sed 's/^v//')" || true
fi
# 2) Fallback to sed/grep only
if [[ -z "${tag:-}" || "${tag:-}" == "null" ]]; then
tag="$(printf '%s' "$json" \
| tr '\n' ' ' \
| sed 's/},[[:space:]]*{/\n/g' \
| grep -m1 -E '"prerelease"[[:space:]]*:[[:space:]]*true' \
| grep -Eo '"tag_name"[[:space:]]*:[[:space:]]*"v?[^"]+"' \
| head -n1 \
| sed -E 's/.*"tag_name"[[:space:]]*:[[:space:]]*"v?([^"]+)".*/\1/')" || true
fi
[[ -n "${tag:-}" && "${tag:-}" != "null" ]] || return 1
printf '%s\n' "$tag"
}
git_try_checkout() {
# Try a series of refs and checkout when found.
local want="$1" ref=""
if git rev-parse --git-dir >/dev/null 2>&1; then
git fetch --tags --force --prune --depth=1 || true
if git rev-parse "refs/tags/v${want}" >/dev/null 2>&1; then
ref="v${want}"
elif git rev-parse "refs/tags/${want}" >/dev/null 2>&1; then
ref="${want}"
elif git rev-parse --verify "${want}" >/dev/null 2>&1; then
ref="${want}"
fi
if [[ -n "$ref" ]]; then
echo "[OK] Found ref '${ref}', checking out..."
git checkout -f "${ref}"
if [[ -f .gitmodules ]]; then
git submodule sync --recursive || true
git submodule update --init --recursive || true
fi
return 0
fi
fi
return 1
}
if git rev-parse --git-dir >/dev/null 2>&1; then
if [[ -n "${VERSION_ARG:-}" ]]; then
echo "[*] Trying to switch v2rayN repo to version: ${VERSION_ARG}"
if git_try_checkout "${VERSION_ARG#v}"; then
VERSION="${VERSION_ARG#v}"
else
echo "[WARN] Tag '${VERSION_ARG}' not found."
ch="$(choose_channel)"
if [[ "$ch" == "keep" ]]; then
echo "[*] Keep current repository state (no checkout)."
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
VERSION="$(git describe --tags --abbrev=0)"
else
VERSION="0.0.0+git"
fi
VERSION="${VERSION#v}"
else
echo "[*] Resolving ${ch} tag from GitHub releases..."
tag=""
if [[ "$ch" == "prerelease" ]]; then
tag="$(get_latest_tag_prerelease || true)"
if [[ -z "$tag" ]]; then
echo "[WARN] Failed to resolve prerelease tag, falling back to latest."
tag="$(get_latest_tag_latest || true)"
fi
else
tag="$(get_latest_tag_latest || true)"
fi
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
echo "[*] Latest tag for '${ch}': ${tag}"
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
VERSION="${tag#v}"
fi
fi
else
ch="$(choose_channel)"
if [[ "$ch" == "keep" ]]; then
echo "[*] Keep current repository state (no checkout)."
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
VERSION="$(git describe --tags --abbrev=0)"
else
VERSION="0.0.0+git"
fi
VERSION="${VERSION#v}"
else
echo "[*] Resolving ${ch} tag from GitHub releases..."
tag=""
if [[ "$ch" == "prerelease" ]]; then
tag="$(get_latest_tag_prerelease || true)"
if [[ -z "$tag" ]]; then
echo "[WARN] Failed to resolve prerelease tag, falling back to latest."
tag="$(get_latest_tag_latest || true)"
fi
else
tag="$(get_latest_tag_latest || true)"
fi
[[ -n "$tag" ]] || { echo "[ERROR] Failed to resolve latest tag for channel '${ch}'."; exit 1; }
echo "[*] Latest tag for '${ch}': ${tag}"
git_try_checkout "$tag" || { echo "[ERROR] Failed to checkout '${tag}'."; exit 1; }
VERSION="${tag#v}"
fi
fi
else
echo "[WARN] Current directory is not a git repo; cannot checkout version. Proceeding on current tree."
VERSION="${VERSION_ARG:-}"
if [[ -z "$VERSION" ]]; then
if git describe --tags --abbrev=0 >/dev/null 2>&1; then
VERSION="$(git describe --tags --abbrev=0)"
else
VERSION="0.0.0+git"
fi
fi
VERSION="${VERSION#v}"
fi
echo "[*] GUI version resolved as: ${VERSION}"
# ===== Helpers for core/rules download (use RID_DIR for arch sync) =====================
download_xray() {
# Download Xray core and install to outdir/xray
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
mkdir -p "$outdir"
if [[ -n "${XRAY_VER:-}" ]]; then ver="${XRAY_VER}"; fi
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
[[ -n "$ver" ]] || { echo "[xray] Failed to get version"; return 1; }
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-arm64-v8a.zip"
else
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-64.zip"
fi
echo "[+] Download xray: $url"
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$zipname"
unzip -q "$tmp/$zipname" -d "$tmp"
install -Dm755 "$tmp/xray" "$outdir/xray"
}
download_singbox() {
# Download sing-box core and install to outdir/sing-box
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -n "${SING_VER:-}" ]]; then ver="${SING_VER}"; fi
if [[ -z "$ver" ]]; then
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest \
| grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
[[ -n "$ver" ]] || { echo "[sing-box] Failed to get version"; return 1; }
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-arm64.tar.gz"
else
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-amd64.tar.gz"
fi
echo "[+] Download sing-box: $url"
tmp="$(mktemp -d)"; trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$tarname"
tar -C "$tmp" -xzf "$tmp/$tarname"
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
[[ -n "$bin" ]] || { echo "[!] sing-box unpack failed"; return 1; }
install -Dm755 "$bin" "$outdir/sing-box"
}
# ---- NEW: download_mihomo (REQUIRED in --netcore mode) ----
download_mihomo() {
# Download mihomo into outroot/bin/mihomo/mihomo
local outroot="$1"
local url=""
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64/bin/mihomo/mihomo"
else
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64/bin/mihomo/mihomo"
fi
echo "[+] Download mihomo: $url"
mkdir -p "$outroot/bin/mihomo"
curl -fL "$url" -o "$outroot/bin/mihomo/mihomo"
chmod +x "$outroot/bin/mihomo/mihomo" || true
}
# Move geo files to a unified path: outroot/bin
unify_geo_layout() {
local outroot="$1"
mkdir -p "$outroot/bin"
local names=( \
"geosite.dat" \
"geoip.dat" \
"geoip-only-cn-private.dat" \
"Country.mmdb" \
"geoip.metadb" \
)
for n in "${names[@]}"; do
# If file exists under bin/xray/, move it up to bin/
if [[ -f "$outroot/bin/xray/$n" ]]; then
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
fi
# If file already in bin/, leave it as-is
if [[ -f "$outroot/bin/$n" ]]; then
:
fi
done
}
# Download geo/rule assets; then unify to bin/
download_geo_assets() {
local outroot="$1"
local bin_dir="$outroot/bin"
local srss_dir="$bin_dir/srss"
mkdir -p "$bin_dir" "$srss_dir"
echo "[+] Download Xray Geo to ${bin_dir}"
curl -fsSL -o "$bin_dir/geosite.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geosite.dat"
curl -fsSL -o "$bin_dir/geoip.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geoip.dat"
curl -fsSL -o "$bin_dir/geoip-only-cn-private.dat" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat"
curl -fsSL -o "$bin_dir/Country.mmdb" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb"
echo "[+] Download sing-box rule DB & rule-sets"
curl -fsSL -o "$bin_dir/geoip.metadb" \
"https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geoip.metadb" || true
for f in \
geoip-private.srs geoip-cn.srs geoip-facebook.srs geoip-fastly.srs \
geoip-google.srs geoip-netflix.srs geoip-telegram.srs geoip-twitter.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geoip/$f" || true
done
for f in \
geosite-cn.srs geosite-gfw.srs geosite-google.srs geosite-greatfire.srs \
geosite-geolocation-cn.srs geosite-category-ads-all.srs geosite-private.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
done
# Unify to bin/
unify_geo_layout "$outroot"
}
# Prefer the prebuilt v2rayN core bundle; then unify geo layout
download_v2rayn_bundle() {
local outroot="$1"
local url=""
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-arm64.zip"
else
url="https://raw.githubusercontent.com/2dust/v2rayN-core-bin/refs/heads/master/v2rayN-linux-64.zip"
fi
echo "[+] Try v2rayN bundle archive: $url"
local tmp zipname
tmp="$(mktemp -d)"; zipname="$tmp/v2rayn.zip"
curl -fL "$url" -o "$zipname" || { echo "[!] Bundle download failed"; return 1; }
unzip -q "$zipname" -d "$tmp" || { echo "[!] Bundle unzip failed"; return 1; }
if [[ -d "$tmp/bin" ]]; then
mkdir -p "$outroot/bin"
rsync -a "$tmp/bin/" "$outroot/bin/"
else
rsync -a "$tmp/" "$outroot/"
fi
rm -f "$outroot/v2rayn.zip" 2>/dev/null || true
# keep mihomo
# find "$outroot" -type d -name "mihomo" -prune -exec rm -rf {} + 2>/dev/null || true
local nested_dir
nested_dir="$(find "$outroot" -maxdepth 1 -type d -name 'v2rayN-linux-*' | head -n1 || true)"
if [[ -n "${nested_dir:-}" && -d "$nested_dir/bin" ]]; then
mkdir -p "$outroot/bin"
rsync -a "$nested_dir/bin/" "$outroot/bin/"
rm -rf "$nested_dir"
fi
# Unify to bin/
unify_geo_layout "$outroot"
echo "[+] Bundle extracted to $outroot"
}
# ===== Build results collection for --arch all ======================================== # ===== Build results collection for --arch all ========================================
BUILT_RPMS=() # Will collect absolute paths of built RPMs BUILT_RPMS=() # Will collect absolute paths of built RPMs
BUILT_ALL=0 # Flag to know if we should print the final summary BUILT_ALL=0 # Flag to know if we should print the final summary
# ===== Build (single-arch) function ==================================================== # ===== Build (single-arch) function ====================================================
build_for_arch() { build_for_arch() {
@@ -174,41 +518,29 @@ build_for_arch() {
local short="$1" local short="$1"
local rid rpm_target archdir local rid rpm_target archdir
case "$short" in case "$short" in
x64) x64) rid="linux-x64"; rpm_target="x86_64"; archdir="x86_64" ;;
rid="linux-x64" arm64) rid="linux-arm64"; rpm_target="aarch64"; archdir="aarch64" ;;
rpm_target="x86_64" *) echo "[ERROR] Unknown arch '$short' (use x64|arm64)"; return 1;;
archdir="x86_64"
;;
arm64)
rid="linux-arm64"
rpm_target="aarch64"
archdir="aarch64"
;;
*)
echo "[ERROR] Unknown arch '$short' (use x64|arm64)"
return 1
;;
esac esac
echo "[*] Building for target: $short (RID=$rid, RPM --target $rpm_target)" echo "[*] Building for target: $short (RID=$rid, RPM --target $rpm_target)"
# .NET publish (self-contained) for this RID # .NET publish (self-contained) for this RID
dotnet restore "$PROJECT" dotnet clean "$PROJECT" -c Release
rm -rf "$(dirname "$PROJECT")/bin/Release/net8.0" || true rm -rf "$(dirname "$PROJECT")/bin/Release/net8.0" || true
dotnet restore "$PROJECT"
dotnet publish "$PROJECT" \ dotnet publish "$PROJECT" \
-c Release -r "$rid" \ -c Release -r "$rid" \
--sc \
-p:PublishSingleFile=false \ -p:PublishSingleFile=false \
-p:SelfContained=true \ -p:SelfContained=true \
-p:IncludeNativeLibrariesForSelfExtract=true \ -p:IncludeNativeLibrariesForSelfExtract=true
-p:StripSymbols=true
# Per-arch variables (scoped) # Per-arch variables (scoped)
local RID_DIR="$rid" local RID_DIR="$rid"
local PUBDIR local PUBDIR
PUBDIR="$(dirname "$PROJECT")/bin/Release/net8.0/${RID_DIR}/publish" PUBDIR="$(dirname "$PROJECT")/bin/Release/net8.0/${RID_DIR}/publish"
[[ -d "$PUBDIR" ]] [[ -d "$PUBDIR" ]]
sudo find "$PUBDIR" -type f -name "*.so" -exec strip {} +
# Make RID_DIR visible to download helpers (they read this var) # Make RID_DIR visible to download helpers (they read this var)
export RID_DIR export RID_DIR
@@ -248,13 +580,31 @@ build_for_arch() {
mkdir -p "$WORKDIR/$PKGROOT/bin/xray" "$WORKDIR/$PKGROOT/bin/sing_box" mkdir -p "$WORKDIR/$PKGROOT/bin/xray" "$WORKDIR/$PKGROOT/bin/sing_box"
# Bundle / cores per-arch # Bundle / cores per-arch
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then if [[ "$FORCE_NETCORE" -eq 0 ]]; then
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)" if download_v2rayn_bundle "$WORKDIR/$PKGROOT"; then
echo "[*] Using v2rayN bundle archive."
else
echo "[*] Bundle failed, fallback to separate core + rules."
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
fi
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
fi
else
echo "[*] --netcore specified: use separate core + rules."
if [[ "$WITH_CORE" == "xray" || "$WITH_CORE" == "both" ]]; then
download_xray "$WORKDIR/$PKGROOT/bin/xray" || echo "[!] xray download failed (skipped)"
fi
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
# ---- REQUIRED: always fetch mihomo in netcore mode, per-arch ----
download_mihomo "$WORKDIR/$PKGROOT" || echo "[!] mihomo download failed (skipped)"
fi fi
if [[ "$WITH_CORE" == "sing-box" || "$WITH_CORE" == "both" ]]; then
download_singbox "$WORKDIR/$PKGROOT/bin/sing_box" || echo "[!] sing-box download failed (skipped)"
fi
download_geo_assets "$WORKDIR/$PKGROOT" || echo "[!] Geo rules download failed (skipped)"
# Tarball # Tarball
mkdir -p "$SOURCEDIR" mkdir -p "$SOURCEDIR"
@@ -263,34 +613,32 @@ build_for_arch() {
# SPEC # SPEC
local SPECFILE="$SPECDIR/v2rayN.spec" local SPECFILE="$SPECDIR/v2rayN.spec"
mkdir -p "$SPECDIR" mkdir -p "$SPECDIR"
cat >"$SPECFILE" <<'SPEC' cat > "$SPECFILE" <<'SPEC'
%global debug_package %{nil} %global debug_package %{nil}
%undefine _debuginfo_subpackages %undefine _debuginfo_subpackages
%undefine _debugsource_packages %undefine _debugsource_packages
# Ignore outdated LTTng dependencies incorrectly reported by the .NET runtime (to avoid installation failures) # Ignore outdated LTTng dependencies incorrectly reported by the .NET runtime (to avoid installation failures)
%global __requires_exclude ^liblttng-ust\.so\..*$ %global __requires_exclude ^liblttng-ust\.so\..*$
Name: v2rayn-unofficial Name: v2rayN
Version: __VERSION__ Version: __VERSION__
Release: __RELEASE__ Release: 1%{?dist}
Summary: v2rayN (Avalonia) GUI client for Linux (x86_64/aarch64) Summary: v2rayN (Avalonia) GUI client for Linux (x86_64/aarch64)
License: GPL-3.0-only License: GPL-3.0-only
URL: https://git.vlyaii.ru/voronin9032/v2rayN URL: https://github.com/2dust/v2rayN
BugURL: https://github.com/2dust/v2rayN/issues BugURL: https://github.com/2dust/v2rayN/issues
ExclusiveArch: aarch64 x86_64 ExclusiveArch: aarch64 x86_64
Source0: __PKGROOT__.tar.gz Source0: __PKGROOT__.tar.gz
# Runtime dependencies (Avalonia / X11 / Fonts / GL) # Runtime dependencies (Avalonia / X11 / Fonts / GL)
Requires: freetype, cairo, pango, openssl, mesa-libEGL, mesa-libGL Requires: cairo, pango, openssl, mesa-libEGL, mesa-libGL
Requires: glibc >= 2.34 Requires: glibc >= 2.34
Requires: fontconfig >= 2.13.1 Requires: fontconfig >= 2.13.1
Requires: desktop-file-utils >= 0.26 Requires: desktop-file-utils >= 0.26
Requires: xdg-utils >= 1.1.3 Requires: xdg-utils >= 1.1.3
Requires: coreutils >= 8.32 Requires: coreutils >= 8.32
Requires: bash >= 5.1 Requires: bash >= 5.1
Requires: freetype >= 2.10
Conflicts: v2rayN
Obsoletes: v2rayN
%description %description
v2rayN Linux for Red Hat Enterprise Linux v2rayN Linux for Red Hat Enterprise Linux
@@ -364,9 +712,41 @@ fi
%{_datadir}/icons/hicolor/256x256/apps/v2rayn.png %{_datadir}/icons/hicolor/256x256/apps/v2rayn.png
SPEC SPEC
# Autostart injection (inside %install) and %files entry
if [[ "$AUTOSTART" -eq 1 ]]; then
awk '
BEGIN{ins=0}
/^%post$/ && !ins {
print "# --- Autostart (.desktop) ---"
print "install -dm0755 %{buildroot}/etc/xdg/autostart"
print "cat > %{buildroot}/etc/xdg/autostart/v2rayn.desktop << '\''EOF'\''"
print "[Desktop Entry]"
print "Type=Application"
print "Name=v2rayN (Autostart)"
print "Exec=v2rayn"
print "X-GNOME-Autostart-enabled=true"
print "NoDisplay=false"
print "EOF"
ins=1
}
{print}
' "$SPECFILE" > "${SPECFILE}.tmp" && mv "${SPECFILE}.tmp" "$SPECFILE"
awk '
BEGIN{infiles=0; done=0}
/^%files$/ {infiles=1}
infiles && done==0 && $0 ~ /%{_datadir}\/icons\/hicolor\/256x256\/apps\/v2rayn\.png/ {
print
print "%config(noreplace) /etc/xdg/autostart/v2rayn.desktop"
done=1
next
}
{print}
' "$SPECFILE" > "${SPECFILE}.tmp" && mv "${SPECFILE}.tmp" "$SPECFILE"
fi
# Replace placeholders # Replace placeholders
sed -i "s/__VERSION__/${VERSION}/g" "$SPECFILE" sed -i "s/__VERSION__/${VERSION}/g" "$SPECFILE"
sed -i "s/__RELEASE__/${RPM_RELEASE}/g" "$SPECFILE"
sed -i "s/__PKGROOT__/${PKGROOT}/g" "$SPECFILE" sed -i "s/__PKGROOT__/${PKGROOT}/g" "$SPECFILE"
# ----- Select proper 'strip' per target arch on Ubuntu only (cross-binutils) ----- # ----- Select proper 'strip' per target arch on Ubuntu only (cross-binutils) -----
@@ -381,7 +761,7 @@ SPEC
STRIP_BIN="/usr/bin/aarch64-linux-gnu-strip" STRIP_BIN="/usr/bin/aarch64-linux-gnu-strip"
fi fi
if [[ -x "$STRIP_BIN" ]]; then if [[ -x "$STRIP_BIN" ]]; then
STRIP_ARGS=(--define "__strip $STRIP_BIN") STRIP_ARGS=( --define "__strip $STRIP_BIN" )
fi fi
fi fi
@@ -401,7 +781,7 @@ SPEC
echo "Build done for $short. RPM at:" echo "Build done for $short. RPM at:"
local f local f
for f in "${TOPDIR}/RPMS/${archdir}/v2rayN-${VERSION}-${RPM_RELEASE}"*.rpm; do for f in "${TOPDIR}/RPMS/${archdir}/v2rayN-${VERSION}-1"*.rpm; do
[[ -e "$f" ]] || continue [[ -e "$f" ]] || continue
echo " $f" echo " $f"
BUILT_RPMS+=("$f") BUILT_RPMS+=("$f")
@@ -410,30 +790,30 @@ SPEC
# ===== Arch selection and build orchestration ========================================= # ===== Arch selection and build orchestration =========================================
case "${ARCH_OVERRIDE:-}" in case "${ARCH_OVERRIDE:-}" in
"") "")
# No --arch: use host architecture # No --arch: use host architecture
if [[ "$host_arch" == "aarch64" ]]; then if [[ "$host_arch" == "aarch64" ]]; then
build_for_arch arm64 build_for_arch arm64
else else
build_for_arch x64
fi
;;
x64|amd64)
build_for_arch x64 build_for_arch x64
fi ;;
;; arm64|aarch64)
x64 | amd64) build_for_arch arm64
build_for_arch x64 ;;
;; all)
arm64 | aarch64) BUILT_ALL=1
build_for_arch arm64 # Build x64 and arm64 separately; each package contains its own arch-only binaries.
;; build_for_arch x64
all) build_for_arch arm64
BUILT_ALL=1 ;;
# Build x64 and arm64 separately; each package contains its own arch-only binaries. *)
build_for_arch x64 echo "[ERROR] Unknown --arch '${ARCH_OVERRIDE}'. Use x64|arm64|all."
build_for_arch arm64 exit 1
;; ;;
*)
echo "[ERROR] Unknown --arch '${ARCH_OVERRIDE}'. Use x64|arm64|all."
exit 1
;;
esac esac
# ===== Final summary if building both arches ========================================== # ===== Final summary if building both arches ==========================================

120
utils.sh
View File

@@ -1,120 +0,0 @@
#!/usr/bin/env bash
download_xray() {
# Download Xray core and install to outdir/xray
local outdir="$1" ver="${XRAY_VER:-}" url tmp zipname="xray.zip"
mkdir -p "$outdir"
if [[ -n "${XRAY_VER:-}" ]]; then ver="${XRAY_VER}"; fi
if [[ -z "$ver" ]]; then
echo "Downloading latest xray"
ver="$(curl -fsSL https://api.github.com/repos/XTLS/Xray-core/releases/latest |
grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
if [[ -z "$ver" ]]; then
echo "[xray] Failed to get version"
return 1
fi
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-arm64-v8a.zip"
else
url="https://github.com/XTLS/Xray-core/releases/download/v${ver}/Xray-linux-64.zip"
fi
echo "[+] Download xray: $url"
tmp="$(mktemp -d)"
trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$zipname"
unzip -q "$tmp/$zipname" -d "$tmp"
install -Dm755 "$tmp/xray" "$outdir/xray"
}
download_singbox() {
# Download sing-box core and install to outdir/sing-box
local outdir="$1" ver="${SING_VER:-}" url tmp tarname="singbox.tar.gz" bin
mkdir -p "$outdir"
if [[ -n "${SING_VER:-}" ]]; then ver="${SING_VER}"; fi
if [[ -z "$ver" ]]; then
echo "Downloading latest sing-box"
ver="$(curl -fsSL https://api.github.com/repos/SagerNet/sing-box/releases/latest |
grep -Eo '"tag_name":\s*"v[^"]+"' | sed -E 's/.*"v([^"]+)".*/\1/' | head -n1)" || true
fi
if [[ -z "$ver" ]]; then
echo "[sing-box] Failed to get version"
return 1
fi
if [[ "$RID_DIR" == "linux-arm64" ]]; then
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-arm64.tar.gz"
else
url="https://github.com/SagerNet/sing-box/releases/download/v${ver}/sing-box-${ver}-linux-amd64.tar.gz"
fi
echo "[+] Download sing-box: $url"
tmp="$(mktemp -d)"
trap '[[ -n "${tmp:-}" ]] && rm -rf "$tmp"' RETURN
curl -fL "$url" -o "$tmp/$tarname"
tar -C "$tmp" -xzf "$tmp/$tarname"
bin="$(find "$tmp" -type f -name 'sing-box' | head -n1 || true)"
[[ -n "$bin" ]] || {
echo "[!] sing-box unpack failed"
return 1
}
install -Dm755 "$bin" "$outdir/sing-box"
}
# Move geo files to a unified path: outroot/bin
unify_geo_layout() {
local outroot="$1"
mkdir -p "$outroot/bin"
local names=(
"geosite.dat"
"geoip.dat"
"geoip-only-cn-private.dat"
"Country.mmdb"
"geoip.metadb"
)
for n in "${names[@]}"; do
# If file exists under bin/xray/, move it up to bin/
if [[ -f "$outroot/bin/xray/$n" ]]; then
mv -f "$outroot/bin/xray/$n" "$outroot/bin/$n"
fi
# If file already in bin/, leave it as-is
if [[ -f "$outroot/bin/$n" ]]; then
:
fi
done
}
# Download geo/rule assets; then unify to bin/
download_geo_assets() {
local outroot="$1"
local bin_dir="$outroot/bin"
local srss_dir="$bin_dir/srss"
mkdir -p "$bin_dir" "$srss_dir"
echo "[+] Download Xray Geo to ${bin_dir}"
curl -fsSL -o "$bin_dir/geosite.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geosite.dat"
curl -fsSL -o "$bin_dir/geoip.dat" \
"https://github.com/Loyalsoldier/V2ray-rules-dat/releases/latest/download/geoip.dat"
curl -fsSL -o "$bin_dir/geoip-only-cn-private.dat" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/geoip-only-cn-private.dat"
curl -fsSL -o "$bin_dir/Country.mmdb" \
"https://raw.githubusercontent.com/Loyalsoldier/geoip/release/Country.mmdb"
echo "[+] Download sing-box rule DB & rule-sets"
curl -fsSL -o "$bin_dir/geoip.metadb" \
"https://github.com/MetaCubeX/meta-rules-dat/releases/latest/download/geoip.metadb" || true
for f in \
geoip-private.srs geoip-cn.srs geoip-facebook.srs geoip-fastly.srs \
geoip-google.srs geoip-netflix.srs geoip-telegram.srs geoip-twitter.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geoip/$f" || true
done
for f in \
geosite-cn.srs geosite-gfw.srs geosite-greatfire.srs \
geosite-geolocation-cn.srs geosite-category-ads-all.srs geosite-private.srs; do
curl -fsSL -o "$srss_dir/$f" \
"https://raw.githubusercontent.com/2dust/sing-box-rules/rule-set-geosite/$f" || true
done
# Unify to bin/
unify_geo_layout "$outroot"
}

View File

@@ -1,7 +1,7 @@
<Project> <Project>
<PropertyGroup> <PropertyGroup>
<Version>7.16.1</Version> <Version>7.16.3</Version>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View File

@@ -19,10 +19,10 @@
<PackageVersion Include="ReactiveUI" Version="22.2.1" /> <PackageVersion Include="ReactiveUI" Version="22.2.1" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" /> <PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" /> <PackageVersion Include="ReactiveUI.WPF" Version="22.2.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.3.7" /> <PackageVersion Include="Semi.Avalonia" Version="11.3.7.1" />
<PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" /> <PackageVersion Include="Semi.Avalonia.AvaloniaEdit" Version="11.2.0.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7" /> <PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.3.7.1" />
<PackageVersion Include="NLog" Version="6.0.5" /> <PackageVersion Include="NLog" Version="6.0.6" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" /> <PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.2" /> <PackageVersion Include="TaskScheduler" Version="2.12.2" />
<PackageVersion Include="WebDav.Client" Version="2.9.0" /> <PackageVersion Include="WebDav.Client" Version="2.9.0" />

View File

@@ -425,7 +425,7 @@ public class Utils
var domain = authority; var domain = authority;
// Handle IPv6 addresses, e.g., "[2001:db8::1]:443" // Handle IPv6 addresses, e.g., "[2001:db8::1]:443"
if (authority.StartsWith("[") && authority.Contains("]")) if (authority.StartsWith('[') && authority.Contains(']'))
{ {
var closingBracketIndex = authority.LastIndexOf(']'); var closingBracketIndex = authority.LastIndexOf(']');
if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':') if (closingBracketIndex < authority.Length - 1 && authority[closingBracketIndex + 1] == ':')

View File

@@ -2080,7 +2080,7 @@ public static class ConfigHandler
/// <returns>0 if successful</returns> /// <returns>0 if successful</returns>
public static async Task<int> InitBuiltinRouting(Config config, bool blImportAdvancedRules = false) public static async Task<int> InitBuiltinRouting(Config config, bool blImportAdvancedRules = false)
{ {
var ver = "V3-"; var ver = "V4-";
var items = await AppManager.Instance.RoutingItems(); var items = await AppManager.Instance.RoutingItems();
//TODO Temporary code to be removed later //TODO Temporary code to be removed later
@@ -2091,7 +2091,7 @@ public static class ConfigHandler
items = await AppManager.Instance.RoutingItems(); items = await AppManager.Instance.RoutingItems();
} }
if (!blImportAdvancedRules && items.Count > 0) if (!blImportAdvancedRules && items.Count(u => u.Remarks.StartsWith(ver)) > 0)
{ {
//migrate //migrate
//TODO Temporary code to be removed later //TODO Temporary code to be removed later

View File

@@ -4,7 +4,7 @@ namespace ServiceLib.Handler.Fmt;
public class BaseFmt public class BaseFmt
{ {
private static readonly string[] _allowInsecureArray = new[] { "insecure", "allowInsecure", "allow_insecure", "verify" }; private static readonly string[] _allowInsecureArray = new[] { "insecure", "allowInsecure", "allow_insecure" };
protected static string GetIpv6(string address) protected static string GetIpv6(string address)
{ {

View File

@@ -45,18 +45,18 @@ public class SocksFmt : BaseFmt
}; };
result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..]; result = result[Global.ProtocolShares[EConfigType.SOCKS].Length..];
//remark //remark
var indexRemark = result.IndexOf("#"); var indexRemark = result.IndexOf('#');
if (indexRemark > 0) if (indexRemark > 0)
{ {
try try
{ {
item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1, result.Length - indexRemark - 1)); item.Remarks = Utils.UrlDecode(result.Substring(indexRemark + 1));
} }
catch { } catch { }
result = result[..indexRemark]; result = result[..indexRemark];
} }
//part decode //part decode
var indexS = result.IndexOf("@"); var indexS = result.IndexOf('@');
if (indexS > 0) if (indexS > 0)
{ {
} }

View File

@@ -374,11 +374,15 @@ public class CertPemManager
{ {
var beginIndex = pemChain.IndexOf(beginMarker, index, StringComparison.Ordinal); var beginIndex = pemChain.IndexOf(beginMarker, index, StringComparison.Ordinal);
if (beginIndex == -1) if (beginIndex == -1)
{
break; break;
}
var endIndex = pemChain.IndexOf(endMarker, beginIndex, StringComparison.Ordinal); var endIndex = pemChain.IndexOf(endMarker, beginIndex, StringComparison.Ordinal);
if (endIndex == -1) if (endIndex == -1)
{
break; break;
}
// Extract certificate content // Extract certificate content
var base64Start = beginIndex + beginMarker.Length; var base64Start = beginIndex + beginMarker.Length;

View File

@@ -19,7 +19,7 @@ namespace ServiceLib.Resx {
// 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。 // 类通过类似于 ResGen 或 Visual Studio 的工具自动生成的。
// 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen // 若要添加或移除成员,请编辑 .ResX 文件,然后重新运行 ResGen
// (以 /str 作为命令选项),或重新生成 VS 项目。 // (以 /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.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public class ResUI { public class ResUI {
@@ -799,7 +799,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Import Share Links from clipboard (Ctrl+V) 的本地化字符串。 /// 查找类似 Import Share Links from clipboard 的本地化字符串。
/// </summary> /// </summary>
public static string menuAddServerViaClipboard { public static string menuAddServerViaClipboard {
get { get {
@@ -817,7 +817,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Scan QR code on the screen (Ctrl+S) 的本地化字符串。 /// 查找类似 Scan QR code on the screen 的本地化字符串。
/// </summary> /// </summary>
public static string menuAddServerViaScan { public static string menuAddServerViaScan {
get { get {
@@ -970,7 +970,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Edit Configuration (Ctrl+D) 的本地化字符串。 /// 查找类似 Edit Configuration 的本地化字符串。
/// </summary> /// </summary>
public static string menuEditServer { public static string menuEditServer {
get { get {
@@ -1015,7 +1015,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Export Share Link to Clipboard (Ctrl+C) 的本地化字符串。 /// 查找类似 Export Share Link to Clipboard 的本地化字符串。
/// </summary> /// </summary>
public static string menuExport2ShareUrl { public static string menuExport2ShareUrl {
get { get {
@@ -1249,7 +1249,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Move to bottom (B) 的本地化字符串。 /// 查找类似 Move to bottom 的本地化字符串。
/// </summary> /// </summary>
public static string menuMoveBottom { public static string menuMoveBottom {
get { get {
@@ -1258,7 +1258,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Down (D) 的本地化字符串。 /// 查找类似 Down 的本地化字符串。
/// </summary> /// </summary>
public static string menuMoveDown { public static string menuMoveDown {
get { get {
@@ -1285,7 +1285,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Move to top (T) 的本地化字符串。 /// 查找类似 Move to top 的本地化字符串。
/// </summary> /// </summary>
public static string menuMoveTop { public static string menuMoveTop {
get { get {
@@ -1294,7 +1294,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Up (U) 的本地化字符串。 /// 查找类似 Up 的本地化字符串。
/// </summary> /// </summary>
public static string menuMoveUp { public static string menuMoveUp {
get { get {
@@ -1312,7 +1312,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Copy (Ctrl+C) 的本地化字符串。 /// 查找类似 Copy 的本地化字符串。
/// </summary> /// </summary>
public static string menuMsgViewCopy { public static string menuMsgViewCopy {
get { get {
@@ -1330,7 +1330,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Select all (Ctrl+A) 的本地化字符串。 /// 查找类似 Select all 的本地化字符串。
/// </summary> /// </summary>
public static string menuMsgViewSelectAll { public static string menuMsgViewSelectAll {
get { get {
@@ -1402,7 +1402,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Select active node (Enter) 的本地化字符串。 /// 查找类似 Select active node 的本地化字符串。
/// </summary> /// </summary>
public static string menuProxiesSelectActivity { public static string menuProxiesSelectActivity {
get { get {
@@ -1411,7 +1411,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Test Configurations real delay (Ctrl+R) 的本地化字符串。 /// 查找类似 Test Configurations real delay 的本地化字符串。
/// </summary> /// </summary>
public static string menuRealPingServer { public static string menuRealPingServer {
get { get {
@@ -1528,7 +1528,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Remove selected Configurations (Delete) 的本地化字符串。 /// 查找类似 Remove selected Configurations 的本地化字符串。
/// </summary> /// </summary>
public static string menuRemoveServer { public static string menuRemoveServer {
get { get {
@@ -1564,7 +1564,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Remove selected (Delete) 的本地化字符串。 /// 查找类似 Remove selected 的本地化字符串。
/// </summary> /// </summary>
public static string menuRoutingAdvancedRemove { public static string menuRoutingAdvancedRemove {
get { get {
@@ -1573,7 +1573,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Set as active rule (Enter) 的本地化字符串。 /// 查找类似 Set as active rule 的本地化字符串。
/// </summary> /// </summary>
public static string menuRoutingAdvancedSetDefault { public static string menuRoutingAdvancedSetDefault {
get { get {
@@ -1645,7 +1645,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Remove Rule (Delete) 的本地化字符串。 /// 查找类似 Remove Rule 的本地化字符串。
/// </summary> /// </summary>
public static string menuRuleRemove { public static string menuRuleRemove {
get { get {
@@ -1654,7 +1654,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Select all (Ctrl+A) 的本地化字符串。 /// 查找类似 Select all 的本地化字符串。
/// </summary> /// </summary>
public static string menuSelectAll { public static string menuSelectAll {
get { get {
@@ -1681,7 +1681,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Set as active Configuration (Enter) 的本地化字符串。 /// 查找类似 Set as active Configuration 的本地化字符串。
/// </summary> /// </summary>
public static string menuSetDefaultServer { public static string menuSetDefaultServer {
get { get {
@@ -1699,7 +1699,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Share Configuration (Ctrl+F) 的本地化字符串。 /// 查找类似 Share Configuration 的本地化字符串。
/// </summary> /// </summary>
public static string menuShareServer { public static string menuShareServer {
get { get {
@@ -1726,7 +1726,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Test Configurations download speed (Ctrl+T) 的本地化字符串。 /// 查找类似 Test Configurations download speed 的本地化字符串。
/// </summary> /// </summary>
public static string menuSpeedServer { public static string menuSpeedServer {
get { get {
@@ -1870,7 +1870,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Test Configurations with tcping (Ctrl+O) 的本地化字符串。 /// 查找类似 Test Configurations with tcping 的本地化字符串。
/// </summary> /// </summary>
public static string menuTcpingServer { public static string menuTcpingServer {
get { get {
@@ -2355,6 +2355,15 @@ namespace ServiceLib.Resx {
} }
} }
/// <summary>
/// 查找类似 Press ESC to terminate the test 的本地化字符串。
/// </summary>
public static string SpeedtestingPressEscToExit {
get {
return ResourceManager.GetString("SpeedtestingPressEscToExit", resourceCulture);
}
}
/// <summary> /// <summary>
/// 查找类似 Skip test 的本地化字符串。 /// 查找类似 Skip test 的本地化字符串。
/// </summary> /// </summary>
@@ -2383,7 +2392,7 @@ namespace ServiceLib.Resx {
} }
/// <summary> /// <summary>
/// 查找类似 Waiting for testing (press ESC to terminate)... 的本地化字符串。 /// 查找类似 Waiting... 的本地化字符串。
/// </summary> /// </summary>
public static string SpeedtestingWait { public static string SpeedtestingWait {
get { get {

View File

@@ -472,10 +472,10 @@
<value>زبان</value> <value>زبان</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>وارد کردن URL انبوه از کلیپ بورد (Ctrl+V)</value> <value>وارد کردن URL انبوه از کلیپ بورد</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>اسکن کد QR روی صفحه (Ctrl+S)</value> <value>اسکن کد QR روی صفحه</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>سرور انتخاب شده را شبیه سازی کنید</value> <value>سرور انتخاب شده را شبیه سازی کنید</value>
@@ -484,31 +484,31 @@
<value>سرورهای تکراری را حذف کنید</value> <value>سرورهای تکراری را حذف کنید</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>حذف سرورهای انتخابی (Delete)</value> <value>حذف سرورهای انتخابی</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>به عنوان سرور فعال تنظیم کنید (Enter)</value> <value>به عنوان سرور فعال تنظیم کنید</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>تمام آمار خدمات را پاک کنید</value> <value>تمام آمار خدمات را پاک کنید</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>آزمایش سرورها با تاخیر واقعی (Ctrl+R)</value> <value>آزمایش سرورها با تاخیر واقعی</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>مرتب سازی بر اساس نتیجه تست</value> <value>مرتب سازی بر اساس نتیجه تست</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>تست سرعت دانلود سرورها (Ctrl+T)</value> <value>تست سرعت دانلود سرورها</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>تست سرورها با tcping (Ctrl+O)</value> <value>تست سرورها با tcping</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>سرور انتخابی را برای پیکربندی کلاینت صادر کنید</value> <value>سرور انتخابی را برای پیکربندی کلاینت صادر کنید</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>URL های اشتراک گذاری را به کلیپ بورد صادر کنید (Ctrl+C)</value> <value>URL های اشتراک گذاری را به کلیپ بورد صادر کنید</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>یک سرور پیکربندی سفارشی اضافه شود</value> <value>یک سرور پیکربندی سفارشی اضافه شود</value>
@@ -529,19 +529,19 @@
<value>سرور [VMess] را اضافه کنید</value> <value>سرور [VMess] را اضافه کنید</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>انتخاب همه (Ctrl+A)</value> <value>انتخاب همه</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>همه را پاک کن</value> <value>همه را پاک کن</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>کپی (Ctrl+C)</value> <value>کپی</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>کپی همه</value> <value>کپی همه</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>انتخاب همه (Ctrl+A)</value> <value>انتخاب همه</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>اضافه کردن</value> <value>اضافه کردن</value>
@@ -796,13 +796,13 @@
<value>به پایین حرکت شود(B)</value> <value>به پایین حرکت شود(B)</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>پایین (D)</value> <value>پایین</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>حرکت به بالا (T)</value> <value>حرکت به بالا</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>بالا (U)</value> <value>بالا</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>فیلتر، از عبارات منظم پشتیبانی می کند</value> <value>فیلتر، از عبارات منظم پشتیبانی می کند</value>
@@ -922,7 +922,7 @@
<value>رد شدن از آزمون</value> <value>رد شدن از آزمون</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>ویرایش سرور (Ctrl+D)</value> <value>ویرایش سرور</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>دوبار کلیک کردن سرور باعث فعال شدن آن می شود</value> <value>دوبار کلیک کردن سرور باعث فعال شدن آن می شود</value>
@@ -976,7 +976,10 @@
<value>فعال‌ سازی شتاب‌ دهنده سخت‌ افزاری (نیاز به راه‌اندازی مجدد)</value> <value>فعال‌ سازی شتاب‌ دهنده سخت‌ افزاری (نیاز به راه‌اندازی مجدد)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>در انتظار آزمایش (برای پایان دادن به ESC فشار دهید)...</value> <value>در انتظار آزمایش...</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>برای پایان دادن به ESC فشار دهید</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>لطفاً در صورت قطع غیرعادی آن را خاموش کنید</value> <value>لطفاً در صورت قطع غیرعادی آن را خاموش کنید</value>
@@ -1204,7 +1207,7 @@
<value>تازه سازی پروکسی ها</value> <value>تازه سازی پروکسی ها</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>انتخاب گره فعال (Enter)</value> <value>انتخاب گره فعال</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>استراتژی دامنه پیش فرض برای خروجی</value> <value>استراتژی دامنه پیش فرض برای خروجی</value>
@@ -1635,4 +1638,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSettingsMacOSShowInDock" xml:space="preserve"> <data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS displays this in the Dock (requires restart)</value> <value>macOS displays this in the Dock (requires restart)</value>
</data> </data>
</root> </root>

View File

@@ -472,10 +472,10 @@
<value>Langue (redémarrage requis)</value> <value>Langue (redémarrage requis)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>Importer liens depuis le presse-papiers (Ctrl+V)</value> <value>Importer liens depuis le presse-papiers</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>Scanner le QR code à lécran (Ctrl+S)</value> <value>Scanner le QR code à lécran</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>Cloner la sélection</value> <value>Cloner la sélection</value>
@@ -484,7 +484,7 @@
<value>Supprimer les doublons</value> <value>Supprimer les doublons</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>Supprimer la sélection (multi-sélection) (Delete)</value> <value>Supprimer la sélection (multi-sélection)</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>Définir comme actif (Entrée)</value> <value>Définir comme actif (Entrée)</value>
@@ -493,22 +493,22 @@
<value>Effacer toutes les statistiques de service</value> <value>Effacer toutes les statistiques de service</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>Tester la latence de connexion réelle (multi-sélect) (Ctrl+R)</value> <value>Tester la latence de connexion réelle (multi-sélect)</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>Trier selon les résultats de test</value> <value>Trier selon les résultats de test</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>Tester la vitesse (multi-sélection) (Ctrl+T)</value> <value>Tester la vitesse (multi-sélection)</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>Tester la latence Tcping (multi-sélection) (Ctrl+O)</value> <value>Tester la latence Tcping (multi-sélection)</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>Exporter la configuration complète sélectionnée</value> <value>Exporter la configuration complète sélectionnée</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>Exporter les liens de partage vers le presse-papiers (multi-sélection) (Ctrl+C)</value> <value>Exporter les liens de partage vers le presse-papiers (multi-sélection)</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>Ajouter une configuration personnalisée</value> <value>Ajouter une configuration personnalisée</value>
@@ -529,19 +529,19 @@
<value>Ajouter [VMess]</value> <value>Ajouter [VMess]</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>Tout sélectionner (Ctrl+A)</value> <value>Tout sélectionner</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>Tout effacer</value> <value>Tout effacer</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>Copier (Ctrl+C)</value> <value>Copier</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>Tout copier</value> <value>Tout copier</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>Tout sélect (Ctrl+A)</value> <value>Tout sélect</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>Ajouter</value> <value>Ajouter</value>
@@ -781,7 +781,7 @@
<value>Mode PAC</value> <value>Mode PAC</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>Partager (Ctrl+F)</value> <value>Partager</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>Routage</value> <value>Routage</value>
@@ -793,16 +793,16 @@
<value>Exécuter en tant quadministrateur</value> <value>Exécuter en tant quadministrateur</value>
</data> </data>
<data name="menuMoveBottom" xml:space="preserve"> <data name="menuMoveBottom" xml:space="preserve">
<value>Déplacer tout en bas (B)</value> <value>Déplacer tout en bas</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>Descendre (D)</value> <value>Descendre</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>Déplacer tout en haut (T)</value> <value>Déplacer tout en haut</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Monter (U)</value> <value>Monter</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Filtre (regex pris en charge)</value> <value>Filtre (regex pris en charge)</value>
@@ -817,7 +817,7 @@
<value>Importer 1-clic du jeu de règles</value> <value>Importer 1-clic du jeu de règles</value>
</data> </data>
<data name="menuRoutingAdvancedRemove" xml:space="preserve"> <data name="menuRoutingAdvancedRemove" xml:space="preserve">
<value>Suppr. règles sélectionnées (Delete)</value> <value>Suppr. règles sélectionnées</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Définir comme règles actives (Entrée)</value> <value>Définir comme règles actives (Entrée)</value>
@@ -853,7 +853,7 @@
<value>Liste des règles</value> <value>Liste des règles</value>
</data> </data>
<data name="menuRuleRemove" xml:space="preserve"> <data name="menuRuleRemove" xml:space="preserve">
<value>Supprimer les règles sélectionnées (Delete)</value> <value>Supprimer les règles sélectionnées</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>Paramètres détaillés des règles de routage</value> <value>Paramètres détaillés des règles de routage</value>
@@ -922,7 +922,7 @@
<value>Ignorer le test</value> <value>Ignorer le test</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>Éditer (Ctrl+D)</value> <value>Éditer</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Double-cliquer sur linterface principale pour activer</value> <value>Double-cliquer sur linterface principale pour activer</value>
@@ -976,7 +976,10 @@
<value>Activer laccélération matérielle (redémarrage requis)</value> <value>Activer laccélération matérielle (redémarrage requis)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>En attente du test (appuyer sur Échap pour arrêter)...</value> <value>En attente du test...</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>Appuyer sur Échap pour arrêter</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Désactiver cette option si coupure anormale</value> <value>Désactiver cette option si coupure anormale</value>
@@ -1603,10 +1606,10 @@
<value>Certificate Pinning</value> <value>Certificate Pinning</value>
</data> </data>
<data name="TbCertPinningTips" xml:space="preserve"> <data name="TbCertPinningTips" xml:space="preserve">
<value>Server Certificate (PEM format, optional) <value>Certificat serveur (format PEM, facultatif)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled. Si le certificat est défini, il est fixé et loption « Ignorer la vérification » est désactivée.
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value> Si un certificat auto-signé est utilisé ou si le système contient une CA non fiable ou malveillante, laction « Obtenir le certificat » peut échouer.</value>
</data> </data>
<data name="TbFetchCert" xml:space="preserve"> <data name="TbFetchCert" xml:space="preserve">
<value>Obtenir le certificat</value> <value>Obtenir le certificat</value>
@@ -1630,6 +1633,6 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<value>Chemin script proxy système personnalisé</value> <value>Chemin script proxy système personnalisé</value>
</data> </data>
<data name="TbSettingsMacOSShowInDock" xml:space="preserve"> <data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS displays this in the Dock (requires restart)</value> <value>Afficher dans le Dock de macOS (redém. requis)</value>
</data> </data>
</root> </root>

View File

@@ -472,10 +472,10 @@
<value>Nyelv (Újraindítás)</value> <value>Nyelv (Újraindítás)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>Megosztási linkek importálása vágólapról (Ctrl+V)</value> <value>Megosztási linkek importálása vágólapról</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>QR kód beolvasása a képernyőről (Ctrl+S)</value> <value>QR kód beolvasása a képernyőről</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>Kijelölt konfiguráció klónozása</value> <value>Kijelölt konfiguráció klónozása</value>
@@ -484,31 +484,31 @@
<value>Ismétlődő konfigurációk eltávolítása</value> <value>Ismétlődő konfigurációk eltávolítása</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>Kijelölt konfigurációk eltávolítása (Delete)</value> <value>Kijelölt konfigurációk eltávolítása</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>Beállítás aktív konfigurációként (Enter)</value> <value>Beállítás aktív konfigurációként</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>Összes szolgáltatás statisztika törlése</value> <value>Összes szolgáltatás statisztika törlése</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>Konfigurációk valós késleltetésének tesztelése (Ctrl+R)</value> <value>Konfigurációk valós késleltetésének tesztelése</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>Rendezés teszteredmény szerint</value> <value>Rendezés teszteredmény szerint</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>Konfigurációk letöltési sebességének tesztelése (Ctrl+T)</value> <value>Konfigurációk letöltési sebességének tesztelése</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>Konfigurációk tesztelése tcpinggel (Ctrl+O)</value> <value>Konfigurációk tesztelése tcpinggel</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>Kijelölt konfiguráció exportálása teljes konfigurációként</value> <value>Kijelölt konfiguráció exportálása teljes konfigurációként</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>Megosztási link exportálása vágólapra (Ctrl+C)</value> <value>Megosztási link exportálása vágólapra</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>Egyéni konfiguráció hozzáadása</value> <value>Egyéni konfiguráció hozzáadása</value>
@@ -529,19 +529,19 @@
<value>[VMess] konfiguráció hozzáadása</value> <value>[VMess] konfiguráció hozzáadása</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>Összes kijelölése (Ctrl+A)</value> <value>Összes kijelölése</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>Összes törlése</value> <value>Összes törlése</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>Másolás (Ctrl+C)</value> <value>Másolás</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>Összes másolása</value> <value>Összes másolása</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>Összes kijelölése (Ctrl+A)</value> <value>Összes kijelölése</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>Hozzáadás</value> <value>Hozzáadás</value>
@@ -781,7 +781,7 @@
<value>PAC mód</value> <value>PAC mód</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>Konfiguráció megosztása (Ctrl+F)</value> <value>Konfiguráció megosztása</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>Útválasztás</value> <value>Útválasztás</value>
@@ -793,16 +793,16 @@
<value>Futtatás rendszergazdaként</value> <value>Futtatás rendszergazdaként</value>
</data> </data>
<data name="menuMoveBottom" xml:space="preserve"> <data name="menuMoveBottom" xml:space="preserve">
<value>Mozgatás alulra (B)</value> <value>Mozgatás alulra</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>Le (D)</value> <value>Le</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>Mozgatás felülre (T)</value> <value>Mozgatás felülre</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Fel (U)</value> <value>Fel</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Szűrő, támogatja a reguláris kifejezéseket</value> <value>Szűrő, támogatja a reguláris kifejezéseket</value>
@@ -817,10 +817,10 @@
<value>Szabályok importálása</value> <value>Szabályok importálása</value>
</data> </data>
<data name="menuRoutingAdvancedRemove" xml:space="preserve"> <data name="menuRoutingAdvancedRemove" xml:space="preserve">
<value>Kijelölt eltávolítása (Delete)</value> <value>Kijelölt eltávolítása</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Beállítás aktív szabályként (Enter)</value> <value>Beállítás aktív szabályként</value>
</data> </data>
<data name="TbdomainStrategy" xml:space="preserve"> <data name="TbdomainStrategy" xml:space="preserve">
<value>Tartomány stratégia</value> <value>Tartomány stratégia</value>
@@ -853,7 +853,7 @@
<value>Szabálylista</value> <value>Szabálylista</value>
</data> </data>
<data name="menuRuleRemove" xml:space="preserve"> <data name="menuRuleRemove" xml:space="preserve">
<value>Szabály eltávolítása (Delete)</value> <value>Szabály eltávolítása</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>Útválasztási szabály részleteinek beállítása</value> <value>Útválasztási szabály részleteinek beállítása</value>
@@ -922,7 +922,7 @@
<value>Teszt kihagyása</value> <value>Teszt kihagyása</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>Konfiguráció szerkesztése (Ctrl+D)</value> <value>Konfiguráció szerkesztése</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Dupla kattintás a konfigurációra aktiválja</value> <value>Dupla kattintás a konfigurációra aktiválja</value>
@@ -976,7 +976,10 @@
<value>Hardveres gyorsítás engedélyezése (újraindítást igényel)</value> <value>Hardveres gyorsítás engedélyezése (újraindítást igényel)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>Tesztelésre vár (ESC megnyomásával megszakítható)...</value> <value>Tesztelésre vár...</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>ESC megnyomásával megszakítható</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Kérjük, kapcsolja ki rendellenes megszakadás esetén</value> <value>Kérjük, kapcsolja ki rendellenes megszakadás esetén</value>
@@ -1204,7 +1207,7 @@
<value>Proxyk frissítése</value> <value>Proxyk frissítése</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Aktív csomópont kiválasztása (Enter)</value> <value>Aktív csomópont kiválasztása</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Alapértelmezett tartomány stratégia kimenő forgalomhoz</value> <value>Alapértelmezett tartomány stratégia kimenő forgalomhoz</value>

View File

@@ -472,10 +472,10 @@
<value>Language (Restart)</value> <value>Language (Restart)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>Import Share Links from clipboard (Ctrl+V)</value> <value>Import Share Links from clipboard</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>Scan QR code on the screen (Ctrl+S)</value> <value>Scan QR code on the screen</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>Clone selected Configuration</value> <value>Clone selected Configuration</value>
@@ -484,31 +484,31 @@
<value>Remove duplicate Configurations</value> <value>Remove duplicate Configurations</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>Remove selected Configurations (Delete)</value> <value>Remove selected Configurations</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>Set as active Configuration (Enter)</value> <value>Set as active Configuration</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>Clear all service statistics</value> <value>Clear all service statistics</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>Test Configurations real delay (Ctrl+R)</value> <value>Test Configurations real delay</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>Sort by test result</value> <value>Sort by test result</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>Test Configurations download speed (Ctrl+T)</value> <value>Test Configurations download speed</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>Test Configurations with tcping (Ctrl+O)</value> <value>Test Configurations with tcping</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>Export selected Configuration for complete configuration</value> <value>Export selected Configuration for complete configuration</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>Export Share Link to Clipboard (Ctrl+C)</value> <value>Export Share Link to Clipboard</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>Add a custom configuration Configuration</value> <value>Add a custom configuration Configuration</value>
@@ -529,19 +529,19 @@
<value>Add [VMess] Configuration</value> <value>Add [VMess] Configuration</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>Select all (Ctrl+A)</value> <value>Select all</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>Clear all</value> <value>Clear all</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>Copy (Ctrl+C)</value> <value>Copy</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>Copy all</value> <value>Copy all</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>Select all (Ctrl+A)</value> <value>Select all</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>Add</value> <value>Add</value>
@@ -781,7 +781,7 @@
<value>PAC mode</value> <value>PAC mode</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>Share Configuration (Ctrl+F)</value> <value>Share Configuration</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>Routing</value> <value>Routing</value>
@@ -793,16 +793,16 @@
<value>Run as Admin</value> <value>Run as Admin</value>
</data> </data>
<data name="menuMoveBottom" xml:space="preserve"> <data name="menuMoveBottom" xml:space="preserve">
<value>Move to bottom (B)</value> <value>Move to bottom</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>Down (D)</value> <value>Down</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>Move to top (T)</value> <value>Move to top</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Up (U)</value> <value>Up</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Filter, supports regular expressions</value> <value>Filter, supports regular expressions</value>
@@ -817,10 +817,10 @@
<value>Import Rules</value> <value>Import Rules</value>
</data> </data>
<data name="menuRoutingAdvancedRemove" xml:space="preserve"> <data name="menuRoutingAdvancedRemove" xml:space="preserve">
<value>Remove selected (Delete)</value> <value>Remove selected</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>Set as active rule (Enter)</value> <value>Set as active rule</value>
</data> </data>
<data name="TbdomainStrategy" xml:space="preserve"> <data name="TbdomainStrategy" xml:space="preserve">
<value>Domain strategy</value> <value>Domain strategy</value>
@@ -853,7 +853,7 @@
<value>Rule List</value> <value>Rule List</value>
</data> </data>
<data name="menuRuleRemove" xml:space="preserve"> <data name="menuRuleRemove" xml:space="preserve">
<value>Remove Rule (Delete)</value> <value>Remove Rule</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>Routing Rule Details Setting</value> <value>Routing Rule Details Setting</value>
@@ -922,7 +922,7 @@
<value>Skip test</value> <value>Skip test</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>Edit Configuration (Ctrl+D)</value> <value>Edit Configuration</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Double-clicking Configuration makes it active</value> <value>Double-clicking Configuration makes it active</value>
@@ -976,7 +976,10 @@
<value>Enable hardware acceleration (requires restart)</value> <value>Enable hardware acceleration (requires restart)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>Waiting for testing (press ESC to terminate)...</value> <value>Waiting...</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>Press ESC to terminate the test</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Please turn off when there is an abnormal disconnection</value> <value>Please turn off when there is an abnormal disconnection</value>
@@ -1204,7 +1207,7 @@
<value>Refresh Proxies</value> <value>Refresh Proxies</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Select active node (Enter)</value> <value>Select active node</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Default domain strategy for outbound</value> <value>Default domain strategy for outbound</value>

View File

@@ -472,10 +472,10 @@
<value>Язык (требуется перезапуск)</value> <value>Язык (требуется перезапуск)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>Импорт массива URL из буфера обмена (Ctrl+V)</value> <value>Импорт массива URL из буфера обмена</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>Сканировать QR-код с экрана (Ctrl+S)</value> <value>Сканировать QR-код с экрана</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>Клонировать выбранный сервер</value> <value>Клонировать выбранный сервер</value>
@@ -484,31 +484,31 @@
<value>Удалить дубликаты серверов</value> <value>Удалить дубликаты серверов</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>Удалить выбранные серверы (Delete)</value> <value>Удалить выбранные серверы</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>Установить как активный сервер (Enter)</value> <value>Установить как активный сервер</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>Очистить всю статистику</value> <value>Очистить всю статистику</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>Тест на реальную задержку сервера (Ctrl+R)</value> <value>Тест на реальную задержку сервера</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>Сортировать по результату теста</value> <value>Сортировать по результату теста</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>Тест на скорость загрузки сервера (Ctrl+T)</value> <value>Тест на скорость загрузки сервера</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>Тест задержки с tcping (Ctrl+O)</value> <value>Тест задержки с tcping</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>Экспортировать выбранный сервер для клиента</value> <value>Экспортировать выбранный сервер для клиента</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>Экспорт URL-адресов общего доступа в буфер обмена (Ctrl+C)</value> <value>Экспорт URL-адресов общего доступа в буфер обмена</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>Добавить сервер пользовательской конфигурации</value> <value>Добавить сервер пользовательской конфигурации</value>
@@ -529,19 +529,19 @@
<value>Добавить сервер [VMess]</value> <value>Добавить сервер [VMess]</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>Выбрать все (Ctrl+A)</value> <value>Выбрать все</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>Очистить все</value> <value>Очистить все</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>Скопировать (Ctrl+C)</value> <value>Скопировать</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>Скопировать все</value> <value>Скопировать все</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>Выбрать все (Ctrl+A)</value> <value>Выбрать все</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>Добавить</value> <value>Добавить</value>
@@ -781,7 +781,7 @@
<value>Режим PAC</value> <value>Режим PAC</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>Поделиться сервером (Ctrl+F)</value> <value>Поделиться сервером</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>Маршрутизация</value> <value>Маршрутизация</value>
@@ -793,16 +793,16 @@
<value>Администратор</value> <value>Администратор</value>
</data> </data>
<data name="menuMoveBottom" xml:space="preserve"> <data name="menuMoveBottom" xml:space="preserve">
<value>Спуститься вниз (B)</value> <value>Спуститься вниз</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>Вниз (D)</value> <value>Вниз</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>Подняться наверх (T)</value> <value>Подняться наверх</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>Вверх (U)</value> <value>Вверх</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>Фильтр, поддерживает regex</value> <value>Фильтр, поддерживает regex</value>
@@ -853,7 +853,7 @@
<value>Список правил</value> <value>Список правил</value>
</data> </data>
<data name="menuRuleRemove" xml:space="preserve"> <data name="menuRuleRemove" xml:space="preserve">
<value>Удалить правила (Delete)</value> <value>Удалить правила</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>Детальные настройки правил маршрутизации</value> <value>Детальные настройки правил маршрутизации</value>
@@ -922,7 +922,7 @@
<value>Пропустить тест</value> <value>Пропустить тест</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>Редактировать сервер (Ctrl+D)</value> <value>Редактировать сервер</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>Двойной клик чтобы сделать сервер активным</value> <value>Двойной клик чтобы сделать сервер активным</value>
@@ -976,7 +976,10 @@
<value>Включить аппаратное ускорение (требуется перезагрузка)</value> <value>Включить аппаратное ускорение (требуется перезагрузка)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>Ожидание тестирования (нажмите ESC для отмены)…</value> <value>Ожидание тестирования…</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>нажмите ESC для отмены</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>Отключите при аномальном разрыве соединения</value> <value>Отключите при аномальном разрыве соединения</value>
@@ -1204,7 +1207,7 @@
<value>Обновить прокси</value> <value>Обновить прокси</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>Сделать узел активным (Enter)</value> <value>Сделать узел активным</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Стратегия домена по умолчанию для исходящих</value> <value>Стратегия домена по умолчанию для исходящих</value>

View File

@@ -472,10 +472,10 @@
<value>语言 (需重启)</value> <value>语言 (需重启)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>从剪贴板导入分享链接 (Ctrl+V)</value> <value>从剪贴板导入分享链接</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>扫描屏幕上的二维码 (Ctrl+S)</value> <value>扫描屏幕上的二维码</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>克隆所选</value> <value>克隆所选</value>
@@ -484,31 +484,31 @@
<value>移除重复</value> <value>移除重复</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>移除所选 (多选) (Delete)</value> <value>移除所选 (多选)</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>设为活动 (Enter)</value> <value>设为活动</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服务统计数据</value> <value>清除所有服务统计数据</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>测试真连接延迟 (多选) (Ctrl+R)</value> <value>测试真连接延迟 (多选)</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>按测试结果排序</value> <value>按测试结果排序</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>测试速度 (多选) (Ctrl+T)</value> <value>测试速度 (多选)</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>测试延迟 Tcping (多选) (Ctrl+O)</value> <value>测试延迟 Tcping (多选)</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>导出所选完整配置</value> <value>导出所选完整配置</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>导出分享链接至剪贴板 (多选) (Ctrl+C)</value> <value>导出分享链接至剪贴板 (多选)</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>添加自定义配置</value> <value>添加自定义配置</value>
@@ -529,19 +529,19 @@
<value>添加 [VMess] </value> <value>添加 [VMess] </value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>全选 (Ctrl+A)</value> <value>全选</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>清除所有</value> <value>清除所有</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>复制 (Ctrl+C)</value> <value>复制</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>复制所有</value> <value>复制所有</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>全选 (Ctrl+A)</value> <value>全选</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>添加</value> <value>添加</value>
@@ -781,7 +781,7 @@
<value>Pac 模式</value> <value>Pac 模式</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>分享 (Ctrl+F)</value> <value>分享</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>路由</value> <value>路由</value>
@@ -793,16 +793,16 @@
<value>以管理员身份运行</value> <value>以管理员身份运行</value>
</data> </data>
<data name="menuMoveBottom" xml:space="preserve"> <data name="menuMoveBottom" xml:space="preserve">
<value>下移至底 (B)</value> <value>下移至底</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>下移 (D)</value> <value>下移</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>上移至顶 (T)</value> <value>上移至顶</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>上移 (U)</value> <value>上移</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>过滤器 (支持正则)</value> <value>过滤器 (支持正则)</value>
@@ -817,10 +817,10 @@
<value>一键导入规则集</value> <value>一键导入规则集</value>
</data> </data>
<data name="menuRoutingAdvancedRemove" xml:space="preserve"> <data name="menuRoutingAdvancedRemove" xml:space="preserve">
<value>移除所选规则 (Delete)</value> <value>移除所选规则</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>设为活动规则 (Enter)</value> <value>设为活动规则</value>
</data> </data>
<data name="TbdomainStrategy" xml:space="preserve"> <data name="TbdomainStrategy" xml:space="preserve">
<value>域名解析策略</value> <value>域名解析策略</value>
@@ -853,7 +853,7 @@
<value>规则列表</value> <value>规则列表</value>
</data> </data>
<data name="menuRuleRemove" xml:space="preserve"> <data name="menuRuleRemove" xml:space="preserve">
<value>移除所选规则 (Delete)</value> <value>移除所选规则</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>路由规则详情设置</value> <value>路由规则详情设置</value>
@@ -922,7 +922,7 @@
<value>跳过测试</value> <value>跳过测试</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>编辑 (Ctrl+D)</value> <value>编辑</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主界面双击设为活动</value> <value>主界面双击设为活动</value>
@@ -976,7 +976,10 @@
<value>启用硬件加速 (需重启)</value> <value>启用硬件加速 (需重启)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>等待测试中 (按 ESC 终止)...</value> <value>等待测试...</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>按 ESC 可终止测试</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>当有异常断流时请关闭</value> <value>当有异常断流时请关闭</value>
@@ -1201,7 +1204,7 @@
<value>刷新</value> <value>刷新</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>设为活动 (Enter)</value> <value>设为活动</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Outbound 默认解析策略</value> <value>Outbound 默认解析策略</value>

View File

@@ -472,10 +472,10 @@
<value>語言 (需重啟)</value> <value>語言 (需重啟)</value>
</data> </data>
<data name="menuAddServerViaClipboard" xml:space="preserve"> <data name="menuAddServerViaClipboard" xml:space="preserve">
<value>從剪貼簿導入分享連結 (Ctrl+V)</value> <value>從剪貼簿導入分享連結</value>
</data> </data>
<data name="menuAddServerViaScan" xml:space="preserve"> <data name="menuAddServerViaScan" xml:space="preserve">
<value>掃描螢幕上的二維碼 (Ctrl+S)</value> <value>掃描螢幕上的二維碼</value>
</data> </data>
<data name="menuCopyServer" xml:space="preserve"> <data name="menuCopyServer" xml:space="preserve">
<value>複製所選</value> <value>複製所選</value>
@@ -484,31 +484,31 @@
<value>移除重複</value> <value>移除重複</value>
</data> </data>
<data name="menuRemoveServer" xml:space="preserve"> <data name="menuRemoveServer" xml:space="preserve">
<value>移除所選 (多選) (Delete)</value> <value>移除所選 (多選)</value>
</data> </data>
<data name="menuSetDefaultServer" xml:space="preserve"> <data name="menuSetDefaultServer" xml:space="preserve">
<value>設為活動 (Enter)</value> <value>設為活動</value>
</data> </data>
<data name="menuClearServerStatistics" xml:space="preserve"> <data name="menuClearServerStatistics" xml:space="preserve">
<value>清除所有服務統計資料</value> <value>清除所有服務統計資料</value>
</data> </data>
<data name="menuRealPingServer" xml:space="preserve"> <data name="menuRealPingServer" xml:space="preserve">
<value>測試真連線延遲 (多選) (Ctrl+R)</value> <value>測試真連線延遲 (多選)</value>
</data> </data>
<data name="menuSortServerResult" xml:space="preserve"> <data name="menuSortServerResult" xml:space="preserve">
<value>按測試結果排序</value> <value>按測試結果排序</value>
</data> </data>
<data name="menuSpeedServer" xml:space="preserve"> <data name="menuSpeedServer" xml:space="preserve">
<value>測試速度 (多選) (Ctrl+T)</value> <value>測試速度 (多選)</value>
</data> </data>
<data name="menuTcpingServer" xml:space="preserve"> <data name="menuTcpingServer" xml:space="preserve">
<value>測試延遲 Tcping (多選) (Ctrl+O)</value> <value>測試延遲 Tcping (多選)</value>
</data> </data>
<data name="menuExport2ClientConfig" xml:space="preserve"> <data name="menuExport2ClientConfig" xml:space="preserve">
<value>匯出所選完整設定</value> <value>匯出所選完整設定</value>
</data> </data>
<data name="menuExport2ShareUrl" xml:space="preserve"> <data name="menuExport2ShareUrl" xml:space="preserve">
<value>匯出分享連結至剪貼簿 (多選) (Ctrl+C)</value> <value>匯出分享連結至剪貼簿 (多選)</value>
</data> </data>
<data name="menuAddCustomServer" xml:space="preserve"> <data name="menuAddCustomServer" xml:space="preserve">
<value>新增自訂節點</value> <value>新增自訂節點</value>
@@ -529,19 +529,19 @@
<value>新增 [VMess] 節點</value> <value>新增 [VMess] 節點</value>
</data> </data>
<data name="menuSelectAll" xml:space="preserve"> <data name="menuSelectAll" xml:space="preserve">
<value>全選 (Ctrl+A)</value> <value>全選</value>
</data> </data>
<data name="menuMsgViewClear" xml:space="preserve"> <data name="menuMsgViewClear" xml:space="preserve">
<value>清除所有</value> <value>清除所有</value>
</data> </data>
<data name="menuMsgViewCopy" xml:space="preserve"> <data name="menuMsgViewCopy" xml:space="preserve">
<value>複製 (Ctrl+C)</value> <value>複製</value>
</data> </data>
<data name="menuMsgViewCopyAll" xml:space="preserve"> <data name="menuMsgViewCopyAll" xml:space="preserve">
<value>複製所有</value> <value>複製所有</value>
</data> </data>
<data name="menuMsgViewSelectAll" xml:space="preserve"> <data name="menuMsgViewSelectAll" xml:space="preserve">
<value>全選 (Ctrl+A)</value> <value>全選</value>
</data> </data>
<data name="menuSubAdd" xml:space="preserve"> <data name="menuSubAdd" xml:space="preserve">
<value>新增</value> <value>新增</value>
@@ -781,7 +781,7 @@
<value>PAC 模式</value> <value>PAC 模式</value>
</data> </data>
<data name="menuShareServer" xml:space="preserve"> <data name="menuShareServer" xml:space="preserve">
<value>分享 (Ctrl+F)</value> <value>分享</value>
</data> </data>
<data name="menuRouting" xml:space="preserve"> <data name="menuRouting" xml:space="preserve">
<value>路由</value> <value>路由</value>
@@ -793,16 +793,16 @@
<value>以管理員身份執行</value> <value>以管理員身份執行</value>
</data> </data>
<data name="menuMoveBottom" xml:space="preserve"> <data name="menuMoveBottom" xml:space="preserve">
<value>下移至底部 (B)</value> <value>下移至底部</value>
</data> </data>
<data name="menuMoveDown" xml:space="preserve"> <data name="menuMoveDown" xml:space="preserve">
<value>下移 (D)</value> <value>下移</value>
</data> </data>
<data name="menuMoveTop" xml:space="preserve"> <data name="menuMoveTop" xml:space="preserve">
<value>上移至頂部 (T)</value> <value>上移至頂部</value>
</data> </data>
<data name="menuMoveUp" xml:space="preserve"> <data name="menuMoveUp" xml:space="preserve">
<value>上移 (U)</value> <value>上移</value>
</data> </data>
<data name="MsgFilterTitle" xml:space="preserve"> <data name="MsgFilterTitle" xml:space="preserve">
<value>過濾 (允許正則)</value> <value>過濾 (允許正則)</value>
@@ -817,10 +817,10 @@
<value>一鍵匯入規則集</value> <value>一鍵匯入規則集</value>
</data> </data>
<data name="menuRoutingAdvancedRemove" xml:space="preserve"> <data name="menuRoutingAdvancedRemove" xml:space="preserve">
<value>移除所選規則 (Delete)</value> <value>移除所選規則</value>
</data> </data>
<data name="menuRoutingAdvancedSetDefault" xml:space="preserve"> <data name="menuRoutingAdvancedSetDefault" xml:space="preserve">
<value>設為活動規則 (Enter)</value> <value>設為活動規則</value>
</data> </data>
<data name="TbdomainStrategy" xml:space="preserve"> <data name="TbdomainStrategy" xml:space="preserve">
<value>域名解析策略</value> <value>域名解析策略</value>
@@ -853,7 +853,7 @@
<value>規則列表</value> <value>規則列表</value>
</data> </data>
<data name="menuRuleRemove" xml:space="preserve"> <data name="menuRuleRemove" xml:space="preserve">
<value>移除所選規則 (Delete)</value> <value>移除所選規則</value>
</data> </data>
<data name="menuRoutingRuleDetailsSetting" xml:space="preserve"> <data name="menuRoutingRuleDetailsSetting" xml:space="preserve">
<value>路由規則詳情設定</value> <value>路由規則詳情設定</value>
@@ -922,7 +922,7 @@
<value>跳過測試</value> <value>跳過測試</value>
</data> </data>
<data name="menuEditServer" xml:space="preserve"> <data name="menuEditServer" xml:space="preserve">
<value>編輯 (Ctrl+D)</value> <value>編輯</value>
</data> </data>
<data name="TbSettingsDoubleClick2Activate" xml:space="preserve"> <data name="TbSettingsDoubleClick2Activate" xml:space="preserve">
<value>主介面輕按兩下設為活動</value> <value>主介面輕按兩下設為活動</value>
@@ -976,7 +976,10 @@
<value>啟用硬體加速 (需重啟)</value> <value>啟用硬體加速 (需重啟)</value>
</data> </data>
<data name="SpeedtestingWait" xml:space="preserve"> <data name="SpeedtestingWait" xml:space="preserve">
<value>等待測試中(按 ESC 終止)...</value> <value>等待測試中...</value>
</data>
<data name="SpeedtestingPressEscToExit" xml:space="preserve">
<value>按 ECS 以終止測試</value>
</data> </data>
<data name="TipDisplayLog" xml:space="preserve"> <data name="TipDisplayLog" xml:space="preserve">
<value>當有異常斷流時請關閉</value> <value>當有異常斷流時請關閉</value>
@@ -1201,7 +1204,7 @@
<value>重新整理</value> <value>重新整理</value>
</data> </data>
<data name="menuProxiesSelectActivity" xml:space="preserve"> <data name="menuProxiesSelectActivity" xml:space="preserve">
<value>設為活動節點 (Enter)</value> <value>設為活動節點</value>
</data> </data>
<data name="TbSettingsDomainStrategy4Out" xml:space="preserve"> <data name="TbSettingsDomainStrategy4Out" xml:space="preserve">
<value>Outbound 預設解析策略</value> <value>Outbound 預設解析策略</value>
@@ -1600,28 +1603,28 @@
<value>自動從訂閱分組新增過濾後的配置</value> <value>自動從訂閱分組新增過濾後的配置</value>
</data> </data>
<data name="TbCertPinning" xml:space="preserve"> <data name="TbCertPinning" xml:space="preserve">
<value>Certificate Pinning</value> <value>憑證綁定</value>
</data> </data>
<data name="TbCertPinningTips" xml:space="preserve"> <data name="TbCertPinningTips" xml:space="preserve">
<value>Server Certificate (PEM format, optional) <value>伺服器憑證PEM 格式,可選)
When specified, the certificate will be pinned, and "Allow Insecure" will be disabled. 若已指定,憑證將會被綁定,並且「跳過憑證驗證」將被停用。
The "Get Certificate" action may fail if a self-signed certificate is used or if the system contains an untrusted or malicious CA.</value> 若使用自簽憑證,或系統中存在不受信任或惡意的 CA「取得憑證」動作可能會失敗。</value>
</data> </data>
<data name="TbFetchCert" xml:space="preserve"> <data name="TbFetchCert" xml:space="preserve">
<value>Fetch Certificate</value> <value>獲取憑證</value>
</data> </data>
<data name="TbFetchCertChain" xml:space="preserve"> <data name="TbFetchCertChain" xml:space="preserve">
<value>Fetch Certificate Chain</value> <value>獲取憑證鏈</value>
</data> </data>
<data name="ServerNameMustBeValidDomain" xml:space="preserve"> <data name="ServerNameMustBeValidDomain" xml:space="preserve">
<value>Please set a valid domain</value> <value>請設定有效的網域名稱</value>
</data> </data>
<data name="CertNotSet" xml:space="preserve"> <data name="CertNotSet" xml:space="preserve">
<value>Certificate not set</value> <value>尚未設定憑證</value>
</data> </data>
<data name="CertSet" xml:space="preserve"> <data name="CertSet" xml:space="preserve">
<value>Certificate set</value> <value>已設定憑證</value>
</data> </data>
<data name="TbSettingsCustomSystemProxyPacPath" xml:space="preserve"> <data name="TbSettingsCustomSystemProxyPacPath" xml:space="preserve">
<value>自訂 PAC 檔案路徑</value> <value>自訂 PAC 檔案路徑</value>
@@ -1632,4 +1635,4 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="TbSettingsMacOSShowInDock" xml:space="preserve"> <data name="TbSettingsMacOSShowInDock" xml:space="preserve">
<value>macOS 在 Dock 欄顯示 (需重啟)</value> <value>macOS 在 Dock 欄顯示 (需重啟)</value>
</data> </data>
</root> </root>

View File

@@ -13,20 +13,19 @@
"api.ip.sb" "api.ip.sb"
] ]
}, },
{
"remarks": "Google cn",
"outboundTag": "proxy",
"domain": [
"domain:googleapis.cn",
"domain:gstatic.com"
]
},
{ {
"remarks": "阻断udp443", "remarks": "阻断udp443",
"outboundTag": "block", "outboundTag": "block",
"port": "443", "port": "443",
"network": "udp" "network": "udp"
}, },
{
"remarks": "代理Google",
"outboundTag": "proxy",
"domain": [
"geosite:google"
]
},
{ {
"remarks": "绕过局域网IP", "remarks": "绕过局域网IP",
"outboundTag": "direct", "outboundTag": "direct",

View File

@@ -1,18 +1,17 @@
[ [
{
"remarks": "Google cn",
"outboundTag": "proxy",
"domain": [
"domain:googleapis.cn",
"domain:gstatic.com"
]
},
{ {
"remarks": "阻断udp443", "remarks": "阻断udp443",
"outboundTag": "block", "outboundTag": "block",
"port": "443", "port": "443",
"network": "udp" "network": "udp"
}, },
{
"remarks": "代理Google",
"outboundTag": "proxy",
"domain": [
"geosite:google"
]
},
{ {
"remarks": "绕过局域网IP", "remarks": "绕过局域网IP",
"outboundTag": "direct", "outboundTag": "direct",

View File

@@ -14,9 +14,8 @@
], ],
"rules": [ "rules": [
{ {
"domain_suffix": [ "rule_set": [
"googleapis.cn", "geosite-google"
"gstatic.com"
], ],
"server": "remote", "server": "remote",
"strategy": "prefer_ipv4" "strategy": "prefer_ipv4"

View File

@@ -8,8 +8,7 @@
"address": "1.1.1.1", "address": "1.1.1.1",
"skipFallback": true, "skipFallback": true,
"domains": [ "domains": [
"domain:googleapis.cn", "geosite:google"
"domain:gstatic.com"
] ]
}, },
{ {

View File

@@ -14,9 +14,8 @@
], ],
"rules": [ "rules": [
{ {
"domain_suffix": [ "rule_set": [
"googleapis.cn", "geosite-google"
"gstatic.com"
], ],
"server": "remote", "server": "remote",
"strategy": "prefer_ipv4" "strategy": "prefer_ipv4"

View File

@@ -300,7 +300,7 @@ public partial class CoreConfigSingboxService
private bool ParseV2Domain(string domain, Rule4Sbox rule) private bool ParseV2Domain(string domain, Rule4Sbox rule)
{ {
if (domain.StartsWith("#") || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:")) if (domain.StartsWith('#') || domain.StartsWith("ext:") || domain.StartsWith("ext-domain:"))
{ {
return false; return false;
} }
@@ -316,10 +316,8 @@ public partial class CoreConfigSingboxService
} }
else if (domain.StartsWith("domain:")) else if (domain.StartsWith("domain:"))
{ {
rule.domain ??= [];
rule.domain_suffix ??= []; rule.domain_suffix ??= [];
rule.domain?.Add(domain.Substring(7)); rule.domain_suffix?.Add(domain.Substring(7));
rule.domain_suffix?.Add("." + domain.Substring(7));
} }
else if (domain.StartsWith("full:")) else if (domain.StartsWith("full:"))
{ {

View File

@@ -19,7 +19,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
public void ExitLoop() public void ExitLoop()
{ {
if (_lstExitLoop.Count > 0) if (!_lstExitLoop.IsEmpty)
{ {
_ = UpdateFunc("", ResUI.SpeedtestingStop); _ = UpdateFunc("", ResUI.SpeedtestingStop);
@@ -27,6 +27,11 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
} }
} }
private static bool ShouldStopTest(string exitLoopKey)
{
return !_lstExitLoop.Any(p => p == exitLoopKey);
}
private async Task RunAsync(ESpeedActionType actionType, List<ProfileItem> selecteds) private async Task RunAsync(ESpeedActionType actionType, List<ProfileItem> selecteds)
{ {
var exitLoopKey = Utils.GetGuid(false); var exitLoopKey = Utils.GetGuid(false);
@@ -103,6 +108,11 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
} }
} }
if (lstSelected.Count > 1 && (actionType == ESpeedActionType.Speedtest || actionType == ESpeedActionType.Mixedtest))
{
NoticeManager.Instance.Enqueue(ResUI.SpeedtestingPressEscToExit);
}
return lstSelected; return lstSelected;
} }
@@ -152,7 +162,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
var pageSizeNext = pageSize / 2; var pageSizeNext = pageSize / 2;
if (lstFailed.Count > 0 && pageSizeNext > 0) if (lstFailed.Count > 0 && pageSizeNext > 0)
{ {
if (_lstExitLoop.Any(p => p == exitLoopKey) == false) if (ShouldStopTest(exitLoopKey))
{ {
await UpdateFunc("", ResUI.SpeedtestingSkip); await UpdateFunc("", ResUI.SpeedtestingSkip);
return; return;
@@ -190,6 +200,12 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
{ {
continue; continue;
} }
if (ShouldStopTest(exitLoopKey))
{
return false;
}
tasks.Add(Task.Run(async () => tasks.Add(Task.Run(async () =>
{ {
await DoRealPing(it); await DoRealPing(it);
@@ -218,7 +234,7 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
List<Task> tasks = new(); List<Task> tasks = new();
foreach (var it in selecteds) foreach (var it in selecteds)
{ {
if (_lstExitLoop.Any(p => p == exitLoopKey) == false) if (ShouldStopTest(exitLoopKey))
{ {
await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
continue; continue;
@@ -234,21 +250,27 @@ public class SpeedtestService(Config config, Func<SpeedTestResult, Task> updateF
if (processService is null) if (processService is null)
{ {
await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore); await UpdateFunc(it.IndexId, "", ResUI.FailedToRunCore);
return;
} }
else
await Task.Delay(1000);
var delay = await DoRealPing(it);
if (blSpeedTest)
{ {
await Task.Delay(1000); if (ShouldStopTest(exitLoopKey))
var delay = await DoRealPing(it);
if (blSpeedTest)
{ {
if (delay > 0) await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
{ return;
await DoSpeedTest(downloadHandle, it); }
}
else if (delay > 0)
{ {
await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip); await DoSpeedTest(downloadHandle, it);
} }
else
{
await UpdateFunc(it.IndexId, "", ResUI.SpeedtestingSkip);
} }
} }
} }

View File

@@ -17,17 +17,17 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{ {
if (args.Success) if (args.Success)
{ {
UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully); _ = UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
UpdateFunc(true, Utils.UrlEncode(fileName)); _ = UpdateFunc(true, Utils.UrlEncode(fileName));
} }
else else
{ {
UpdateFunc(false, args.Msg); _ = UpdateFunc(false, args.Msg);
} }
}; };
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
{ {
UpdateFunc(false, args.GetException().Message); _ = UpdateFunc(false, args.GetException().Message);
}; };
await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN)); await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, ECoreType.v2rayN));
@@ -57,26 +57,26 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{ {
if (args.Success) if (args.Success)
{ {
UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully); _ = UpdateFunc(false, ResUI.MsgDownloadV2rayCoreSuccessfully);
UpdateFunc(false, ResUI.MsgUnpacking); _ = UpdateFunc(false, ResUI.MsgUnpacking);
try try
{ {
UpdateFunc(true, fileName); _ = UpdateFunc(true, fileName);
} }
catch (Exception ex) catch (Exception ex)
{ {
UpdateFunc(false, ex.Message); _ = UpdateFunc(false, ex.Message);
} }
} }
else else
{ {
UpdateFunc(false, args.Msg); _ = UpdateFunc(false, args.Msg);
} }
}; };
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
{ {
UpdateFunc(false, args.GetException().Message); _ = UpdateFunc(false, args.GetException().Message);
}; };
await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, type)); await UpdateFunc(false, string.Format(ResUI.MsgStartUpdating, type));
@@ -396,6 +396,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
} }
//append dns items TODO //append dns items TODO
geoSiteFiles.Add("google");
geoSiteFiles.Add("cn"); geoSiteFiles.Add("cn");
geoSiteFiles.Add("geolocation-cn"); geoSiteFiles.Add("geolocation-cn");
geoSiteFiles.Add("category-ads-all"); geoSiteFiles.Add("category-ads-all");
@@ -438,7 +439,7 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
{ {
if (args.Success) if (args.Success)
{ {
UpdateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, fileName)); _ = UpdateFunc(false, string.Format(ResUI.MsgDownloadGeoFileSuccessfully, fileName));
try try
{ {
@@ -452,17 +453,17 @@ public class UpdateService(Config config, Func<bool, string, Task> updateFunc)
} }
catch (Exception ex) catch (Exception ex)
{ {
UpdateFunc(false, ex.Message); _ = UpdateFunc(false, ex.Message);
} }
} }
else else
{ {
UpdateFunc(false, args.Msg); _ = UpdateFunc(false, args.Msg);
} }
}; };
downloadHandle.Error += (sender2, args) => downloadHandle.Error += (sender2, args) =>
{ {
UpdateFunc(false, args.GetException().Message); _ = UpdateFunc(false, args.GetException().Message);
}; };
await downloadHandle.DownloadFileAsync(url, tmpFileName, true, _timeout); await downloadHandle.DownloadFileAsync(url, tmpFileName, true, _timeout);

View File

@@ -1,14 +1,31 @@
using System;
using System.Collections.Generic;
using System.IO;
using Avalonia;
using Avalonia.Media;
namespace v2rayN.Desktop.Common; namespace v2rayN.Desktop.Common;
public static class AppBuilderExtension public static class AppBuilderExtension
{ {
public static AppBuilder WithFontByDefault(this AppBuilder appBuilder) public static AppBuilder WithFontByDefault(this AppBuilder appBuilder)
{ {
var uri = Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC"); var fallbacks = new List<FontFallback>();
return appBuilder.With(new FontManagerOptions()
var notoSansSc = new FontFamily(Path.Combine(Global.AvaAssets, "Fonts#Noto Sans SC"));
fallbacks.Add(new FontFallback { FontFamily = notoSansSc });
if (OperatingSystem.IsLinux())
{ {
//DefaultFamilyName = uri, fallbacks.Add(new FontFallback
FontFallbacks = new[] { new FontFallback { FontFamily = new FontFamily(uri) } } {
FontFamily = new FontFamily("Noto Color Emoji")
});
}
return appBuilder.With(new FontManagerOptions
{
FontFallbacks = fallbacks.ToArray()
}); });
} }
} }

View File

@@ -152,13 +152,31 @@
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem x:Name="menuAddChildServer" Header="{x:Static resx:ResUI.menuAddChildServer}" /> <MenuItem x:Name="menuAddChildServer" Header="{x:Static resx:ResUI.menuAddChildServer}" />
<MenuItem x:Name="menuRemoveChildServer" Header="{x:Static resx:ResUI.menuRemoveChildServer}" /> <MenuItem
<MenuItem x:Name="menuSelectAllChild" Header="{x:Static resx:ResUI.menuSelectAll}" /> x:Name="menuRemoveChildServer"
Header="{x:Static resx:ResUI.menuRemoveChildServer}"
InputGesture="Back" />
<MenuItem
x:Name="menuSelectAllChild"
Header="{x:Static resx:ResUI.menuSelectAll}"
InputGesture="Ctrl+A" />
<Separator /> <Separator />
<MenuItem x:Name="menuMoveTop" Header="{x:Static resx:ResUI.menuMoveTop}" /> <MenuItem
<MenuItem x:Name="menuMoveUp" Header="{x:Static resx:ResUI.menuMoveUp}" /> x:Name="menuMoveTop"
<MenuItem x:Name="menuMoveDown" Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuMoveTop}"
<MenuItem x:Name="menuMoveBottom" Header="{x:Static resx:ResUI.menuMoveBottom}" /> InputGesture="T" />
<MenuItem
x:Name="menuMoveUp"
Header="{x:Static resx:ResUI.menuMoveUp}"
InputGesture="U" />
<MenuItem
x:Name="menuMoveDown"
Header="{x:Static resx:ResUI.menuMoveDown}"
InputGesture="D" />
<MenuItem
x:Name="menuMoveBottom"
Header="{x:Static resx:ResUI.menuMoveBottom}"
InputGesture="B" />
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.Columns> <DataGrid.Columns>

View File

@@ -132,6 +132,7 @@ public partial class AddGroupServerWindow : WindowBase<AddGroupServerViewModel>
break; break;
case Key.Delete: case Key.Delete:
case Key.Back:
ViewModel?.ChildRemoveAsync(); ViewModel?.ChildRemoveAsync();
e.Handled = true; e.Handled = true;
break; break;

View File

@@ -110,7 +110,10 @@
<ItemsControl.ContextMenu> <ItemsControl.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" /> <MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" />
<MenuItem x:Name="menuProxiesSelectActivity" Header="{x:Static resx:ResUI.menuProxiesSelectActivity}" /> <MenuItem
x:Name="menuProxiesSelectActivity"
Header="{x:Static resx:ResUI.menuProxiesSelectActivity}"
InputGesture="Enter" />
</ContextMenu> </ContextMenu>
</ItemsControl.ContextMenu> </ItemsControl.ContextMenu>
<ItemsControl.ItemsPanel> <ItemsControl.ItemsPanel>

View File

@@ -24,15 +24,16 @@
<DockPanel> <DockPanel>
<DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Top"> <DockPanel Margin="{StaticResource Margin8}" DockPanel.Dock="Top">
<ContentControl x:Name="conTheme" DockPanel.Dock="Right" /> <ContentControl x:Name="conTheme" DockPanel.Dock="Right" />
<Menu Margin="0,1"> <Menu Margin="{StaticResource Margin4}">
<MenuItem Padding="{StaticResource MarginLr8}"> <MenuItem Header="{x:Static resx:ResUI.menuServers}">
<MenuItem.Header> <MenuItem
<StackPanel Orientation="Horizontal"> x:Name="menuAddServerViaClipboard"
<TextBlock Text="{x:Static resx:ResUI.menuServers}" /> Header="{x:Static resx:ResUI.menuAddServerViaClipboard}"
</StackPanel> InputGesture="Ctrl+V" />
</MenuItem.Header> <MenuItem
<MenuItem x:Name="menuAddServerViaClipboard" Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" /> x:Name="menuAddServerViaScan"
<MenuItem x:Name="menuAddServerViaScan" Header="{x:Static resx:ResUI.menuAddServerViaScan}" /> Header="{x:Static resx:ResUI.menuAddServerViaScan}"
InputGesture="Ctrl+S" />
<MenuItem x:Name="menuAddServerViaImage" Header="{x:Static resx:ResUI.menuAddServerViaImage}" /> <MenuItem x:Name="menuAddServerViaImage" Header="{x:Static resx:ResUI.menuAddServerViaImage}" />
<MenuItem x:Name="menuAddCustomServer" Header="{x:Static resx:ResUI.menuAddCustomServer}" /> <MenuItem x:Name="menuAddCustomServer" Header="{x:Static resx:ResUI.menuAddCustomServer}" />
<MenuItem x:Name="menuAddPolicyGroupServer" Header="{x:Static resx:ResUI.menuAddPolicyGroupServer}" /> <MenuItem x:Name="menuAddPolicyGroupServer" Header="{x:Static resx:ResUI.menuAddPolicyGroupServer}" />
@@ -51,12 +52,7 @@
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" /> <MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
</MenuItem> </MenuItem>
<MenuItem Padding="{StaticResource MarginLr8}"> <MenuItem Header="{x:Static resx:ResUI.menuSubscription}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static resx:ResUI.menuSubscription}" />
</StackPanel>
</MenuItem.Header>
<MenuItem x:Name="menuSubSetting" Header="{x:Static resx:ResUI.menuSubSetting}" /> <MenuItem x:Name="menuSubSetting" Header="{x:Static resx:ResUI.menuSubSetting}" />
<Separator /> <Separator />
<MenuItem x:Name="menuSubUpdate" Header="{x:Static resx:ResUI.menuSubUpdate}" /> <MenuItem x:Name="menuSubUpdate" Header="{x:Static resx:ResUI.menuSubUpdate}" />
@@ -65,20 +61,24 @@
<MenuItem x:Name="menuSubGroupUpdateViaProxy" Header="{x:Static resx:ResUI.menuSubGroupUpdateViaProxy}" /> <MenuItem x:Name="menuSubGroupUpdateViaProxy" Header="{x:Static resx:ResUI.menuSubGroupUpdateViaProxy}" />
</MenuItem> </MenuItem>
<MenuItem Padding="{StaticResource MarginLr8}"> <MenuItem Header="{x:Static resx:ResUI.menuSetting}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{x:Static resx:ResUI.menuSetting}" />
</StackPanel>
</MenuItem.Header>
<MenuItem x:Name="menuOptionSetting" Header="{x:Static resx:ResUI.menuOptionSetting}" /> <MenuItem x:Name="menuOptionSetting" Header="{x:Static resx:ResUI.menuOptionSetting}" />
<MenuItem x:Name="menuRoutingSetting" Header="{x:Static resx:ResUI.menuRoutingSetting}" /> <MenuItem x:Name="menuRoutingSetting" Header="{x:Static resx:ResUI.menuRoutingSetting}" />
<MenuItem x:Name="menuDNSSetting" Header="{x:Static resx:ResUI.menuDNSSetting}" /> <MenuItem x:Name="menuDNSSetting" Header="{x:Static resx:ResUI.menuDNSSetting}" />
<MenuItem x:Name="menuFullConfigTemplate" Header="{x:Static resx:ResUI.menuFullConfigTemplate}" /> <MenuItem x:Name="menuFullConfigTemplate" Header="{x:Static resx:ResUI.menuFullConfigTemplate}" />
<MenuItem x:Name="menuGlobalHotkeySetting" Header="{x:Static resx:ResUI.menuGlobalHotkeySetting}" IsVisible="{Binding BlIsWindows}" /> <MenuItem
x:Name="menuGlobalHotkeySetting"
Header="{x:Static resx:ResUI.menuGlobalHotkeySetting}"
IsVisible="{Binding BlIsWindows}" />
<Separator /> <Separator />
<MenuItem x:Name="menuRebootAsAdmin" Header="{x:Static resx:ResUI.menuRebootAsAdmin}" IsVisible="{Binding BlIsWindows}" /> <MenuItem
<MenuItem x:Name="menuSettingsSetUWP" Header="{x:Static resx:ResUI.TbSettingsSetUWP}" IsVisible="{Binding BlIsWindows}" /> x:Name="menuRebootAsAdmin"
Header="{x:Static resx:ResUI.menuRebootAsAdmin}"
IsVisible="{Binding BlIsWindows}" />
<MenuItem
x:Name="menuSettingsSetUWP"
Header="{x:Static resx:ResUI.TbSettingsSetUWP}"
IsVisible="{Binding BlIsWindows}" />
<MenuItem x:Name="menuClearServerStatistics" Header="{x:Static resx:ResUI.menuClearServerStatistics}" /> <MenuItem x:Name="menuClearServerStatistics" Header="{x:Static resx:ResUI.menuClearServerStatistics}" />
<Separator /> <Separator />
<MenuItem Header="{x:Static resx:ResUI.menuRegionalPresets}"> <MenuItem Header="{x:Static resx:ResUI.menuRegionalPresets}">
@@ -90,15 +90,16 @@
<MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" /> <MenuItem x:Name="menuOpenTheFileLocation" Header="{x:Static resx:ResUI.menuOpenTheFileLocation}" />
</MenuItem> </MenuItem>
<MenuItem x:Name="menuReload" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuReload}" /> <MenuItem x:Name="menuReload" Header="{x:Static resx:ResUI.menuReload}" />
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" /> <MenuItem x:Name="menuHelp" Header="{x:Static resx:ResUI.menuHelp}">
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
<Separator />
</MenuItem>
<MenuItem x:Name="menuHelp" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuHelp}" /> <MenuItem x:Name="menuPromotion" Header="{x:Static resx:ResUI.menuPromotion}" />
<MenuItem x:Name="menuPromotion" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuPromotion}" /> <MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuExit}" />
<MenuItem x:Name="menuClose" Padding="{StaticResource MarginLr8}" Header="{x:Static resx:ResUI.menuExit}" />
</Menu> </Menu>
</DockPanel> </DockPanel>

View File

@@ -72,18 +72,20 @@
VerticalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto"
WordWrap="True"> WordWrap="True">
<avaloniaEdit:TextEditor.Options> <avaloniaEdit:TextEditor.Options>
<avaloniaEdit:TextEditorOptions AllowScrollBelowDocument="False"/> <avaloniaEdit:TextEditorOptions AllowScrollBelowDocument="False" />
</avaloniaEdit:TextEditor.Options> </avaloniaEdit:TextEditor.Options>
<avaloniaEdit:TextEditor.ContextFlyout> <avaloniaEdit:TextEditor.ContextFlyout>
<MenuFlyout> <MenuFlyout>
<MenuItem <MenuItem
x:Name="menuMsgViewSelectAll" x:Name="menuMsgViewSelectAll"
Click="menuMsgViewSelectAll_Click" Click="menuMsgViewSelectAll_Click"
InputGesture="Ctrl+A"
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" /> Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" />
<MenuItem <MenuItem
x:Name="menuMsgViewCopy" x:Name="menuMsgViewCopy"
Click="menuMsgViewCopy_Click" Click="menuMsgViewCopy_Click"
Header="{x:Static resx:ResUI.menuMsgViewCopy}" /> Header="{x:Static resx:ResUI.menuMsgViewCopy}"
InputGesture="Ctrl+C" />
<MenuItem <MenuItem
x:Name="menuMsgViewCopyAll" x:Name="menuMsgViewCopyAll"
Click="menuMsgViewCopyAll_Click" Click="menuMsgViewCopyAll_Click"

View File

@@ -103,16 +103,34 @@
</DataGrid.KeyBindings> </DataGrid.KeyBindings>
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem x:Name="menuSetDefaultServer" Header="{x:Static resx:ResUI.menuSetDefaultServer}" /> <MenuItem
<MenuItem x:Name="menuEditServer" Header="{x:Static resx:ResUI.menuEditServer}" /> x:Name="menuSetDefaultServer"
Header="{x:Static resx:ResUI.menuSetDefaultServer}"
InputGesture="Enter" />
<MenuItem
x:Name="menuEditServer"
Header="{x:Static resx:ResUI.menuEditServer}"
InputGesture="Ctrl+D" />
<MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" /> <MenuItem x:Name="menuCopyServer" Header="{x:Static resx:ResUI.menuCopyServer}" />
<MenuItem x:Name="menuRemoveServer" Header="{x:Static resx:ResUI.menuRemoveServer}" /> <MenuItem
x:Name="menuRemoveServer"
Header="{x:Static resx:ResUI.menuRemoveServer}"
InputGesture="Back" />
<MenuItem x:Name="menuRemoveDuplicateServer" Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" /> <MenuItem x:Name="menuRemoveDuplicateServer" Header="{x:Static resx:ResUI.menuRemoveDuplicateServer}" />
<MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" /> <MenuItem x:Name="menuRemoveInvalidServerResult" Header="{x:Static resx:ResUI.menuRemoveInvalidServerResult}" />
<Separator /> <Separator />
<MenuItem x:Name="menuTcpingServer" Header="{x:Static resx:ResUI.menuTcpingServer}" /> <MenuItem
<MenuItem x:Name="menuRealPingServer" Header="{x:Static resx:ResUI.menuRealPingServer}" /> x:Name="menuTcpingServer"
<MenuItem x:Name="menuSpeedServer" Header="{x:Static resx:ResUI.menuSpeedServer}" /> Header="{x:Static resx:ResUI.menuTcpingServer}"
InputGesture="Ctrl+O" />
<MenuItem
x:Name="menuRealPingServer"
Header="{x:Static resx:ResUI.menuRealPingServer}"
InputGesture="Ctrl+R" />
<MenuItem
x:Name="menuSpeedServer"
Header="{x:Static resx:ResUI.menuSpeedServer}"
InputGesture="Ctrl+T" />
<MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" /> <MenuItem x:Name="menuSortServerResult" Header="{x:Static resx:ResUI.menuSortServerResult}" />
<Separator /> <Separator />
<MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}"> <MenuItem x:Name="menuMoveToGroup" Header="{x:Static resx:ResUI.menuMoveToGroup}">
@@ -130,19 +148,40 @@
</MenuItem> </MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuMoveTo}"> <MenuItem Header="{x:Static resx:ResUI.menuMoveTo}">
<MenuItem x:Name="menuMoveTop" Header="{x:Static resx:ResUI.menuMoveTop}" /> <MenuItem
<MenuItem x:Name="menuMoveUp" Header="{x:Static resx:ResUI.menuMoveUp}" /> x:Name="menuMoveTop"
<MenuItem x:Name="menuMoveDown" Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuMoveTop}"
<MenuItem x:Name="menuMoveBottom" Header="{x:Static resx:ResUI.menuMoveBottom}" /> InputGesture="T" />
<MenuItem
x:Name="menuMoveUp"
Header="{x:Static resx:ResUI.menuMoveUp}"
InputGesture="U" />
<MenuItem
x:Name="menuMoveDown"
Header="{x:Static resx:ResUI.menuMoveDown}"
InputGesture="D" />
<MenuItem
x:Name="menuMoveBottom"
Header="{x:Static resx:ResUI.menuMoveBottom}"
InputGesture="B" />
</MenuItem> </MenuItem>
<MenuItem x:Name="menuSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" /> <MenuItem
x:Name="menuSelectAll"
Header="{x:Static resx:ResUI.menuSelectAll}"
InputGesture="Ctrl+A" />
<Separator /> <Separator />
<MenuItem x:Name="menuShareServer" Header="{x:Static resx:ResUI.menuShareServer}" /> <MenuItem
x:Name="menuShareServer"
Header="{x:Static resx:ResUI.menuShareServer}"
InputGesture="Ctrl+F" />
<MenuItem Header="{x:Static resx:ResUI.menuExportConfig}"> <MenuItem Header="{x:Static resx:ResUI.menuExportConfig}">
<MenuItem x:Name="menuExport2ClientConfig" Header="{x:Static resx:ResUI.menuExport2ClientConfig}" /> <MenuItem x:Name="menuExport2ClientConfig" Header="{x:Static resx:ResUI.menuExport2ClientConfig}" />
<MenuItem x:Name="menuExport2ClientConfigClipboard" Header="{x:Static resx:ResUI.menuExport2ClientConfigClipboard}" /> <MenuItem x:Name="menuExport2ClientConfigClipboard" Header="{x:Static resx:ResUI.menuExport2ClientConfigClipboard}" />
<Separator /> <Separator />
<MenuItem x:Name="menuExport2ShareUrl" Header="{x:Static resx:ResUI.menuExport2ShareUrl}" /> <MenuItem
x:Name="menuExport2ShareUrl"
Header="{x:Static resx:ResUI.menuExport2ShareUrl}"
InputGesture="Ctrl+C" />
<MenuItem x:Name="menuExport2ShareUrlBase64" Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" /> <MenuItem x:Name="menuExport2ShareUrlBase64" Header="{x:Static resx:ResUI.menuExport2ShareUrlBase64}" />
</MenuItem> </MenuItem>
<Separator /> <Separator />

View File

@@ -313,33 +313,37 @@ public partial class ProfilesView : ReactiveUserControl<ProfilesViewModel>
} }
else else
{ {
if (e.Key is Key.Enter or Key.Return) switch (e.Key)
{ {
ViewModel?.SetDefaultServer(); case Key.Enter:
} //case Key.Return:
else if (e.Key == Key.Delete) ViewModel?.SetDefaultServer();
{ break;
ViewModel?.RemoveServerAsync();
} case Key.Delete:
else if (e.Key == Key.T) case Key.Back:
{ ViewModel?.RemoveServerAsync();
ViewModel?.MoveServer(EMove.Top); break;
}
else if (e.Key == Key.U) case Key.T:
{ ViewModel?.MoveServer(EMove.Top);
ViewModel?.MoveServer(EMove.Up); break;
}
else if (e.Key == Key.D) case Key.U:
{ ViewModel?.MoveServer(EMove.Up);
ViewModel?.MoveServer(EMove.Down); break;
}
else if (e.Key == Key.B) case Key.D:
{ ViewModel?.MoveServer(EMove.Down);
ViewModel?.MoveServer(EMove.Bottom); break;
}
else if (e.Key == Key.Escape) case Key.B:
{ ViewModel?.MoveServer(EMove.Bottom);
ViewModel?.ServerSpeedtestStop(); break;
case Key.Escape:
ViewModel?.ServerSpeedtestStop();
break;
} }
} }
} }

View File

@@ -14,17 +14,12 @@
WindowStartupLocation="CenterScreen" WindowStartupLocation="CenterScreen"
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel> <DockPanel>
<StackPanel <Menu Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
Margin="{StaticResource Margin4}" <MenuItem x:Name="menuRuleAdd" Header="{x:Static resx:ResUI.menuRuleAdd}" />
DockPanel.Dock="Top" <MenuItem x:Name="menuImportRulesFromFile" Header="{x:Static resx:ResUI.menuImportRulesFromFile}" />
Orientation="Horizontal"> <MenuItem x:Name="menuImportRulesFromClipboard" Header="{x:Static resx:ResUI.menuImportRulesFromClipboard}" />
<Menu> <MenuItem x:Name="menuImportRulesFromUrl" Header="{x:Static resx:ResUI.menuImportRulesFromUrl}" />
<MenuItem x:Name="menuRuleAdd" Header="{x:Static resx:ResUI.menuRuleAdd}" /> </Menu>
<MenuItem x:Name="menuImportRulesFromFile" Header="{x:Static resx:ResUI.menuImportRulesFromFile}" />
<MenuItem x:Name="menuImportRulesFromClipboard" Header="{x:Static resx:ResUI.menuImportRulesFromClipboard}" />
<MenuItem x:Name="menuImportRulesFromUrl" Header="{x:Static resx:ResUI.menuImportRulesFromUrl}" />
</Menu>
</StackPanel>
<StackPanel <StackPanel
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
@@ -189,14 +184,32 @@
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem x:Name="menuRuleAdd2" Header="{x:Static resx:ResUI.menuRuleAdd}" /> <MenuItem x:Name="menuRuleAdd2" Header="{x:Static resx:ResUI.menuRuleAdd}" />
<MenuItem x:Name="menuRuleRemove" Header="{x:Static resx:ResUI.menuRuleRemove}" /> <MenuItem
<MenuItem x:Name="menuRuleSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" /> x:Name="menuRuleRemove"
Header="{x:Static resx:ResUI.menuRuleRemove}"
InputGesture="Back" />
<MenuItem
x:Name="menuRuleSelectAll"
Header="{x:Static resx:ResUI.menuSelectAll}"
InputGesture="Ctrl+A" />
<MenuItem x:Name="menuRuleExportSelected" Header="{x:Static resx:ResUI.menuRuleExportSelected}" /> <MenuItem x:Name="menuRuleExportSelected" Header="{x:Static resx:ResUI.menuRuleExportSelected}" />
<Separator /> <Separator />
<MenuItem x:Name="menuMoveTop" Header="{x:Static resx:ResUI.menuMoveTop}" /> <MenuItem
<MenuItem x:Name="menuMoveUp" Header="{x:Static resx:ResUI.menuMoveUp}" /> x:Name="menuMoveTop"
<MenuItem x:Name="menuMoveDown" Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuMoveTop}"
<MenuItem x:Name="menuMoveBottom" Header="{x:Static resx:ResUI.menuMoveBottom}" /> InputGesture="T" />
<MenuItem
x:Name="menuMoveUp"
Header="{x:Static resx:ResUI.menuMoveUp}"
InputGesture="U" />
<MenuItem
x:Name="menuMoveDown"
Header="{x:Static resx:ResUI.menuMoveDown}"
InputGesture="D" />
<MenuItem
x:Name="menuMoveBottom"
Header="{x:Static resx:ResUI.menuMoveBottom}"
InputGesture="B" />
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.Columns> <DataGrid.Columns>

View File

@@ -140,25 +140,28 @@ public partial class RoutingRuleSettingWindow : WindowBase<RoutingRuleSettingVie
} }
else else
{ {
if (e.Key == Key.T) switch (e.Key)
{ {
ViewModel?.MoveRule(EMove.Top); case Key.T:
} ViewModel?.MoveRule(EMove.Top);
else if (e.Key == Key.U) break;
{
ViewModel?.MoveRule(EMove.Up); case Key.U:
} ViewModel?.MoveRule(EMove.Up);
else if (e.Key == Key.D) break;
{
ViewModel?.MoveRule(EMove.Down); case Key.D:
} ViewModel?.MoveRule(EMove.Down);
else if (e.Key == Key.B) break;
{
ViewModel?.MoveRule(EMove.Bottom); case Key.B:
} ViewModel?.MoveRule(EMove.Bottom);
else if (e.Key == Key.Delete) break;
{
ViewModel?.RuleRemoveAsync(); case Key.Delete:
case Key.Back:
ViewModel?.RuleRemoveAsync();
break;
} }
} }
} }

View File

@@ -15,16 +15,10 @@
mc:Ignorable="d"> mc:Ignorable="d">
<DockPanel> <DockPanel>
<StackPanel <Menu Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
Margin="{StaticResource Margin4}" <MenuItem x:Name="menuRoutingAdvancedAdd2" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
DockPanel.Dock="Top" <MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
Orientation="Horizontal" </Menu>
Spacing="4">
<Menu>
<MenuItem x:Name="menuRoutingAdvancedAdd2" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
<MenuItem x:Name="menuRoutingAdvancedImportRules2" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
</Menu>
</StackPanel>
<StackPanel <StackPanel
Margin="{StaticResource Margin4}" Margin="{StaticResource Margin4}"
@@ -105,9 +99,18 @@
<DataGrid.ContextMenu> <DataGrid.ContextMenu>
<ContextMenu> <ContextMenu>
<MenuItem x:Name="menuRoutingAdvancedAdd" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" /> <MenuItem x:Name="menuRoutingAdvancedAdd" Header="{x:Static resx:ResUI.menuRoutingAdvancedAdd}" />
<MenuItem x:Name="menuRoutingAdvancedRemove" Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" /> <MenuItem
<MenuItem x:Name="menuRoutingAdvancedSelectAll" Header="{x:Static resx:ResUI.menuSelectAll}" /> x:Name="menuRoutingAdvancedRemove"
<MenuItem x:Name="menuRoutingAdvancedSetDefault" Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" /> Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}"
InputGesture="Back" />
<MenuItem
x:Name="menuRoutingAdvancedSelectAll"
Header="{x:Static resx:ResUI.menuSelectAll}"
InputGesture="Ctrl+A" />
<MenuItem
x:Name="menuRoutingAdvancedSetDefault"
Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}"
InputGesture="Enter" />
<Separator /> <Separator />
<MenuItem x:Name="menuRoutingAdvancedImportRules" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" /> <MenuItem x:Name="menuRoutingAdvancedImportRules" Header="{x:Static resx:ResUI.menuRoutingAdvancedImportRules}" />
</ContextMenu> </ContextMenu>

View File

@@ -73,18 +73,27 @@ public partial class RoutingSettingWindow : WindowBase<RoutingSettingViewModel>
{ {
if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta) if (e.KeyModifiers is KeyModifiers.Control or KeyModifiers.Meta)
{ {
if (e.Key == Key.A) switch (e.Key)
{ {
lstRoutings.SelectAll(); case Key.A:
lstRoutings.SelectAll();
break;
} }
} }
else if (e.Key is Key.Enter or Key.Return) else
{ {
ViewModel?.RoutingAdvancedSetDefault(); switch (e.Key)
} {
else if (e.Key == Key.Delete) case Key.Enter:
{ //case Key.Return:
ViewModel?.RoutingAdvancedRemoveAsync(); ViewModel?.RoutingAdvancedSetDefault();
break;
case Key.Delete:
case Key.Back:
ViewModel?.RoutingAdvancedRemoveAsync();
break;
}
} }
} }

View File

@@ -20,18 +20,13 @@
DisableOpeningAnimation="True" DisableOpeningAnimation="True"
Identifier="dialogHostSub"> Identifier="dialogHostSub">
<DockPanel Margin="{StaticResource Margin8}"> <DockPanel Margin="{StaticResource Margin8}">
<StackPanel <Menu Margin="{StaticResource Margin4}" DockPanel.Dock="Top">
Margin="{StaticResource Margin4}" <MenuItem x:Name="menuSubAdd" Header="{x:Static resx:ResUI.menuSubAdd}" />
DockPanel.Dock="Top" <MenuItem x:Name="menuSubDelete" Header="{x:Static resx:ResUI.menuSubDelete}" />
Orientation="Horizontal"> <MenuItem x:Name="menuSubEdit" Header="{x:Static resx:ResUI.menuSubEdit}" />
<Menu> <MenuItem x:Name="menuSubShare" Header="{x:Static resx:ResUI.menuSubShare}" />
<MenuItem x:Name="menuSubAdd" Header="{x:Static resx:ResUI.menuSubAdd}" /> <MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuClose}" />
<MenuItem x:Name="menuSubDelete" Header="{x:Static resx:ResUI.menuSubDelete}" /> </Menu>
<MenuItem x:Name="menuSubEdit" Header="{x:Static resx:ResUI.menuSubEdit}" />
<MenuItem x:Name="menuSubShare" Header="{x:Static resx:ResUI.menuSubShare}" />
<MenuItem x:Name="menuClose" Header="{x:Static resx:ResUI.menuClose}" />
</Menu>
</StackPanel>
<DataGrid <DataGrid
x:Name="lstSubscription" x:Name="lstSubscription"
@@ -74,4 +69,4 @@
</DataGrid> </DataGrid>
</DockPanel> </DockPanel>
</dialogHost:DialogHost> </dialogHost:DialogHost>
</Window> </Window>

View File

@@ -218,24 +218,29 @@
<MenuItem <MenuItem
x:Name="menuSelectAllChild" x:Name="menuSelectAllChild"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSelectAll}" /> Header="{x:Static resx:ResUI.menuSelectAll}"
InputGestureText="Ctrl+A" />
<Separator /> <Separator />
<MenuItem <MenuItem
x:Name="menuMoveTop" x:Name="menuMoveTop"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveTop}" /> Header="{x:Static resx:ResUI.menuMoveTop}"
InputGestureText="T" />
<MenuItem <MenuItem
x:Name="menuMoveUp" x:Name="menuMoveUp"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveUp}" /> Header="{x:Static resx:ResUI.menuMoveUp}"
InputGestureText="U" />
<MenuItem <MenuItem
x:Name="menuMoveDown" x:Name="menuMoveDown"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuMoveDown}"
InputGestureText="D" />
<MenuItem <MenuItem
x:Name="menuMoveBottom" x:Name="menuMoveBottom"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveBottom}" /> Header="{x:Static resx:ResUI.menuMoveBottom}"
InputGestureText="B" />
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.Columns> <DataGrid.Columns>

View File

@@ -91,25 +91,28 @@ public partial class AddGroupServerWindow
} }
else else
{ {
if (e.Key == Key.T) switch (e.Key)
{ {
ViewModel?.MoveServer(EMove.Top); case Key.T:
} ViewModel?.MoveServer(EMove.Top);
else if (e.Key == Key.U) break;
{
ViewModel?.MoveServer(EMove.Up); case Key.U:
} ViewModel?.MoveServer(EMove.Up);
else if (e.Key == Key.D) break;
{
ViewModel?.MoveServer(EMove.Down); case Key.D:
} ViewModel?.MoveServer(EMove.Down);
else if (e.Key == Key.B) break;
{
ViewModel?.MoveServer(EMove.Bottom); case Key.B:
} ViewModel?.MoveServer(EMove.Bottom);
else if (e.Key == Key.Delete) break;
{
ViewModel?.ChildRemoveAsync(); case Key.Delete:
case Key.Back:
ViewModel?.ChildRemoveAsync();
break;
} }
} }
} }

View File

@@ -142,7 +142,10 @@
<ListView.ContextMenu> <ListView.ContextMenu>
<ContextMenu Style="{StaticResource DefContextMenu}"> <ContextMenu Style="{StaticResource DefContextMenu}">
<MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" /> <MenuItem x:Name="menuProxiesDelaytestPart" Header="{x:Static resx:ResUI.menuProxiesDelaytestPart}" />
<MenuItem x:Name="menuProxiesSelectActivity" Header="{x:Static resx:ResUI.menuProxiesSelectActivity}" /> <MenuItem
x:Name="menuProxiesSelectActivity"
Header="{x:Static resx:ResUI.menuProxiesSelectActivity}"
InputGestureText="Enter" />
</ContextMenu> </ContextMenu>
</ListView.ContextMenu> </ListView.ContextMenu>
<ListView.ItemsPanel> <ListView.ItemsPanel>

View File

@@ -58,11 +58,13 @@
<MenuItem <MenuItem
x:Name="menuAddServerViaClipboard" x:Name="menuAddServerViaClipboard"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddServerViaClipboard}" /> Header="{x:Static resx:ResUI.menuAddServerViaClipboard}"
InputGestureText="Ctrl+V" />
<MenuItem <MenuItem
x:Name="menuAddServerViaScan" x:Name="menuAddServerViaScan"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddServerViaScan}" /> Header="{x:Static resx:ResUI.menuAddServerViaScan}"
InputGestureText="Ctrl+S" />
<MenuItem <MenuItem
x:Name="menuAddServerViaImage" x:Name="menuAddServerViaImage"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -246,23 +248,6 @@
</MenuItem> </MenuItem>
</Menu> </Menu>
<Separator /> <Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem
Name="menuCheckUpdate"
Padding="{StaticResource MarginLeftRight8}"
AutomationProperties.Name="{x:Static resx:ResUI.menuCheckUpdate}">
<MenuItem.Header>
<StackPanel Orientation="Horizontal">
<materialDesign:PackIcon
Margin="{StaticResource MarginRight8}"
VerticalAlignment="Center"
Kind="Update" />
<TextBlock Text="{x:Static resx:ResUI.menuCheckUpdate}" />
</StackPanel>
</MenuItem.Header>
</MenuItem>
</Menu>
<Separator />
<Menu Margin="0,1" Style="{StaticResource ToolbarMenu}"> <Menu Margin="0,1" Style="{StaticResource ToolbarMenu}">
<MenuItem <MenuItem
x:Name="menuHelp" x:Name="menuHelp"
@@ -277,6 +262,8 @@
<TextBlock Text="{x:Static resx:ResUI.menuHelp}" /> <TextBlock Text="{x:Static resx:ResUI.menuHelp}" />
</StackPanel> </StackPanel>
</MenuItem.Header> </MenuItem.Header>
<MenuItem x:Name="menuCheckUpdate" Header="{x:Static resx:ResUI.menuCheckUpdate}" />
<Separator Margin="-40,5" />
</MenuItem> </MenuItem>
</Menu> </Menu>
<Separator /> <Separator />

View File

@@ -91,11 +91,13 @@
<MenuItem <MenuItem
x:Name="menuMsgViewSelectAll" x:Name="menuMsgViewSelectAll"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMsgViewSelectAll}" /> Header="{x:Static resx:ResUI.menuMsgViewSelectAll}"
InputGestureText="Ctrl+A" />
<MenuItem <MenuItem
x:Name="menuMsgViewCopy" x:Name="menuMsgViewCopy"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMsgViewCopy}" /> Header="{x:Static resx:ResUI.menuMsgViewCopy}"
InputGestureText="Ctrl+C" />
<MenuItem <MenuItem
x:Name="menuMsgViewCopyAll" x:Name="menuMsgViewCopyAll"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"

View File

@@ -123,11 +123,13 @@
<MenuItem <MenuItem
x:Name="menuSetDefaultServer" x:Name="menuSetDefaultServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSetDefaultServer}" /> Header="{x:Static resx:ResUI.menuSetDefaultServer}"
InputGestureText="Enter" />
<MenuItem <MenuItem
x:Name="menuEditServer" x:Name="menuEditServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuEditServer}" /> Header="{x:Static resx:ResUI.menuEditServer}"
InputGestureText="Ctrl+D" />
<MenuItem <MenuItem
x:Name="menuCopyServer" x:Name="menuCopyServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -135,7 +137,8 @@
<MenuItem <MenuItem
x:Name="menuRemoveServer" x:Name="menuRemoveServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRemoveServer}" /> Header="{x:Static resx:ResUI.menuRemoveServer}"
InputGestureText="Back" />
<MenuItem <MenuItem
x:Name="menuRemoveDuplicateServer" x:Name="menuRemoveDuplicateServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -148,15 +151,18 @@
<MenuItem <MenuItem
x:Name="menuTcpingServer" x:Name="menuTcpingServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuTcpingServer}" /> Header="{x:Static resx:ResUI.menuTcpingServer}"
InputGestureText="Ctrl+O" />
<MenuItem <MenuItem
x:Name="menuRealPingServer" x:Name="menuRealPingServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRealPingServer}" /> Header="{x:Static resx:ResUI.menuRealPingServer}"
InputGestureText="Ctrl+R" />
<MenuItem <MenuItem
x:Name="menuSpeedServer" x:Name="menuSpeedServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSpeedServer}" /> Header="{x:Static resx:ResUI.menuSpeedServer}"
InputGestureText="Ctrl+T" />
<MenuItem <MenuItem
x:Name="menuSortServerResult" x:Name="menuSortServerResult"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -184,29 +190,35 @@
<MenuItem <MenuItem
x:Name="menuMoveTop" x:Name="menuMoveTop"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveTop}" /> Header="{x:Static resx:ResUI.menuMoveTop}"
InputGestureText="T" />
<MenuItem <MenuItem
x:Name="menuMoveUp" x:Name="menuMoveUp"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveUp}" /> Header="{x:Static resx:ResUI.menuMoveUp}"
InputGestureText="U" />
<MenuItem <MenuItem
x:Name="menuMoveDown" x:Name="menuMoveDown"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuMoveDown}"
InputGestureText="D" />
<MenuItem <MenuItem
x:Name="menuMoveBottom" x:Name="menuMoveBottom"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveBottom}" /> Header="{x:Static resx:ResUI.menuMoveBottom}"
InputGestureText="B" />
</MenuItem> </MenuItem>
<MenuItem <MenuItem
x:Name="menuSelectAll" x:Name="menuSelectAll"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSelectAll}" /> Header="{x:Static resx:ResUI.menuSelectAll}"
InputGestureText="Ctrl+A" />
<Separator /> <Separator />
<MenuItem <MenuItem
x:Name="menuShareServer" x:Name="menuShareServer"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuShareServer}" /> Header="{x:Static resx:ResUI.menuShareServer}"
InputGestureText="Ctrl+F" />
<MenuItem Header="{x:Static resx:ResUI.menuExportConfig}"> <MenuItem Header="{x:Static resx:ResUI.menuExportConfig}">
<MenuItem <MenuItem
x:Name="menuExport2ClientConfig" x:Name="menuExport2ClientConfig"
@@ -220,7 +232,8 @@
<MenuItem <MenuItem
x:Name="menuExport2ShareUrl" x:Name="menuExport2ShareUrl"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuExport2ShareUrl}" /> Header="{x:Static resx:ResUI.menuExport2ShareUrl}"
InputGestureText="Ctrl+C" />
<MenuItem <MenuItem
x:Name="menuExport2ShareUrlBase64" x:Name="menuExport2ShareUrlBase64"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"

View File

@@ -292,33 +292,37 @@ public partial class ProfilesView
} }
else else
{ {
if (e.Key is Key.Enter or Key.Return) switch (e.Key)
{ {
ViewModel?.SetDefaultServer(); case Key.Enter:
} //case Key.Return:
else if (e.Key == Key.Delete) ViewModel?.SetDefaultServer();
{ break;
ViewModel?.RemoveServerAsync();
} case Key.Delete:
else if (e.Key == Key.T) case Key.Back:
{ ViewModel?.RemoveServerAsync();
ViewModel?.MoveServer(EMove.Top); break;
}
else if (e.Key == Key.U) case Key.T:
{ ViewModel?.MoveServer(EMove.Top);
ViewModel?.MoveServer(EMove.Up); break;
}
else if (e.Key == Key.D) case Key.U:
{ ViewModel?.MoveServer(EMove.Up);
ViewModel?.MoveServer(EMove.Down); break;
}
else if (e.Key == Key.B) case Key.D:
{ ViewModel?.MoveServer(EMove.Down);
ViewModel?.MoveServer(EMove.Bottom); break;
}
else if (e.Key == Key.Escape) case Key.B:
{ ViewModel?.MoveServer(EMove.Bottom);
ViewModel?.ServerSpeedtestStop(); break;
case Key.Escape:
ViewModel?.ServerSpeedtestStop();
break;
} }
} }
} }

View File

@@ -263,11 +263,13 @@
<MenuItem <MenuItem
x:Name="menuRuleRemove" x:Name="menuRuleRemove"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRuleRemove}" /> Header="{x:Static resx:ResUI.menuRuleRemove}"
InputGestureText="Back" />
<MenuItem <MenuItem
x:Name="menuRuleSelectAll" x:Name="menuRuleSelectAll"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSelectAll}" /> Header="{x:Static resx:ResUI.menuSelectAll}"
InputGestureText="Ctrl+A" />
<MenuItem <MenuItem
x:Name="menuRuleExportSelected" x:Name="menuRuleExportSelected"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
@@ -276,19 +278,23 @@
<MenuItem <MenuItem
x:Name="menuMoveTop" x:Name="menuMoveTop"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveTop}" /> Header="{x:Static resx:ResUI.menuMoveTop}"
InputGestureText="T" />
<MenuItem <MenuItem
x:Name="menuMoveUp" x:Name="menuMoveUp"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveUp}" /> Header="{x:Static resx:ResUI.menuMoveUp}"
InputGestureText="U" />
<MenuItem <MenuItem
x:Name="menuMoveDown" x:Name="menuMoveDown"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveDown}" /> Header="{x:Static resx:ResUI.menuMoveDown}"
InputGestureText="D" />
<MenuItem <MenuItem
x:Name="menuMoveBottom" x:Name="menuMoveBottom"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuMoveBottom}" /> Header="{x:Static resx:ResUI.menuMoveBottom}"
InputGestureText="B" />
</ContextMenu> </ContextMenu>
</DataGrid.ContextMenu> </DataGrid.ContextMenu>
<DataGrid.Columns> <DataGrid.Columns>

View File

@@ -140,25 +140,28 @@ public partial class RoutingRuleSettingWindow
} }
else else
{ {
if (e.Key == Key.T) switch (e.Key)
{ {
ViewModel?.MoveRule(EMove.Top); case Key.T:
} ViewModel?.MoveRule(EMove.Top);
else if (e.Key == Key.U) break;
{
ViewModel?.MoveRule(EMove.Up); case Key.U:
} ViewModel?.MoveRule(EMove.Up);
else if (e.Key == Key.D) break;
{
ViewModel?.MoveRule(EMove.Down); case Key.D:
} ViewModel?.MoveRule(EMove.Down);
else if (e.Key == Key.B) break;
{
ViewModel?.MoveRule(EMove.Bottom); case Key.B:
} ViewModel?.MoveRule(EMove.Bottom);
else if (e.Key == Key.Delete) break;
{
ViewModel?.RuleRemoveAsync(); case Key.Delete:
case Key.Back:
ViewModel?.RuleRemoveAsync();
break;
} }
} }
} }

View File

@@ -144,15 +144,18 @@
<MenuItem <MenuItem
x:Name="menuRoutingAdvancedRemove" x:Name="menuRoutingAdvancedRemove"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}" /> Header="{x:Static resx:ResUI.menuRoutingAdvancedRemove}"
InputGestureText="Back" />
<MenuItem <MenuItem
x:Name="menuRoutingAdvancedSelectAll" x:Name="menuRoutingAdvancedSelectAll"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuSelectAll}" /> Header="{x:Static resx:ResUI.menuSelectAll}"
InputGestureText="Ctrl+A" />
<MenuItem <MenuItem
x:Name="menuRoutingAdvancedSetDefault" x:Name="menuRoutingAdvancedSetDefault"
Height="{StaticResource MenuItemHeight}" Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}" /> Header="{x:Static resx:ResUI.menuRoutingAdvancedSetDefault}"
InputGestureText="Enter" />
<Separator /> <Separator />
<MenuItem <MenuItem
x:Name="menuRoutingAdvancedImportRules" x:Name="menuRoutingAdvancedImportRules"

View File

@@ -78,18 +78,27 @@ public partial class RoutingSettingWindow
{ {
if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
{ {
if (e.Key == Key.A) switch (e.Key)
{ {
lstRoutings.SelectAll(); case Key.A:
lstRoutings.SelectAll();
break;
} }
} }
else if (e.Key is Key.Enter or Key.Return) else
{ {
ViewModel?.RoutingAdvancedSetDefault(); switch (e.Key)
} {
else if (e.Key == Key.Delete) case Key.Enter:
{ //case Key.Return:
ViewModel?.RoutingAdvancedRemoveAsync(); ViewModel?.RoutingAdvancedSetDefault();
break;
case Key.Delete:
case Key.Back:
ViewModel?.RoutingAdvancedRemoveAsync();
break;
}
} }
} }