Compare commits

...

2 Commits

Author SHA1 Message Date
chen08209 6c9b9043e7 Support custom overwrite
Support run on demand

Optimize windows ipc

Optimize windows arm64

Optimize build

Optimize some details

Update core
2026-05-26 10:43:36 +08:00
chen08209 672eaccd35 Update changelog 2026-02-02 02:38:00 +00:00
384 changed files with 36637 additions and 9251 deletions
+93
View File
@@ -0,0 +1,93 @@
Scan `lib/` for hardcoded Chinese strings and internationalize them following the project's ARB conventions.
## Instructions
You are performing i18n extraction for a Flutter project that uses the Flutter Intl plugin with ARB files.
### Project localization context
- **ARB files**: `arb/intl_en.arb`, `arb/intl_zh_CN.arb`, `arb/intl_ja.arb`, `arb/intl_ru.arb`
- **Key conventions**: camelCase, flat keys (no nesting, no `@` metadata), semantic naming
- Short labels: `rule`, `global`, `direct`, `edit`, `confirm`
- Descriptive suffix `Desc`: `themeDesc`, `tunDesc`, `logsDesc`
- Validation suffix: `profileNameNullValidationDesc`
- **Generated code**: `lib/l10n/l10n.dart` (DO NOT edit generated files)
- **Access patterns**:
- In widgets with BuildContext: `context.appLocalizations.key` (import `common.dart`)
- In controllers/providers/non-widget code: `currentAppLocalizations.key` (import `app_localizations.dart`)
### Step 1: Find hardcoded Chinese strings
Use Grep to search `lib/` for Chinese characters. The regex pattern for Chinese characters is `[\x{4e00}-\x{9fff}]`.
Important filters:
- **Exclude** `lib/l10n/` (generated localization files)
- **Exclude** lines that already use `appLocalizations` or `currentAppLocalizations`
- **Exclude** false positives that are NOT Chinese text:
- Arrow symbols: `↑` `↓` `←` `→` (Unicode arrows, not CJK)
- Middle dot: `·` (U+00B7, not CJK)
- Command symbol: `⌘` (U+2318, not CJK)
- Other CJK punctuation that is used as UI symbols, not translatable text
For each match, read the surrounding context (5-10 lines) to understand:
- What the string represents (label, error message, tooltip, dialog text, etc.)
- Whether it's inside a widget (has `BuildContext` available) or non-widget code
- Whether it appears in a `Text()`, `TextSpan()`, `title:`, `label:`, or other text position
### Step 2: Generate ARB keys
For each hardcoded Chinese string:
1. **Choose a key name** following project conventions:
- Use the Chinese meaning to derive an English camelCase key
- Keep it concise: `createConfig` not `createAConfigurationForTheProfile`
- Use `Desc` suffix for descriptive/explanatory text
- Use existing similar keys as reference (check `arb/intl_en.arb` for patterns)
2. **Determine the English translation** (the value for `intl_en.arb`)
3. **Determine the Chinese translation** (the value for `intl_zh_CN.arb`) — this is the original hardcoded string
4. **For ja and ru**: use the English translation as a placeholder (translators will fill these in later)
### Step 3: Update ARB files
For each new key, add it to all 4 ARB files. Read each ARB file first to find the right insertion point (alphabetical order is not required, but appending near the end is clean).
Format: add a new line like `"keyName": "value",` before the closing `}`.
### Step 4: Replace hardcoded strings in Dart files
Replace each hardcoded Chinese string with the appropriate localization call:
- If inside a widget BuildContext: `context.appLocalizations.keyName`
- If in controller/provider code: `currentAppLocalizations.keyName`
Ensure the correct import exists:
- For `context.appLocalizations`: the file should import `common.dart` (check if it already does)
- For `currentAppLocalizations`: the file should import `app_localizations.dart` (check if it already does)
If the file uses string interpolation (e.g., `'创建 $name 配置'`), convert to the ARB parameterized format:
- In ARB: `"keyName": "Create $name config"`
- In Dart: `currentAppLocalizations.keyName(name)`
### Step 5: Report changes
After all modifications, output a summary:
1. List each file modified and what changed
2. List each new ARB key added with its translations
3. Remind the user to run code generation if needed:
```
dart run build_runner build --delete-conflicting-outputs
```
or trigger Flutter Intl regeneration in their IDE.
### Important notes
- Only extract strings that are user-facing UI text. Do NOT extract:
- Comments (lines starting with `//`)
- Log messages (unless they appear in the UI)
- Variable names or identifiers
- String literals used as keys/identifiers (not displayed to users)
- If a Chinese string is split across multiple lines or concatenations, merge them into a single ARB entry
- If a string contains dynamic content (variables, URLs), use ARB parameter syntax `$variableName`
+46 -11
View File
@@ -8,7 +8,33 @@ env:
IS_STABLE: ${{ !contains(github.ref, '-') }}
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup Flutter
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.41.9
cache: true
- name: Install dependencies
run: flutter pub get
- name: Analyze
run: flutter analyze --no-fatal-infos
- name: Run tests
run: flutter test --reporter expanded
build:
needs: [ test ]
runs-on: ${{ matrix.os }}
strategy:
matrix:
@@ -27,9 +53,9 @@ jobs:
- platform: macos
os: macos-latest
arch: arm64
# - platform: windows
# os: windows-11-arm
# arch: arm64
- platform: windows
os: windows-11-arm
arch: arm64
- platform: linux
os: ubuntu-24.04-arm
arch: arm64
@@ -64,28 +90,40 @@ jobs:
cache-dependency-path: |
core/go.sum
- name: Enable git long paths (Windows)
if: startsWith(matrix.os, 'windows')
run: git config --global core.longpaths true
- name: Setup Flutter
if: ${{ !(startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')) }}
uses: subosito/flutter-action@v2
with:
channel: stable
flutter-version: 3.35.7
flutter-version: 3.41.9
cache: true
- name: Setup Flutter With Other
if: startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')
uses: subosito/flutter-action@v2
with:
channel: master
flutter-version: 3.35.7
flutter-version: 3.44.0-0.1.pre
cache: true
- name: Setup NDK
if: startsWith(matrix.platform,'android')
uses: nttld/setup-ndk@v1
with:
ndk-version: r28c
add-to-path: true
link-to-sdk: true
- name: Get Flutter Dependency
run: |
flutter --version
flutter pub get
- name: Setup
run: dart setup.dart ${{ matrix.platform }} ${{ matrix.arch && format('--arch {0}', matrix.arch) }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }}
run: dart setup.dart ${{ matrix.platform }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }} -v
- name: Upload
uses: actions/upload-artifact@v4
@@ -210,12 +248,10 @@ jobs:
sed "s|VERSION|$version|g" ./.github/release_template.md >> release.md
- name: Generate sha256
if: env.IS_STABLE == 'true'
if: ${{ env.IS_STABLE == 'true' }}
run: |
cd ./dist
for file in $(find . -type f -not -name "*.sha256"); do
sha256sum "$file" > "${file}.sha256"
done
sha256sum * > SHA256SUMS
- name: Release
if: ${{ env.IS_STABLE == 'true' }}
@@ -245,4 +281,3 @@ jobs:
target-branch: main
commit-message: Update from ${{ github.ref_name }}
target-directory: /tmp/
+10 -3
View File
@@ -41,9 +41,11 @@ app.*.symbols
# Obfuscation related
app.*.map.json
#AI generated
CLAUDE.md
/.claude
# Claude Code - ignore user-specific settings only
.claude/settings.local.json
# Documentation (generated/planning artifacts)
docs/
# Android Studio will place build artifacts here
@@ -63,6 +65,11 @@ CLAUDE.md
/android/app/src/main/jniLibs/
/services/helper/target
/macos/**/Package.resolved
/macos/build/
/env.json
/core_sha256.json
/core/*.exe
/plugins/rust_api/.claude/
devtools_options.yaml
# FVM Version Cache
+233
View File
@@ -0,0 +1,233 @@
# AGENTS.md
This file provides guidance for AI coding agents working with code in this repository.
## Project Overview
FlClash is a multi-platform proxy client based on ClashMeta (mihomo), built with Flutter. Supports Android, Windows,
macOS, and Linux. Material You design with Surfboard-like UI.
## Common Development Commands
### Building
```bash
# Update submodules first (ClashMeta Go core lives in core/Clash.Meta/)
git submodule update --init --recursive
# Full package build (Go core + Flutter + packaging) via setup.dart
dart setup.dart macos
dart setup.dart linux
dart setup.dart windows
dart setup.dart android
# Build only the Go core (skip Flutter packaging)
bash plugins/setup/buildkit/run_build_tool.sh macos
bash plugins/setup/buildkit/run_build_tool.sh linux
bash plugins/setup/buildkit/run_build_tool.sh windows
bash plugins/setup/buildkit/run_build_tool.sh android
```
### Flutter Development
```bash
# Project is pinned with FVM (.fvmrc currently uses Flutter 3.35.7)
fvm flutter pub get
fvm flutter run
fvm flutter test
# Plain Flutter also works when your global SDK matches the project constraints
flutter pub get
flutter run # Run on connected device/desktop
flutter test # Run all tests (use flutter test, not dart test — models pull in Flutter types)
```
### Code Generation
Required after modifying models, providers, or database schema:
```bash
dart run build_runner build --delete-conflicting-outputs
dart run build_runner watch # Continuous regeneration
```
Code generation covers: Riverpod providers (`riverpod_generator`), models (`freezed`, `json_serializable`), and database
tables (`drift_dev`).
### Testing
Tests use `package:test/test.dart` for pure Dart logic (common utils, models) and `flutter_test` for provider/widget tests.
`mocktail` is the mocking framework.
```bash
flutter test test/models/ # Model serialization & extension round-trip tests
flutter test test/core/ # CoreController tests (mocked CoreHandlerInterface)
flutter test test/providers/ # Riverpod provider tests (config & app state notifiers)
flutter test test/common/ # Utility function tests (utils, string, iterable, fixed, etc.)
flutter test test/database/ # Database type converter tests
flutter test test/widgets/ # Widget-level rendering/interaction tests
flutter test test/setup_test.dart
flutter test plugins/proxy/test/proxy_test.dart # Dart tests for bundled plugin packages
```
Root `flutter test` only discovers the root package's `test/` directory by default. Include bundled plugin Dart tests by
passing their paths explicitly, or run `flutter test` from that plugin package directory. Native plugin tests under
platform folders (for example Windows C++ tests) are not run by `flutter test`.
**Mocking `CoreHandlerInterface`:** Use `CoreController.test(mock)` to inject a mock interface. Call
`CoreController.resetInstance()` in `tearDown` to clean up the singleton between tests. Remember to
`registerFallbackValue()` for freezed params used with `any()` matchers.
**Provider tests:** Use `ProviderContainer` directly (no widget tree needed for simple notifiers). The Riverpod
generated `update()` method takes a callback: `notifier.update((state) => newValue)`.
**Model round-trip tests:** Always go through `jsonEncode`/`jsonDecode` when testing freezed models with
nested objects — `toJson()` stores child objects directly (not as maps), so direct `fromJson(toJson())`
fails for nested freezed types.
### Build Dependencies
**Linux:** `sudo apt-get install libayatana-appindicator3-dev libkeybinder-3.0-dev`
**Windows:** GCC and Inno Setup. `ANDROID_NDK` env var for Android builds.
**macOS:** `npm install -g appdmg` for DMG creation.
## Architecture
### Core Integration (Go ClashMeta <-> Flutter)
This is the most important architectural concept. The Go proxy core (`core/`) operates in two modes:
- **Android (lib mode):** Go core compiled as C shared library (`libclash.so`) via `go build -buildmode=c-shared` with
CGO. Flutter calls it via FFI through the `service` plugin. Dart-side: `lib/core/lib.dart` (`CoreLib` class).
- **Desktop (core mode):** Go core runs as a separate process with `CGO_ENABLED=0`. Flutter communicates via
JSON-over-socket (Unix socket on macOS/Linux, TCP on Windows). Dart-side: `lib/core/service.dart` (`CoreService`
class).
`lib/core/controller.dart` (`CoreController`) selects the implementation based on platform. `lib/core/interface.dart`
defines the shared `CoreHandlerInterface`.
Go core key files: `core/hub.go` (handler functions), `core/action.go` (dispatch), `core/lib.go` (CGO exports),
`core/server.go` (socket server).
### State Management (Riverpod)
Provider files in `lib/providers/`:
- `app.dart` - Runtime/UI state (logs, traffic, delays, loading, navigation)
- `config.dart` - Persistent config providers (app settings, theme, VPN, proxy style)
- `state.dart` - Derived/computed providers (navigation, proxy, tray, color scheme)
- `action.dart` - Business logic notifiers (setup, backup, core lifecycle, proxy selection)
- `database.dart` - Drift database provider wrappers
`globalState` (`lib/state.dart`) is a singleton holding app lifecycle, timers, theme, and the start/stop state.
Providers are generated into `lib/providers/generated/`.
### Database (Drift/SQLite)
Type-safe SQLite via Drift in `lib/database/`. Current schema version is 2. Tables are `Profiles`, `Scripts`, `Rules`,
`ProfileRuleLinks` (`profile_rule_mapping`), `ProxyGroups`, and `IconRecords` (`icon_records`). Rule scenes distinguish
global added rules, profile added rules, profile custom rules, and disabled links. Uses fractional indexing for rule and
proxy-group ordering.
Generated Drift output lives in `lib/database/generated/database.g.dart`. After schema changes, run code generation and
add/update focused database tests under `test/database/` when converter or migration behavior changes.
### Manager Stack (Widget Tree)
Managers are nested InheritedWidgets/StatefulWidgets in `lib/application.dart`:
```
AppEnvManager > StatusManager > ThemeManager
> [Desktop: WindowManager > TrayManager > HotKeyManager > ProxyManager]
> ConnectivityManager > CoreManager > AppStateManager
> [Mobile: AndroidManager > VpnManager | Desktop: WindowHeaderContainer]
```
Each manager in `lib/manager/` handles a specific platform concern. Desktop-only managers are conditionally inserted.
### Core Controller + Actions
`lib/core/controller.dart` (`CoreController`) is a singleton facade over `CoreHandlerInterface`. All 25+ public methods
delegate to the platform-specific interface (Android FFI or desktop socket). Has `@visibleForTesting` constructor and
`resetInstance()` for test injection.
Business logic lives in Riverpod notifier classes in `lib/providers/action.dart` (~960 lines, should be split):
- `CommonAction` — update check, common UI operations
- `SetupAction` — config setup, TUN management
- `BackupAction` — backup/restore with WebDAV sync
- `CoreAction` — core lifecycle (init, connect, restart, shutdown)
- `SystemAction` — system integration (tray, exit, brightness)
- `StoreAction` — profile storage operations
- `ThemeAction` — theme state updates
- `ProxiesAction` — group management, proxy selection
- `ProfilesAction` — profile CRUD, auto-update, import
### Platform Managers (`lib/manager/`)
Desktop: `WindowManager`, `TrayManager`, `HotKeyManager`, `ProxyManager`
Mobile: `AndroidManager`, `TileManager`, `VpnManager`
Shared: `ConnectivityManager`, `CoreManager`, `AppStateManager`, `StatusManager`, `ThemeManager`
### Build System
`setup.dart` (project root) is the release build orchestrator:
1. On Windows, pre-builds Go core via `dart run build_tool windows` and reads `core_sha256.json`
2. Writes `env.json` (APP_ENV)
3. Passes SHA256 as `--dart-define=CORE_SHA256=$val` (compile-time embedded, secure; Windows only)
4. Activates `flutter_distributor` for packaging
Go core building is handled by `build_tool`, a standalone Dart CLI in `plugins/setup/buildkit/build_tool/`.
Platform build hooks inside `flutter build` trigger `build_tool` automatically:
- **macOS:** podspec script phase → `build_pod.sh` → `build_tool macos`
- **Linux:** CMake include → `buildkit/cmake/buildkit.cmake` → `build_tool linux`
- **Windows:** CMake include → `buildkit/cmake/buildkit.cmake` → `build_tool windows` (debug: `--dev` via `CMAKE_BUILD_TYPE`)
- **Android:** Gradle include → `buildkit/gradle/plugin.gradle` → `build_tool android`
**Windows helper auth (release):** Core SHA256 is embedded in both the Flutter app (`--dart-define`) and the Rust
helper (`TOKEN` env var during cargo build). The Dart app pings the helper and verifies the token matches.
**Windows helper auth (debug):** The Rust helper skips token verification when built in debug mode
(`cfg!(debug_assertions)`), so `flutter run` works without any SHA256 dance.
`plugins/setup/` is an FFI plugin that exists solely as a build harness — it carries no Dart API, only platform build hooks
(podspec, CMake, Gradle) that trigger Go compilation. Windows builds also compile a Rust helper (`services/helper/`) via
`RustBuilder`.
Build configuration defaults live in `build_tool/lib/src/options.dart` and can be overridden via `build_config.yaml`
in the project root.
Architecture detection is automatic (host arch via `uname -m` on Unix, `PROCESSOR_ARCHITECTURE` on Windows). The
`--description` flag passed to flutter_distributor adds arch suffix to artifact names (e.g.,
`FlClash-0.8.93-macos-arm64.dmg`).
### Local Plugins (`plugins/`)
- `setup` - Build harness FFI plugin (triggers Go/Rust compilation per platform)
- `proxy` - System proxy configuration
- `rust_api` - Flutter Rust Bridge FFI plugin (named pipe / local socket communication)
- `tray_manager` - System tray (forked/custom)
- `wifi_ssid` - Wi-Fi SSID detection
- `window_ext` - Window extensions
- `flutter_distributor` - App packaging/distribution
### Rust Helper Service (`services/helper/`)
Windows-only privileged helper for starting the core as admin and managing TUN. Built with
`cargo build --release --features windows-service`. Token-based auth with Flutter app.
### Localization
ARB files in `arb/`. Generated via `flutter_intl` into `lib/l10n/`. Use `AppLocalizations.of(context)!` for strings.
**Supported locales:** `en`, `zh_CN`, `ja`, `ru`
**Access patterns:**
- In widgets with BuildContext: `context.appLocalizations.key` (import `common.dart`)
- In controllers/providers/non-widget code: `currentAppLocalizations.key` (import `app_localizations.dart`)
+10
View File
@@ -1,3 +1,13 @@
## v0.8.92
- Add sqlite store
- Optimize android quick action
- Optimize backup and restore
- Optimize more details
## v0.8.91
- Fix windows some issues
+5
View File
@@ -0,0 +1,5 @@
# CLAUDE.md - Claude Code instructions
Please read AGENTS.md for all project guidelines and conventions.
@AGENTS.md
+25 -20
View File
@@ -74,46 +74,51 @@ Support the following actions
- android
1. Install `Android SDK` , `Android NDK`
1. Install `Android SDK`, `Android NDK`
2. Set `ANDROID_NDK` environment variables
3. Run Build script
```bash
dart .\setup.dart android
```
- windows
1. You need a windows client
2. Install `Gcc`,`Inno Setup`
2. Set `ANDROID_NDK` environment variable
3. Run build script
```bash
dart .\setup.dart windows --arch <arm64 | amd64>
dart setup.dart android
```
- windows
1. Requires a Windows client
2. Install `GCC`, `Inno Setup`
3. Run build script
```bash
dart setup.dart windows
```
- linux
1. You need a linux client
1. Requires a Linux client
2. Run build script
2. Dependencies are auto-installed by setup script, or manually:
```bash
sudo apt-get install -y libayatana-appindicator3-dev libkeybinder-3.0-dev
```
3. Run build script
```bash
dart .\setup.dart linux --arch <arm64 | amd64>
dart setup.dart linux
```
- macOS
1. You need a macOS client
1. Requires a macOS client
2. Run build script
```bash
dart .\setup.dart macos --arch <arm64 | amd64>
dart setup.dart macos
```
## Star
+13 -8
View File
@@ -81,29 +81,34 @@ on Mobile:
3. 运行构建脚本
```bash
dart .\setup.dart android
dart setup.dart android
```
- windows
1. 你需要一个windows客户端
2. 安装 `Gcc`,`Inno Setup`
2. 安装 `GCC`,`Inno Setup`
3. 运行构建脚本
```bash
dart .\setup.dart windows --arch <arm64 | amd64>
dart setup.dart windows
```
- linux
1. 你需要一个linux客户端
2. 运行构建脚本
2. 依赖会由 setup 脚本自动安装,也可以手动安装:
```bash
sudo apt-get install -y libayatana-appindicator3-dev libkeybinder-3.0-dev
```
3. 运行构建脚本
```bash
dart .\setup.dart linux --arch <arm64 | amd64>
dart setup.dart linux
```
- macOS
@@ -113,10 +118,10 @@ on Mobile:
2. 运行构建脚本
```bash
dart .\setup.dart macos --arch <arm64 | amd64>
dart setup.dart macos
```
## Star History
## Star
支持开发者的最简单方式是点击页面顶部的星标(⭐)。
@@ -124,4 +129,4 @@ on Mobile:
<a href="https://api.star-history.com/svg?repos=chen08209/FlClash&Date">
<img alt="start" width=50% src="https://api.star-history.com/svg?repos=chen08209/FlClash&Date"/>
</a>
</p>
</p>
+34 -1
View File
@@ -1,10 +1,43 @@
include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- build/**
- lib/l10n/intl/**
- lib/**/generated/**
- plugins/**
errors:
invalid_annotation_target: ignore
linter:
rules:
prefer_single_quotes: true
# Style
prefer_single_quotes: true
require_trailing_commas: true
sort_child_properties_last: true
# Avoid unnecessary code
unnecessary_new: true
unnecessary_this: true
unnecessary_const: true
avoid_print: true
avoid_unnecessary_containers: true
sized_box_for_whitespace: true
# Prefer best practices
prefer_const_constructors: true
prefer_const_declarations: true
prefer_final_locals: true
prefer_final_in_for_each: true
prefer_is_empty: true
prefer_is_not_empty: true
prefer_interpolation_to_compose_strings: true
# Type safety
always_declare_return_types: true
annotate_overrides: true
use_key_in_widget_constructors: true
no_leading_underscores_for_local_identifiers: true
# Async
use_build_context_synchronously: true
unnecessary_await_in_return: true
+3
View File
@@ -16,12 +16,15 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission
android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.POST_PROMOTED_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<application
android:name=".Application"
@@ -9,12 +9,15 @@ import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
import android.net.VpnService
import android.os.Build
import android.os.PowerManager
import android.provider.Settings
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import androidx.core.net.toUri
import com.android.tools.smali.dexlib2.dexbacked.DexBackedDexFile
import com.follow.clash.R
import com.follow.clash.common.Components
@@ -157,6 +160,18 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
result.success(true)
}
"isBatteryOptimizationDisabled" -> {
result.success(isBatteryOptimizationDisabled())
}
"openBatteryOptimizationSettings" -> {
result.success(openBatteryOptimizationSettings())
}
"openAppSettings" -> {
result.success(openAppSettings())
}
else -> {
result.notImplemented()
}
@@ -196,6 +211,36 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
GlobalState.application.showToast(message)
}
private fun isBatteryOptimizationDisabled(): Boolean {
val powerManager = getSystemService(GlobalState.application, PowerManager::class.java)
return powerManager?.isIgnoringBatteryOptimizations(GlobalState.application.packageName)
?: false
}
private fun openBatteryOptimizationSettings(): Boolean {
return try {
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {
data = "package:${GlobalState.application.packageName}".toUri()
}
activityRef?.get()?.startActivity(intent)
true
} catch (_: Exception) {
false
}
}
private fun openAppSettings(): Boolean {
return try {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = "package:${GlobalState.application.packageName}".toUri()
}
activityRef?.get()?.startActivity(intent)
true
} catch (_: Exception) {
false
}
}
@Suppress("DEPRECATION")
private fun updateExcludeFromRecents(value: Boolean?) {
val am = getSystemService(GlobalState.application, ActivityManager::class.java)
@@ -304,11 +349,8 @@ class AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware
skipPrefixList.forEach {
if (packageName == it || packageName.startsWith("$it.")) return false
}
val packageManagerFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val packageManagerFlags =
PackageManager.MATCH_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
} else {
PackageManager.GET_UNINSTALLED_PACKAGES or PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS
}
if (packageName.matches(chinaAppRegex)) {
return true
}
-26
View File
@@ -53,29 +53,3 @@ kotlin {
dependencies {
implementation(libs.annotation.jvm)
}
val copyNativeLibs by tasks.register<Copy>("copyNativeLibs") {
doFirst {
delete("src/main/jniLibs")
}
from("../../libclash/android")
into("src/main/jniLibs")
doLast {
val includesDir = file("src/main/jniLibs/includes")
val targetDir = file("src/main/cpp/includes")
if (includesDir.exists()) {
copy {
from(includesDir)
into(targetDir)
}
delete(includesDir)
}
}
}
afterEvaluate {
tasks.named("preBuild") {
dependsOn(copyNativeLibs)
}
}
+8 -5
View File
@@ -14,12 +14,15 @@ if (NOT "${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
endif ()
set(LIB_CLASH_PATH "${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libclash.so")
set(LIB_CLASH_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../cpp/includes/${ANDROID_ABI}")
set(LIB_CLASH_HEADER_PATH "${LIB_CLASH_INCLUDE_DIR}/libclash.h")
set(BRIDE_HEADER_PATH "${LIB_CLASH_INCLUDE_DIR}/bride.h")
message("LIB_CLASH_PATH ${LIB_CLASH_PATH}")
if (EXISTS ${LIB_CLASH_PATH})
message("Found libclash.so for ABI ${ANDROID_ABI}")
if (EXISTS ${LIB_CLASH_PATH} AND EXISTS ${LIB_CLASH_HEADER_PATH} AND EXISTS ${BRIDE_HEADER_PATH})
message("Found libclash.so and headers for ABI ${ANDROID_ABI}")
add_compile_definitions(LIBCLASH)
include_directories(${CMAKE_SOURCE_DIR}/../cpp/includes/${ANDROID_ABI})
include_directories(${LIB_CLASH_INCLUDE_DIR})
link_directories(${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
add_library(${CMAKE_PROJECT_NAME} SHARED
jni_helper.cpp
@@ -27,9 +30,9 @@ if (EXISTS ${LIB_CLASH_PATH})
target_link_libraries(${CMAKE_PROJECT_NAME}
clash)
else ()
message("Not found libclash.so for ABI ${ANDROID_ABI}")
message("Not found complete libclash output for ABI ${ANDROID_ABI}; building core without LIBCLASH")
add_library(${CMAKE_PROJECT_NAME} SHARED
jni_helper.cpp
core.cpp)
target_link_libraries(${CMAKE_PROJECT_NAME})
endif ()
endif ()
+1 -1
View File
@@ -4,7 +4,7 @@ firebaseBom = "34.2.0"
minSdk = "23"
targetSdk = "36"
compileSdk = "36"
ndkVersion = "28.0.13004108"
ndkVersion = "28.2.13676358"
coreKtx = "1.17.0"
annotationJvm = "1.9.1"
coreSplashscreen = "1.0.1"
@@ -85,22 +85,24 @@ class NotificationModule(private val service: Service) : Module() {
private val notificationBuilder: NotificationCompat.Builder by lazy {
val intent = Intent().setComponent(Components.MAIN_ACTIVITY)
with(
NotificationCompat.Builder(
service, GlobalState.NOTIFICATION_CHANNEL
)
) {
setSmallIcon(R.drawable.ic)
NotificationCompat.Builder(
service, GlobalState.NOTIFICATION_CHANNEL
).apply {
setSmallIcon(R.drawable.ic_service)
setContentTitle("FlClash")
setContentIntent(intent.toPendingIntent)
setPriority(NotificationCompat.PRIORITY_HIGH)
setCategory(NotificationCompat.CATEGORY_SERVICE)
setOngoing(true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE
}
setOngoing(true)
setShowWhen(true)
setOnlyAlertOnce(true)
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
// setRequestPromotedOngoing(true)
// }
}
}
+118 -57
View File
@@ -12,14 +12,11 @@
"resources": "Resources",
"resourcesDesc": "External resource related info",
"trafficUsage": "Traffic usage",
"coreInfo": "Core info",
"networkSpeed": "Network speed",
"outboundMode": "Outbound mode",
"networkDetection": "Network detection",
"upload": "Upload",
"download": "Download",
"noProxy": "No proxy",
"noProxyDesc": "Please create a profile or add a valid profile",
"nullProfileDesc": "No profile, Please add a profile",
"settings": "Settings",
"language": "Language",
@@ -34,7 +31,6 @@
"theme": "Theme",
"themeDesc": "Set dark mode,adjust the color",
"override": "Override",
"overrideDesc": "Override Proxy related config",
"allowLan": "AllowLan",
"allowLanDesc": "Allow access proxy through the LAN",
"tun": "TUN",
@@ -61,14 +57,7 @@
"add": "Add",
"save": "Save",
"delete": "Delete",
"years": "Years",
"months": "Months",
"hours": "Hours",
"days": "Days",
"minutes": "Minutes",
"seconds": "Seconds",
"ago": " Ago",
"just": "Just",
"qrcode": "QR code",
"qrcodeDesc": "Scan QR code to obtain profile",
"url": "URL",
@@ -93,26 +82,16 @@
"submit": "Submit",
"doYouWantToPass": "Do you want to pass",
"create": "Create",
"defaultSort": "Sort by default",
"delaySort": "Sort by delay",
"nameSort": "Sort by name",
"pleaseUploadFile": "Please upload file",
"pleaseUploadValidQrcode": "Please upload a valid QR code",
"blacklistMode": "Blacklist mode",
"whitelistMode": "Whitelist mode",
"filterSystemApp": "Filter system app",
"cancelFilterSystemApp": "Cancel filter system app",
"selectAll": "Select all",
"cancelSelectAll": "Cancel select all",
"appAccessControl": "App access control",
"accessControlAllowDesc": "Only allow selected app to enter VPN",
"accessControlNotAllowDesc": "The selected application will be excluded from VPN",
"selected": "Selected",
"unableToUpdateCurrentProfileDesc": "unable to update current profile",
"noMoreInfoDesc": "No more info",
"profileParseErrorDesc": "profile parse error",
"proxyPort": "ProxyPort",
"proxyPortDesc": "Set the Clash listening port",
"port": "Port",
"logLevel": "LogLevel",
"show": "Show",
@@ -124,9 +103,7 @@
"desc": "A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
"startVpn": "Starting VPN...",
"stopVpn": "Stopping VPN...",
"discovery": "Discovery a new version",
"compatible": "Compatibility mode",
"compatibleDesc": "Opening it will lose part of its application ability and gain the support of full amount of Clash.",
"notSelectedTip": "The current proxy group cannot be selected.",
"tip": "tip",
"account": "Account",
@@ -146,11 +123,7 @@
"checkUpdateError": "The current application is already the latest version",
"goDownload": "Go to download",
"unknown": "Unknown",
"geoData": "GeoData",
"externalResources": "External resources",
"checking": "Checking...",
"country": "Country",
"checkError": "Check error",
"search": "Search",
"allowBypass": "Allow applications to bypass VPN",
"allowBypassDesc": "Some apps can bypass VPN when turned on",
@@ -159,7 +132,6 @@
"ipv6Desc": "When turned on it will be able to receive IPv6 traffic",
"app": "App",
"general": "General",
"vpnSystemProxyDesc": "Attach HTTP proxy to VpnService",
"systemProxyDesc": "Attach HTTP proxy to VpnService",
"unifiedDelay": "Unified delay",
"unifiedDelayDesc": "Remove extra delays such as handshaking",
@@ -172,7 +144,6 @@
"findProcessMode": "Find process",
"init": "Init",
"infiniteTime": "Long term effective",
"expirationTime": "Expiration time",
"connections": "Connections",
"connectionsDesc": "View current connections data",
"intranetIP": "Intranet IP",
@@ -184,10 +155,6 @@
"sync": "Sync",
"exclude": "Hidden from recent tasks",
"excludeDesc": "When the app is in the background, the app is hidden from the recent task",
"oneColumn": "One column",
"twoColumns": "Two columns",
"threeColumns": "Three columns",
"fourColumns": "Four columns",
"expand": "Standard",
"shrink": "Shrink",
"min": "Min",
@@ -198,7 +165,6 @@
"size": "Size",
"sort": "Sort",
"columns": "Columns",
"proxiesSetting": "Proxies setting",
"proxyGroup": "Proxy group",
"go": "Go",
"externalLink": "External link",
@@ -217,8 +183,6 @@
"mode": "Mode",
"time": "Time",
"source": "Source",
"allApps": "All apps",
"onlyOtherApps": "Only third-party apps",
"action": "Action",
"intelligentSelected": "Intelligent selection",
"clipboardImport": "Clipboard import",
@@ -230,8 +194,6 @@
"profilesSort": "Profiles sort",
"start": "Start",
"stop": "Stop",
"appDesc": "Processing app related settings",
"vpnDesc": "Modify VPN related settings",
"dnsDesc": "Update DNS related settings",
"key": "Key",
"value": "Value",
@@ -292,21 +254,15 @@
"exportSuccess": "Export Success",
"iconStyle": "Icon style",
"onlyIcon": "Icon",
"noIcon": "None",
"stackMode": "Stack mode",
"network": "Network",
"networkDesc": "Modify network-related settings",
"bypassDomain": "Bypass domain",
"bypassDomainDesc": "Only takes effect when the system proxy is enabled",
"resetTip": "Make sure to reset",
"regExp": "RegExp",
"icon": "Icon",
"iconConfiguration": "Icon configuration",
"noData": "No data",
"adminAutoLaunch": "Admin auto launch",
"adminAutoLaunchDesc": "Boot up by using admin mode",
"fontFamily": "FontFamily",
"systemFont": "System font",
"toggle": "Toggle",
"system": "System",
"routeMode": "Route mode",
@@ -342,15 +298,9 @@
"ruleTarget": "Rule target",
"sourceIp": "Source IP",
"noResolve": "No resolve IP",
"getOriginRules": "Get original rules",
"overrideOriginRules": "Override the original rule",
"addedOriginRules": "Attach on the original rules",
"enableOverride": "Enable override",
"saveChanges": "Do you want to save the changes?",
"generalDesc": "Modify general settings",
"findProcessModeDesc": "There is a certain performance loss after opening",
"tabAnimationDesc": "Effective only in mobile view",
"saveTip": "Are you sure you want to save?",
"colorSchemes": "Color schemes",
"palette": "Palette",
"tonalSpotScheme": "TonalSpot",
@@ -372,7 +322,6 @@
"internet": "Internet",
"systemApp": "System APP",
"noNetworkApp": "No network APP",
"contactMe": "Contact me",
"restoreStrategy": "Restore strategy",
"restoreStrategy_override": "Override",
"restoreStrategy_compatible": "Compatible",
@@ -390,7 +339,6 @@
"rename": "Rename",
"unnamed": "Unnamed",
"pleaseEnterScriptName": "Please enter a script name",
"overrideInvalidTip": "Does not take effect in script mode",
"mixedPort": "Mixed Port",
"socksPort": "Socks Port",
"redirPort": "Redir Port",
@@ -453,8 +401,6 @@
"accessControlSettings": "Access Control Settings",
"turnOn": "Turn On",
"turnOff": "Turn Off",
"coreConfigChangeDetected": "Core configuration change detected",
"reload": "Reload",
"vpnConfigChangeDetected": "VPN configuration change detected",
"restart": "Restart",
"speedStatistics": "Speed statistics",
@@ -462,7 +408,6 @@
"overwriteTypeCustom": "Custom",
"overwriteTypeCustomDesc": "Custom mode, fully customize proxy groups and rules",
"unknownNetworkError": "Unknown network error",
"networkRequestException": "Network request exception, please try again later.",
"restoreException": "Recovery exception",
"networkException": "Network exception, please check your connection and try again",
"invalidBackupFile": "Invalid backup file",
@@ -476,5 +421,121 @@
"restoreOnlyConfig": "Restore configuration files only",
"restoreAllData": "Restore all data",
"addProfile": "Add Profile",
"delayTest": "Delay Test"
}
"delayTest": "Delay Test",
"proxyGroupEmpty": "Proxy group is empty",
"proxyGroupNameEmpty": "Proxy group name cannot be empty",
"proxyGroupNameDuplicate": "Proxy group name is duplicate",
"confirmExitWindow": "Are you sure you want to exit the current window?",
"dataChangedSave": "Data changes detected, do you want to save?",
"selectProxyProviders": "Select proxy providers",
"proxyFilter": "Proxy filter",
"optional": "Optional",
"maxFailedTimes": "Max failed times",
"testInterval": "Test interval",
"excludeProxyFilter": "Exclude proxy filter",
"excludeType": "Exclude type",
"expectedStatus": "Expected status",
"selectProxies": "Select proxies",
"inputProxyGroupName": "Input proxy group name",
"hideFromList": "Hide from list",
"testWhenUsed": "Test when used",
"disableUDP": "Disable UDP",
"confirmDeleteProxyGroup": "Are you sure you want to delete the current proxy group?",
"ruleEmpty": "Rule is empty",
"inputRuleContent": "Input rule content",
"ruleSet": "Rule set",
"selectRuleSet": "Please select rule set",
"splitStrategy": "Split strategy",
"selectSplitStrategy": "Please select split strategy",
"selectSubRule": "Please select sub rule",
"noResolveHostname": "No resolve hostname",
"matchSourceIp": "Match source IP",
"basicInfo": "Basic info",
"additionalParameters": "Additional parameters",
"proxyType": "Proxy type",
"basicStrategy": "Basic strategy",
"editProxy": "Edit proxy",
"includeAllProxyProviders": "Include all proxy providers",
"includeAllProxyProvidersTip": "When enabled, it will override the imported proxy providers",
"addProxyProviders": "Add proxy providers",
"includeAllProxies": "Include all proxies",
"includeAllProxiesTip": "Import all proxies not containing proxy groups, additional proxy groups can be added below",
"proxiesEmpty": "Proxies is empty",
"addProxies": "Add proxies",
"addProxyGroup": "Add proxy group",
"editProxyGroup": "Edit proxy group",
"confirmOverwriteTip": "Existing data will be overwritten after confirmation",
"configDataDetected": "Data detected in configuration",
"quickFill": "Quick fill",
"iconUrl": "Icon URL",
"iconRecords": "Icon records",
"noRecords": "No records",
"custom": "Custom",
"ruleActionDomainDesc": "Match full domain",
"ruleActionDomainSuffixDesc": "Match domain suffix",
"ruleActionDomainKeywordDesc": "Match domain keyword",
"ruleActionDomainRegexDesc": "Wildcard match, only supports * and ? wildcards",
"ruleActionGeositeDesc": "Match domains within Geosite",
"ruleActionIpCidrDesc": "Match IP address range",
"ruleActionIpCidr6Desc": "Match IP address range, IP-CIDR6 is just an alias",
"ruleActionIpSuffixDesc": "Match IP suffix range",
"ruleActionIpAsnDesc": "Match IP's ASN",
"ruleActionGeoipDesc": "Match IP's country code",
"ruleActionSrcGeoipDesc": "Match source IP's country code",
"ruleActionSrcIpAsnDesc": "Match source IP's ASN",
"ruleActionSrcIpCidrDesc": "Match source IP address range",
"ruleActionSrcIpSuffixDesc": "Match source IP suffix range",
"ruleActionDstPortDesc": "Match request target port range",
"ruleActionSrcPortDesc": "Match request source port range",
"ruleActionInPortDesc": "Match inbound port",
"ruleActionInTypeDesc": "Match inbound type",
"ruleActionInUserDesc": "Match inbound username, supports multiple usernames separated by /",
"ruleActionInNameDesc": "Match inbound name",
"ruleActionProcessPathDesc": "Match using full process path",
"ruleActionProcessPathRegexDesc": "Match using process path regex",
"ruleActionProcessNameDesc": "Match using process name, matches package name on Android",
"ruleActionProcessNameRegexDesc": "Match using process name regex, matches package name on Android",
"ruleActionUidDesc": "Match Linux USER ID",
"ruleActionNetworkDesc": "Match TCP or UDP",
"ruleActionDscpDesc": "Match DSCP mark (tproxy udp inbound only)",
"ruleActionRuleSetDesc": "Reference rule set, requires rule-providers configuration",
"ruleActionAndDesc": "Logical rule AND",
"ruleActionOrDesc": "Logical rule OR",
"ruleActionNotDesc": "Logical rule NOT",
"ruleActionSubRuleDesc": "Match to sub-rule, pay attention to the use of parentheses",
"ruleActionMatchDesc": "Match all requests, no conditions needed",
"subRuleEmpty": "Sub rule is empty",
"proxyProvidersNotEmpty": "Proxy providers cannot be empty",
"contentNotEmpty": "Content cannot be empty",
"subRuleNotEmpty": "Sub rule cannot be empty",
"splitStrategyNotEmpty": "Split strategy cannot be empty",
"proxyProvidersEmpty": "Proxy providers is empty",
"timeout": "Timeout",
"invalidSubRule": "{subRule} is an invalid SUB_RULE",
"invalidPolicy": "{target} is an invalid policy",
"invalidProxyProvider": "{providerName} is an invalid proxy provider",
"invalidProxy": "{proxyName} is an invalid proxy",
"proxyGroupDetectedAbnormal": "Detected current proxy group is abnormal",
"proxyProviderDetectedAbnormal": "Detected selected proxy providers are abnormal",
"proxyDetectedAbnormal": "Detected selected proxies are abnormal",
"createProfile": "Create Profile",
"locationPermissionRequired": "Location Permission Required",
"locationPermissionGuide": "1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check {appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.",
"prerequisites": "Prerequisites",
"ignoreBatteryOptimization": "Ignore Battery Optimization",
"batteryOptimizationDesc": "To ensure background operation, please disable battery optimization for this app. Tap to go to settings.",
"batteryOptimizationStatusTip": "Affected by the system, this status may not always be accurate.",
"locationPermission": "Location Permission",
"locationPermissionDesc": "According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
"excludeSsids": "Exclude SSIDs",
"excludeSsidsDesc": "When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
"ssidsEmpty": "SSIDs is empty",
"onDemand": "On Demand",
"onDemandDesc": "Configure the program running state for specific scenarios",
"locationPermissionDeniedMessage": "Location permission was denied, so the current Wi-Fi name cannot be obtained. Please open location permission manually in system settings.",
"addSsid": "Add SSID",
"editSsid": "Edit SSID",
"authorized": "Authorized",
"tapToAuthorize": "Tap to authorize",
"suspended": "Suspended..."
}
+118 -58
View File
@@ -12,14 +12,11 @@
"resources": "リソース",
"resourcesDesc": "外部リソース関連情報",
"trafficUsage": "トラフィック使用量",
"coreInfo": "コア情報",
"networkSpeed": "ネットワーク速度",
"outboundMode": "アウトバウンドモード",
"networkDetection": "ネットワーク検出",
"upload": "アップロード",
"download": "ダウンロード",
"noProxy": "プロキシなし",
"noProxyDesc": "プロファイルを作成するか、有効なプロファイルを追加してください",
"nullProfileDesc": "プロファイルがありません。追加してください",
"settings": "設定",
"language": "言語",
@@ -34,7 +31,6 @@
"theme": "テーマ",
"themeDesc": "ダークモードの設定、色の調整",
"override": "上書き",
"overrideDesc": "プロキシ関連設定を上書き",
"allowLan": "LANを許可",
"allowLanDesc": "LAN経由でのプロキシアクセスを許可",
"tun": "TUN",
@@ -61,14 +57,7 @@
"add": "追加",
"save": "保存",
"delete": "削除",
"years": "年",
"months": "月",
"hours": "時間",
"days": "日",
"minutes": "分",
"seconds": "秒",
"ago": "前",
"just": "たった今",
"qrcode": "QRコード",
"qrcodeDesc": "QRコードをスキャンしてプロファイルを取得",
"url": "URL",
@@ -93,26 +82,16 @@
"submit": "送信",
"doYouWantToPass": "通過させますか?",
"create": "作成",
"defaultSort": "デフォルト順",
"delaySort": "遅延順",
"nameSort": "名前順",
"pleaseUploadFile": "ファイルをアップロードしてください",
"pleaseUploadValidQrcode": "有効なQRコードをアップロードしてください",
"blacklistMode": "ブラックリストモード",
"whitelistMode": "ホワイトリストモード",
"filterSystemApp": "システムアプリを除外",
"cancelFilterSystemApp": "システムアプリの除外を解除",
"selectAll": "すべて選択",
"cancelSelectAll": "全選択解除",
"appAccessControl": "アプリアクセス制御",
"accessControlAllowDesc": "選択したアプリのみVPNを許可",
"accessControlNotAllowDesc": "選択したアプリをVPNから除外",
"selected": "選択済み",
"unableToUpdateCurrentProfileDesc": "現在のプロファイルを更新できません",
"noMoreInfoDesc": "追加情報なし",
"profileParseErrorDesc": "プロファイル解析エラー",
"proxyPort": "プロキシポート",
"proxyPortDesc": "Clashのリスニングポートを設定",
"port": "ポート",
"logLevel": "ログレベル",
"show": "表示",
@@ -124,9 +103,7 @@
"desc": "ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
"startVpn": "VPNを開始中...",
"stopVpn": "VPNを停止中...",
"discovery": "新しいバージョンを発見",
"compatible": "互換モード",
"compatibleDesc": "有効化すると一部機能を失いますが、Clashの完全サポートを獲得",
"notSelectedTip": "現在のプロキシグループは選択できません",
"tip": "ヒント",
"account": "アカウント",
@@ -146,11 +123,7 @@
"checkUpdateError": "アプリは最新版です",
"goDownload": "ダウンロードへ",
"unknown": "不明",
"geoData": "地域データ",
"externalResources": "外部リソース",
"checking": "確認中...",
"country": "国",
"checkError": "確認エラー",
"search": "検索",
"allowBypass": "アプリがVPNをバイパスすることを許可",
"allowBypassDesc": "有効化すると一部アプリがVPNをバイパス",
@@ -159,7 +132,6 @@
"ipv6Desc": "有効化するとIPv6トラフィックを受信可能",
"app": "アプリ",
"general": "一般",
"vpnSystemProxyDesc": "HTTPプロキシをVpnServiceに接続",
"systemProxyDesc": "HTTPプロキシをVpnServiceに接続",
"unifiedDelay": "統一遅延",
"unifiedDelayDesc": "ハンドシェイクなどの余分な遅延を削除",
@@ -172,7 +144,6 @@
"findProcessMode": "プロセス検出",
"init": "初期化",
"infiniteTime": "長期有効",
"expirationTime": "有効期限",
"connections": "接続",
"connectionsDesc": "現在の接続データを表示",
"intranetIP": "イントラネットIP",
@@ -184,10 +155,6 @@
"sync": "同期",
"exclude": "最近のタスクから非表示",
"excludeDesc": "アプリがバックグラウンド時に最近のタスクから非表示",
"oneColumn": "1列",
"twoColumns": "2列",
"threeColumns": "3列",
"fourColumns": "4列",
"expand": "標準",
"shrink": "縮小",
"min": "最小化",
@@ -198,7 +165,6 @@
"size": "サイズ",
"sort": "並び替え",
"columns": "列",
"proxiesSetting": "プロキシ設定",
"proxyGroup": "プロキシグループ",
"go": "移動",
"externalLink": "外部リンク",
@@ -217,8 +183,6 @@
"mode": "モード",
"time": "時間",
"source": "ソース",
"allApps": "全アプリ",
"onlyOtherApps": "サードパーティアプリのみ",
"action": "アクション",
"intelligentSelected": "インテリジェント選択",
"clipboardImport": "クリップボードからインポート",
@@ -230,8 +194,6 @@
"profilesSort": "プロファイルの並び替え",
"start": "開始",
"stop": "停止",
"appDesc": "アプリ関連設定の処理",
"vpnDesc": "VPN関連設定の変更",
"dnsDesc": "DNS関連設定の更新",
"key": "キー",
"value": "値",
@@ -292,21 +254,15 @@
"exportSuccess": "エクスポート成功",
"iconStyle": "アイコンスタイル",
"onlyIcon": "アイコンのみ",
"noIcon": "なし",
"stackMode": "スタックモード",
"network": "ネットワーク",
"networkDesc": "ネットワーク関連設定の変更",
"bypassDomain": "バイパスドメイン",
"bypassDomainDesc": "システムプロキシ有効時のみ適用",
"resetTip": "リセットを確定",
"regExp": "正規表現",
"icon": "アイコン",
"iconConfiguration": "アイコン設定",
"noData": "データなし",
"adminAutoLaunch": "管理者自動起動",
"adminAutoLaunchDesc": "管理者モードで起動",
"fontFamily": "フォントファミリー",
"systemFont": "システムフォント",
"toggle": "トグル",
"system": "システム",
"routeMode": "ルートモード",
@@ -342,15 +298,9 @@
"ruleTarget": "ルール対象",
"sourceIp": "送信元IP",
"noResolve": "IPを解決しない",
"getOriginRules": "元のルールを取得",
"overrideOriginRules": "元のルールを上書き",
"addedOriginRules": "元のルールに追加",
"enableOverride": "上書きを有効化",
"saveChanges": "変更を保存しますか?",
"generalDesc": "一般設定を変更",
"findProcessModeDesc": "有効化するとパフォーマンスが若干低下します",
"tabAnimationDesc": "モバイル表示でのみ有効",
"saveTip": "保存してもよろしいですか?",
"colorSchemes": "カラースキーム",
"palette": "パレット",
"tonalSpotScheme": "トーンスポット",
@@ -368,12 +318,10 @@
"messageTestTip": "これはメッセージです。",
"crashTest": "クラッシュテスト",
"clearData": "データを消去",
"zoom": "ズーム",
"textScale": "テキストスケーリング",
"internet": "インターネット",
"systemApp": "システムアプリ",
"noNetworkApp": "ネットワークなしアプリ",
"contactMe": "連絡する",
"restoreStrategy": "復元ストラテジー",
"restoreStrategy_override": "上書き",
"restoreStrategy_compatible": "互換",
@@ -391,7 +339,6 @@
"rename": "リネーム",
"unnamed": "無題",
"pleaseEnterScriptName": "スクリプト名を入力してください",
"overrideInvalidTip": "スクリプトモードでは有効になりません",
"mixedPort": "混合ポート",
"socksPort": "Socksポート",
"redirPort": "Redirポート",
@@ -454,8 +401,6 @@
"accessControlSettings": "アクセス制御設定",
"turnOn": "オン",
"turnOff": "オフ",
"coreConfigChangeDetected": "コア設定の変更が検出されました",
"reload": "リロード",
"vpnConfigChangeDetected": "VPN設定の変更が検出されました",
"restart": "再起動",
"speedStatistics": "速度統計",
@@ -463,7 +408,6 @@
"overwriteTypeCustom": "カスタム",
"overwriteTypeCustomDesc": "カスタムモード、プロキシグループとルールを完全にカスタマイズ可能",
"unknownNetworkError": "不明なネットワークエラー",
"networkRequestException": "ネットワーク要求例外、後でもう一度試してください。",
"restoreException": "復元例外",
"networkException": "ネットワーク例外、接続を確認してもう一度お試しください",
"invalidBackupFile": "無効なバックアップファイル",
@@ -477,5 +421,121 @@
"restoreOnlyConfig": "設定ファイルのみを復元する",
"restoreAllData": "すべてのデータを復元する",
"addProfile": "プロファイルを追加",
"delayTest": "遅延テスト"
}
"delayTest": "遅延テスト",
"proxyGroupEmpty": "プロキシグループが空です",
"proxyGroupNameEmpty": "プロキシグループ名は空にできません",
"proxyGroupNameDuplicate": "プロキシグループ名が重複しています",
"confirmExitWindow": "現在のウィンドウを閉じてもよろしいですか?",
"dataChangedSave": "データの変更が検出されました。保存しますか?",
"selectProxyProviders": "プロキシプロバイダーを選択",
"proxyFilter": "プロキシフィルター",
"optional": "オプション",
"maxFailedTimes": "最大失敗回数",
"testInterval": "テスト間隔",
"excludeProxyFilter": "除外プロキシフィルター",
"excludeType": "除外タイプ",
"expectedStatus": "期待されるステータス",
"selectProxies": "プロキシを選択",
"inputProxyGroupName": "プロキシグループ名を入力",
"hideFromList": "リストから隠す",
"testWhenUsed": "使用時にテスト",
"disableUDP": "UDPを無効化",
"confirmDeleteProxyGroup": "現在のプロキシグループを削除してもよろしいですか?",
"ruleEmpty": "ルールが空です",
"inputRuleContent": "ルール内容を入力",
"ruleSet": "ルールセット",
"selectRuleSet": "ルールセットを選択してください",
"splitStrategy": "分流戦略",
"selectSplitStrategy": "分流戦略を選択してください",
"selectSubRule": "サブルールを選択してください",
"noResolveHostname": "ホスト名を解決しない",
"matchSourceIp": "送信元IPをマッチング",
"basicInfo": "基本情報",
"additionalParameters": "追加パラメータ",
"proxyType": "プロキシタイプ",
"basicStrategy": "基本戦略",
"editProxy": "プロキシを編集",
"includeAllProxyProviders": "すべてのプロキシプロバイダーを含める",
"includeAllProxyProvidersTip": "有効にすると、インポートされたプロキシプロバイダーを上書きします",
"addProxyProviders": "プロキシプロバイダーを追加",
"includeAllProxies": "すべてのプロキシを含める",
"includeAllProxiesTip": "プロキシグループに含まれないすべてのプロキシをインポートします。下でさらにプロキシグループを追加できます",
"proxiesEmpty": "プロキシが空です",
"addProxies": "プロキシを追加",
"addProxyGroup": "プロキシグループを追加",
"editProxyGroup": "プロキシグループを編集",
"confirmOverwriteTip": "確認後、既存のデータは上書きされます",
"configDataDetected": "設定内にデータが検出されました",
"quickFill": "クイック入力",
"iconUrl": "アイコンURL",
"iconRecords": "アイコン履歴",
"noRecords": "履歴なし",
"custom": "カスタム",
"ruleActionDomainDesc": "完全なドメインをマッチング",
"ruleActionDomainSuffixDesc": "ドメイン接尾辞をマッチング",
"ruleActionDomainKeywordDesc": "ドメインキーワードをマッチング",
"ruleActionDomainRegexDesc": "ワイルドカードマッチング(*と?のみサポート)",
"ruleActionGeositeDesc": "Match domains within Geosite",
"ruleActionIpCidrDesc": "IPアドレス範囲をマッチング",
"ruleActionIpCidr6Desc": "IPアドレス範囲をマッチング(IP-CIDR6はエイリアスです)",
"ruleActionIpSuffixDesc": "IP接尾辞範囲をマッチング",
"ruleActionIpAsnDesc": "IPのASNをマッチング",
"ruleActionGeoipDesc": "IPの国コードをマッチング",
"ruleActionSrcGeoipDesc": "送信元IPの国コードをマッチング",
"ruleActionSrcIpAsnDesc": "送信元IPのASNをマッチング",
"ruleActionSrcIpCidrDesc": "送信元IPアドレス範囲をマッチング",
"ruleActionSrcIpSuffixDesc": "送信元IP接尾辞範囲をマッチング",
"ruleActionDstPortDesc": "宛先ポート範囲をマッチング",
"ruleActionSrcPortDesc": "送信元ポート範囲をマッチング",
"ruleActionInPortDesc": "インバウンドポートをマッチング",
"ruleActionInTypeDesc": "インバウンドタイプをマッチング",
"ruleActionInUserDesc": "インバウンドユーザー名をマッチング(/で複数指定可)",
"ruleActionInNameDesc": "インバウンド名をマッチング",
"ruleActionProcessPathDesc": "フルプロセスパスでマッチング",
"ruleActionProcessPathRegexDesc": "プロセスパス正規表現でマッチング",
"ruleActionProcessNameDesc": "プロセス名でマッチング(Androidではパッケージ名)",
"ruleActionProcessNameRegexDesc": "プロセス名正規表現でマッチング(Androidではパッケージ名)",
"ruleActionUidDesc": "Linux USER IDをマッチング",
"ruleActionNetworkDesc": "TCPまたはUDPをマッチング",
"ruleActionDscpDesc": "DSCPマークをマッチング (tproxy udp inboundのみ)",
"ruleActionRuleSetDesc": "ルールセットを参照。rule-providersの設定が必要",
"ruleActionAndDesc": "論理ルール AND",
"ruleActionOrDesc": "論理ルール OR",
"ruleActionNotDesc": "論理ルール NOT",
"ruleActionSubRuleDesc": "サブルールにマッチング。括弧の使用に注意",
"ruleActionMatchDesc": "すべてのリクエストにマッチ(条件なし)",
"subRuleEmpty": "サブルールが空です",
"proxyProvidersNotEmpty": "プロキシプロバイダーは空にできません",
"contentNotEmpty": "内容は空にできません",
"subRuleNotEmpty": "サブルールは空にできません",
"splitStrategyNotEmpty": "分流戦略は空にできません",
"proxyProvidersEmpty": "プロキシプロバイダーが空です",
"timeout": "タイムアウト",
"invalidSubRule": "{subRule} は無効なSUB_RULEです",
"invalidPolicy": "{target} は無効なポリシーです",
"invalidProxyProvider": "{providerName} は無効なプロキシプロバイダーです",
"invalidProxy": "{proxyName} は無効なプロキシです",
"proxyGroupDetectedAbnormal": "現在のプロキシグループが異常であることを検出しました",
"proxyProviderDetectedAbnormal": "選択されたプロキシプロバイダーに異常があることを検出しました",
"proxyDetectedAbnormal": "選択されたプロキシに異常があることを検出しました",
"createProfile": "Create Profile",
"locationPermissionRequired": "Location Permission Required",
"locationPermissionGuide": "1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check {appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.",
"prerequisites": "Prerequisites",
"ignoreBatteryOptimization": "Ignore Battery Optimization",
"batteryOptimizationDesc": "To ensure background operation, please disable battery optimization for this app. Tap to go to settings.",
"batteryOptimizationStatusTip": "システムの影響により、この状態は必ずしも正確とは限りません。",
"locationPermission": "Location Permission",
"locationPermissionDesc": "According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
"excludeSsids": "Exclude SSIDs",
"excludeSsidsDesc": "When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
"ssidsEmpty": "SSIDs is empty",
"onDemand": "On Demand",
"onDemandDesc": "Configure the program running state for specific scenarios",
"locationPermissionDeniedMessage": "位置情報の権限が拒否されたため、現在の Wi-Fi 名を取得できません。システム設定で位置情報の権限を手動で有効にしてください。",
"addSsid": "SSIDを追加",
"editSsid": "SSIDを編集",
"authorized": "許可済み",
"tapToAuthorize": "タップして許可",
"suspended": "一時停止中..."
}
+118 -66
View File
@@ -12,14 +12,11 @@
"resources": "Ресурсы",
"resourcesDesc": "Информация, связанная с внешними ресурсами",
"trafficUsage": "Использование трафика",
"coreInfo": "Информация о ядре",
"networkSpeed": "Скорость сети",
"outboundMode": "Режим исходящего трафика",
"networkDetection": "Обнаружение сети",
"upload": "Загрузка",
"download": "Скачивание",
"noProxy": "Нет прокси",
"noProxyDesc": "Пожалуйста, создайте профиль или добавьте действительный профиль",
"nullProfileDesc": "Нет профиля, пожалуйста, добавьте профиль",
"settings": "Настройки",
"language": "Язык",
@@ -34,7 +31,6 @@
"theme": "Тема",
"themeDesc": "Установить темный режим, настроить цвет",
"override": "Переопределить",
"overrideDesc": "Переопределить конфигурацию, связанную с прокси",
"allowLan": "Разрешить LAN",
"allowLanDesc": "Разрешить доступ к прокси через локальную сеть",
"tun": "TUN",
@@ -61,14 +57,7 @@
"add": "Добавить",
"save": "Сохранить",
"delete": "Удалить",
"years": "Лет",
"months": "Месяцев",
"hours": "Часов",
"days": "Дней",
"minutes": "Минут",
"seconds": "Секунд",
"ago": " назад",
"just": "Только что",
"qrcode": "QR-код",
"qrcodeDesc": "Сканируйте QR-код для получения профиля",
"url": "URL",
@@ -93,26 +82,16 @@
"submit": "Отправить",
"doYouWantToPass": "Вы хотите пропустить",
"create": "Создать",
"defaultSort": "Сортировка по умолчанию",
"delaySort": "Сортировка по задержке",
"nameSort": "Сортировка по имени",
"pleaseUploadFile": "Пожалуйста, загрузите файл",
"pleaseUploadValidQrcode": "Пожалуйста, загрузите действительный QR-код",
"blacklistMode": "Режим черного списка",
"whitelistMode": "Режим белого списка",
"filterSystemApp": "Фильтровать системные приложения",
"cancelFilterSystemApp": "Отменить фильтрацию системных приложений",
"selectAll": "Выбрать все",
"cancelSelectAll": "Отменить выбор всего",
"appAccessControl": "Контроль доступа приложений",
"accessControlAllowDesc": "Разрешить только выбранным приложениям доступ к VPN",
"accessControlNotAllowDesc": "Выбранные приложения будут исключены из VPN",
"selected": "Выбрано",
"unableToUpdateCurrentProfileDesc": "невозможно обновить текущий профиль",
"noMoreInfoDesc": "Нет дополнительной информации",
"profileParseErrorDesc": "ошибка разбора профиля",
"proxyPort": "Порт прокси",
"proxyPortDesc": "Установить порт прослушивания Clash",
"port": "Порт",
"logLevel": "Уровень логов",
"show": "Показать",
@@ -124,19 +103,11 @@
"desc": "Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
"startVpn": "Запуск VPN...",
"stopVpn": "Остановка VPN...",
"discovery": "Обнаружена новая версия",
"compatible": "Режим совместимости",
"compatibleDesc": "Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.",
"notSelectedTip": "Текущая группа прокси не может быть выбрана.",
"tip": "подсказка",
"backupAndRecovery": "Резервное копирование и восстановление",
"backupAndRecoveryDesc": "Синхронизация данных через WebDAV или файл",
"account": "Аккаунт",
"backup": "Резервное копирование",
"recovery": "Восстановление",
"recoveryProfiles": "Только восстановление профилей",
"recoveryAll": "Восстановить все данные",
"recoverySuccess": "Восстановление успешно",
"backupSuccess": "Резервное копирование успешно",
"noInfo": "Нет информации",
"pleaseBindWebDAV": "Пожалуйста, привяжите WebDAV",
@@ -152,11 +123,7 @@
"checkUpdateError": "Текущее приложение уже является последней версией",
"goDownload": "Перейти к загрузке",
"unknown": "Неизвестно",
"geoData": "Геоданные",
"externalResources": "Внешние ресурсы",
"checking": "Проверка...",
"country": "Страна",
"checkError": "Ошибка проверки",
"search": "Поиск",
"allowBypass": "Разрешить приложениям обходить VPN",
"allowBypassDesc": "Некоторые приложения могут обходить VPN при включении",
@@ -165,7 +132,6 @@
"ipv6Desc": "При включении будет возможно получать IPv6 трафик",
"app": "Приложение",
"general": "Общие",
"vpnSystemProxyDesc": "Прикрепить HTTP-прокси к VpnService",
"systemProxyDesc": "Прикрепить HTTP-прокси к VpnService",
"unifiedDelay": "Унифицированная задержка",
"unifiedDelayDesc": "Убрать дополнительные задержки, такие как рукопожатие",
@@ -178,7 +144,6 @@
"findProcessMode": "Режим поиска процесса",
"init": "Инициализация",
"infiniteTime": "Долгосрочное действие",
"expirationTime": "Время истечения",
"connections": "Соединения",
"connectionsDesc": "Просмотр текущих данных о соединениях",
"intranetIP": "Внутренний IP",
@@ -190,10 +155,6 @@
"sync": "Синхронизация",
"exclude": "Скрыть из последних задач",
"excludeDesc": "Когда приложение находится в фоновом режиме, оно скрыто из последних задач",
"oneColumn": "Один столбец",
"twoColumns": "Два столбца",
"threeColumns": "Три столбца",
"fourColumns": "Четыре столбца",
"expand": "Стандартный",
"shrink": "Сжать",
"min": "Мин",
@@ -204,7 +165,6 @@
"size": "Размер",
"sort": "Сортировка",
"columns": "Столбцы",
"proxiesSetting": "Настройка прокси",
"proxyGroup": "Группа прокси",
"go": "Перейти",
"externalLink": "Внешняя ссылка",
@@ -219,14 +179,10 @@
"local": "Локальный",
"remote": "Удаленный",
"remoteBackupDesc": "Резервное копирование локальных данных на WebDAV",
"remoteRecoveryDesc": "Восстановление данных с WebDAV",
"localBackupDesc": "Резервное копирование локальных данных на локальный диск",
"localRecoveryDesc": "Восстановление данных из файла",
"mode": "Режим",
"time": "Время",
"source": "Источник",
"allApps": "Все приложения",
"onlyOtherApps": "Только сторонние приложения",
"action": "Действие",
"intelligentSelected": "Интеллектуальный выбор",
"clipboardImport": "Импорт из буфера обмена",
@@ -238,8 +194,6 @@
"profilesSort": "Сортировка профилей",
"start": "Старт",
"stop": "Стоп",
"appDesc": "Обработка настроек, связанных с приложением",
"vpnDesc": "Изменение настроек, связанных с VPN",
"dnsDesc": "Обновление настроек, связанных с DNS",
"key": "Ключ",
"value": "Значение",
@@ -300,21 +254,15 @@
"exportSuccess": "Экспорт успешен",
"iconStyle": "Стиль иконки",
"onlyIcon": "Только иконка",
"noIcon": "Нет иконки",
"stackMode": "Режим стека",
"network": "Сеть",
"networkDesc": "Изменение настроек, связанных с сетью",
"bypassDomain": "Обход домена",
"bypassDomainDesc": "Действует только при включенном системном прокси",
"resetTip": "Убедитесь, что хотите сбросить",
"regExp": "Регулярное выражение",
"icon": "Иконка",
"iconConfiguration": "Конфигурация иконки",
"noData": "Нет данных",
"adminAutoLaunch": "Автозапуск с правами администратора",
"adminAutoLaunchDesc": "Запуск с правами администратора при загрузке системы",
"fontFamily": "Семейство шрифтов",
"systemFont": "Системный шрифт",
"toggle": "Переключить",
"system": "Система",
"routeMode": "Режим маршрутизации",
@@ -350,15 +298,9 @@
"ruleTarget": "Цель правила",
"sourceIp": "Исходный IP",
"noResolve": "Не разрешать IP",
"getOriginRules": "Получить оригинальные правила",
"overrideOriginRules": "Переопределить оригинальное правило",
"addedOriginRules": "Добавить к оригинальным правилам",
"enableOverride": "Включить переопределение",
"saveChanges": "Сохранить изменения?",
"generalDesc": "Изменение общих настроек",
"findProcessModeDesc": "При включении возможны небольшие потери производительности",
"tabAnimationDesc": "Действительно только в мобильном виде",
"saveTip": "Вы уверены, что хотите сохранить?",
"colorSchemes": "Цветовые схемы",
"palette": "Палитра",
"tonalSpotScheme": "Тональный акцент",
@@ -376,12 +318,10 @@
"messageTestTip": "Это сообщение.",
"crashTest": "Тест на сбои",
"clearData": "Очистить данные",
"zoom": "Масштаб",
"textScale": "Масштабирование текста",
"internet": "Интернет",
"systemApp": "Системное приложение",
"noNetworkApp": "Приложение без сети",
"contactMe": "Свяжитесь со мной",
"restoreStrategy": "Стратегия восстановления",
"restoreStrategy_override": "Перезаписать",
"restoreStrategy_compatible": "Совместимый",
@@ -399,7 +339,6 @@
"rename": "Переименовать",
"unnamed": "Без имени",
"pleaseEnterScriptName": "Пожалуйста, введите название скрипта",
"overrideInvalidTip": "В скриптовом режиме не действует",
"mixedPort": "Смешанный порт",
"socksPort": "Socks-порт",
"redirPort": "Redir-порт",
@@ -462,8 +401,6 @@
"accessControlSettings": "Настройки контроля доступа",
"turnOn": "Включить",
"turnOff": "Выключить",
"coreConfigChangeDetected": "Обнаружено изменение конфигурации ядра",
"reload": "Перезагрузить",
"vpnConfigChangeDetected": "Обнаружено изменение конфигурации VPN",
"restart": "Перезапустить",
"speedStatistics": "Статистика скорости",
@@ -471,7 +408,6 @@
"overwriteTypeCustom": "Пользовательский",
"overwriteTypeCustomDesc": "Пользовательский режим, полная настройка групп прокси и правил",
"unknownNetworkError": "Неизвестная сетевая ошибка",
"networkRequestException": "Исключение сетевого запроса, пожалуйста, попробуйте позже.",
"restoreException": "Ошибка восстановления",
"networkException": "Ошибка сети, проверьте соединение и попробуйте еще раз",
"invalidBackupFile": "Неверный файл резервной копии",
@@ -485,5 +421,121 @@
"restoreOnlyConfig": "Восстановить только файлы конфигурации",
"restoreAllData": "Восстановить все данные",
"addProfile": "Добавить профиль",
"delayTest": "Тест задержки"
}
"delayTest": "Тест задержки",
"proxyGroupEmpty": "Группа прокси пуста",
"proxyGroupNameEmpty": "Имя группы прокси не может быть пустым",
"proxyGroupNameDuplicate": "Имя группы прокси дублируется",
"confirmExitWindow": "Вы уверены, что хотите выйти из текущего окна?",
"dataChangedSave": "Обнаружены изменения данных, хотите сохранить?",
"selectProxyProviders": "Выбрать провайдеров прокси",
"proxyFilter": "Фильтр прокси",
"optional": "Необязательно",
"maxFailedTimes": "Макс. количество неудач",
"testInterval": "Интервал тестирования",
"excludeProxyFilter": "Исключить фильтр прокси",
"excludeType": "Тип исключения",
"expectedStatus": "Ожидаемый статус",
"selectProxies": "Выбрать прокси",
"inputProxyGroupName": "Введите имя группы прокси",
"hideFromList": "Скрыть из списка",
"testWhenUsed": "Тестировать при использовании",
"disableUDP": "Отключить UDP",
"confirmDeleteProxyGroup": "Вы уверены, что хотите удалить текущую группу прокси?",
"ruleEmpty": "Правило пусто",
"inputRuleContent": "Введите содержимое правила",
"ruleSet": "Набор правил",
"selectRuleSet": "Пожалуйста, выберите набор правил",
"splitStrategy": "Стратегия разделения",
"selectSplitStrategy": "Пожалуйста, выберите стратегию разделения",
"selectSubRule": "Пожалуйста, выберите подправило",
"noResolveHostname": "Не разрешать имя хоста",
"matchSourceIp": "Сопоставить исходный IP",
"basicInfo": "Основная информация",
"additionalParameters": "Дополнительные параметры",
"proxyType": "Тип прокси",
"basicStrategy": "Базовая стратегия",
"editProxy": "Редактировать прокси",
"includeAllProxyProviders": "Включить всех провайдеров прокси",
"includeAllProxyProvidersTip": "При включении это переопределит импортированных провайдеров прокси",
"addProxyProviders": "Добавить провайдеров прокси",
"includeAllProxies": "Включить все прокси",
"includeAllProxiesTip": "Импорт всех прокси, не содержащих группы прокси, дополнительные группы прокси можно добавить ниже",
"proxiesEmpty": "Список прокси пуст",
"addProxies": "Добавить прокси",
"addProxyGroup": "Добавить группу прокси",
"editProxyGroup": "Редактировать группу прокси",
"confirmOverwriteTip": "Существующие данные будут перезаписаны после подтверждения",
"configDataDetected": "Данные обнаружены в конфигурации",
"quickFill": "Быстрое заполнение",
"iconUrl": "URL иконки",
"iconRecords": "История иконок",
"noRecords": "Нет записей",
"custom": "Пользовательский",
"ruleActionDomainDesc": "Сопоставить полный домен",
"ruleActionDomainSuffixDesc": "Сопоставить суффикс домена",
"ruleActionDomainKeywordDesc": "Сопоставить ключевое слово домена",
"ruleActionDomainRegexDesc": "Сопоставление по маске, поддерживает только * и ?",
"ruleActionGeositeDesc": "Сопоставить домены внутри Geosite",
"ruleActionIpCidrDesc": "Сопоставить диапазон IP-адресов",
"ruleActionIpCidr6Desc": "Сопоставить диапазон IP-адресов (IP-CIDR6 — это просто псевдоним)",
"ruleActionIpSuffixDesc": "Сопоставить диапазон суффиксов IP",
"ruleActionIpAsnDesc": "Сопоставить ASN IP-адреса",
"ruleActionGeoipDesc": "Сопоставить код страны IP-адреса",
"ruleActionSrcGeoipDesc": "Сопоставить код страны исходного IP",
"ruleActionSrcIpAsnDesc": "Сопоставить ASN исходного IP",
"ruleActionSrcIpCidrDesc": "Сопоставить диапазон исходных IP-адресов",
"ruleActionSrcIpSuffixDesc": "Сопоставить диапазон исходных суффиксов IP",
"ruleActionDstPortDesc": "Сопоставить диапазон портов назначения запроса",
"ruleActionSrcPortDesc": "Сопоставить диапазон портов источника запроса",
"ruleActionInPortDesc": "Сопоставить входящий порт",
"ruleActionInTypeDesc": "Сопоставить входящий тип",
"ruleActionInUserDesc": "Сопоставить входящее имя пользователя, поддерживает несколько имен через /",
"ruleActionInNameDesc": "Сопоставить входящее имя",
"ruleActionProcessPathDesc": "Сопоставить по полному пути процесса",
"ruleActionProcessPathRegexDesc": "Сопоставить по регулярному выражению пути процесса",
"ruleActionProcessNameDesc": "Сопоставить по имени процесса, на Android соответствует имени пакета",
"ruleActionProcessNameRegexDesc": "Сопоставить по регулярному выражению имени процесса, на Android соответствует имени пакета",
"ruleActionUidDesc": "Сопоставить Linux USER ID",
"ruleActionNetworkDesc": "Сопоставить TCP или UDP",
"ruleActionDscpDesc": "Сопоставить метку DSCP (только для tproxy udp inbound)",
"ruleActionRuleSetDesc": "Ссылка на набор правил, требуется настройка rule-providers",
"ruleActionAndDesc": "Логическое правило AND",
"ruleActionOrDesc": "Логическое правило OR",
"ruleActionNotDesc": "Логическое правило NOT",
"ruleActionSubRuleDesc": "Сопоставить с подправилом, обратите внимание на использование скобок",
"ruleActionMatchDesc": "Сопоставить все запросы, условия не требуются",
"subRuleEmpty": "Подправило пусто",
"proxyProvidersNotEmpty": "Провайдеры прокси не могут быть пустыми",
"contentNotEmpty": "Содержимое не может быть пустым",
"subRuleNotEmpty": "Подправило не может быть пустым",
"splitStrategyNotEmpty": "Стратегия разделения не может быть пустой",
"proxyProvidersEmpty": "Провайдеры прокси пусты",
"timeout": "Таймаут",
"invalidSubRule": "{subRule} является недопустимым подправилом",
"invalidPolicy": "{target} является недопустимой политикой",
"invalidProxyProvider": "{providerName} является недопустимым провайдером прокси",
"invalidProxy": "{proxyName} является недопустимым прокси",
"proxyGroupDetectedAbnormal": "Обнаружена аномалия текущей группы прокси",
"proxyProviderDetectedAbnormal": "Обнаружена аномалия выбранных провайдеров прокси",
"proxyDetectedAbnormal": "Обнаружена аномалия выбранных прокси",
"createProfile": "Create Profile",
"locationPermissionRequired": "Location Permission Required",
"locationPermissionGuide": "1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check {appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.",
"prerequisites": "Prerequisites",
"ignoreBatteryOptimization": "Ignore Battery Optimization",
"batteryOptimizationDesc": "To ensure background operation, please disable battery optimization for this app. Tap to go to settings.",
"batteryOptimizationStatusTip": "Из-за особенностей системы этот статус не всегда может быть точным.",
"locationPermission": "Location Permission",
"locationPermissionDesc": "According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
"excludeSsids": "Exclude SSIDs",
"excludeSsidsDesc": "When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
"ssidsEmpty": "SSIDs is empty",
"onDemand": "On Demand",
"onDemandDesc": "Configure the program running state for specific scenarios",
"locationPermissionDeniedMessage": "Разрешение на геолокацию отклонено, поэтому невозможно получить имя текущей Wi-Fi сети. Включите разрешение на геолокацию вручную в системных настройках.",
"addSsid": "Добавить SSID",
"editSsid": "Изменить SSID",
"authorized": "Разрешено",
"tapToAuthorize": "Нажмите, чтобы разрешить",
"suspended": "Приостановлено..."
}
+120 -60
View File
@@ -12,14 +12,11 @@
"resources": "资源",
"resourcesDesc": "外部资源相关信息",
"trafficUsage": "流量统计",
"coreInfo": "内核信息",
"networkSpeed": "网络速度",
"outboundMode": "出站模式",
"networkDetection": "网络检测",
"upload": "上传",
"download": "下载",
"noProxy": "暂无代理",
"noProxyDesc": "请创建配置文件或者添加有效配置文件",
"nullProfileDesc": "没有配置文件,请先添加配置文件",
"settings": "设置",
"language": "语言",
@@ -34,7 +31,6 @@
"theme": "主题",
"themeDesc": "设置深色模式,调整色彩",
"override": "覆写",
"overrideDesc": "覆写代理相关配置",
"allowLan": "局域网代理",
"allowLanDesc": "允许通过局域网访问代理",
"tun": "虚拟网卡",
@@ -61,14 +57,7 @@
"add": "添加",
"save": "保存",
"delete": "删除",
"years": "年",
"months": "月",
"hours": "小时",
"days": "天",
"minutes": "分钟",
"seconds": "秒",
"ago": "前",
"just": "刚刚",
"qrcode": "二维码",
"qrcodeDesc": "扫描二维码获取配置文件",
"url": "URL",
@@ -93,26 +82,16 @@
"submit": "提交",
"doYouWantToPass": "是否要通过",
"create": "创建",
"defaultSort": "按默认排序",
"delaySort": "按延迟排序",
"nameSort": "按名称排序",
"pleaseUploadFile": "请上传文件",
"pleaseUploadValidQrcode": "请上传有效的二维码",
"blacklistMode": "黑名单模式",
"whitelistMode": "白名单模式",
"filterSystemApp": "过滤系统应用",
"cancelFilterSystemApp": "取消过滤系统应用",
"selectAll": "全选",
"cancelSelectAll": "取消全选",
"appAccessControl": "应用访问控制",
"accessControlAllowDesc": "只允许选中应用进入VPN",
"accessControlNotAllowDesc": "选中应用将会被排除在VPN之外",
"selected": "已选择",
"unableToUpdateCurrentProfileDesc": "无法更新当前配置文件",
"noMoreInfoDesc": "暂无更多信息",
"profileParseErrorDesc": "配置文件解析错误",
"proxyPort": "代理端口",
"proxyPortDesc": "设置Clash监听端口",
"port": "端口",
"logLevel": "日志等级",
"show": "显示",
@@ -124,9 +103,7 @@
"desc": "基于ClashMeta的多平台代理客户端,简单易用,开源无广告。",
"startVpn": "正在启动VPN...",
"stopVpn": "正在停止VPN...",
"discovery": "发现新版本",
"compatible": "兼容模式",
"compatibleDesc": "开启将失去部分应用能力,获得全量的Clash的支持",
"notSelectedTip": "当前代理组无法选中",
"tip": "提示",
"account": "账号",
@@ -146,11 +123,7 @@
"checkUpdateError": "当前应用已经是最新版了",
"goDownload": "前往下载",
"unknown": "未知",
"geoData": "地理数据",
"externalResources": "外部资源",
"checking": "检测中...",
"country": "区域",
"checkError": "检测失败",
"search": "搜索",
"allowBypass": "允许应用绕过VPN",
"allowBypassDesc": "开启后部分应用可绕过VPN",
@@ -159,7 +132,6 @@
"ipv6Desc": "开启后将可以接收IPv6流量",
"app": "应用",
"general": "常规",
"vpnSystemProxyDesc": "为VpnService附加HTTP代理",
"systemProxyDesc": "设置系统代理",
"unifiedDelay": "统一延迟",
"unifiedDelayDesc": "去除握手等额外延迟",
@@ -172,7 +144,6 @@
"findProcessMode": "查找进程",
"init": "初始化",
"infiniteTime": "长期有效",
"expirationTime": "到期时间",
"connections": "连接",
"connectionsDesc": "查看当前连接数据",
"intranetIP": "内网 IP",
@@ -184,10 +155,6 @@
"sync": "同步",
"exclude": "从最近任务中隐藏",
"excludeDesc": "应用在后台时,从最近任务中隐藏应用",
"oneColumn": "一列",
"twoColumns": "两列",
"threeColumns": "三列",
"fourColumns": "四列",
"expand": "标准",
"shrink": "紧凑",
"min": "最小",
@@ -198,8 +165,7 @@
"size": "尺寸",
"sort": "排序",
"columns": "列数",
"proxiesSetting": "代理设置",
"proxyGroup": "代理组",
"proxyGroup": "策略组",
"go": "前往",
"externalLink": "外部链接",
"otherContributors": "其他贡献者",
@@ -217,8 +183,6 @@
"mode": "模式",
"time": "时间",
"source": "来源",
"allApps": "所有应用",
"onlyOtherApps": "仅第三方应用",
"action": "操作",
"intelligentSelected": "智能选择",
"clipboardImport": "剪贴板导入",
@@ -230,8 +194,6 @@
"profilesSort": "配置排序",
"start": "启动",
"stop": "暂停",
"appDesc": "处理应用相关设置",
"vpnDesc": "修改VPN相关设置",
"dnsDesc": "更新DNS相关设置",
"key": "键",
"value": "值",
@@ -242,8 +204,8 @@
"loopback": "回环解锁工具",
"loopbackDesc": "用于UWP回环解锁",
"providers": "提供者",
"proxyProviders": "代理提供者",
"ruleProviders": "规则提供者",
"proxyProviders": "代理集",
"ruleProviders": "规则集",
"overrideDns": "覆写DNS",
"overrideDnsDesc": "开启后将覆盖配置中的DNS选项",
"status": "状态",
@@ -292,21 +254,15 @@
"exportSuccess": "导出成功",
"iconStyle": "图标样式",
"onlyIcon": "仅图标",
"noIcon": "无图标",
"stackMode": "栈模式",
"network": "网络",
"networkDesc": "修改网络相关设置",
"bypassDomain": "排除域名",
"bypassDomainDesc": "仅在系统代理启用时生效",
"resetTip": "确定要重置吗?",
"regExp": "正则",
"icon": "图片",
"iconConfiguration": "图片配置",
"noData": "暂无数据",
"adminAutoLaunch": "管理员自启动",
"adminAutoLaunchDesc": "使用管理员模式开机自启动",
"fontFamily": "字体",
"systemFont": "系统字体",
"toggle": "切换",
"system": "系统",
"routeMode": "路由模式",
@@ -342,15 +298,9 @@
"ruleTarget": "规则目标",
"sourceIp": "源IP",
"noResolve": "不解析IP",
"getOriginRules": "获取原始规则",
"overrideOriginRules": "覆盖原始规则",
"addedOriginRules": "附加到原始规则",
"enableOverride": "启用覆写",
"saveChanges": "是否保存更改?",
"generalDesc": "修改通用设置",
"findProcessModeDesc": "开启后会有一定性能损耗",
"tabAnimationDesc": "仅在移动视图中有效",
"saveTip": "确定要保存吗?",
"colorSchemes": "配色方案",
"palette": "调色板",
"tonalSpotScheme": "调性点缀",
@@ -368,12 +318,10 @@
"messageTestTip": "这是一条消息。",
"crashTest": "崩溃测试",
"clearData": "清除数据",
"zoom": "缩放",
"textScale": "文本缩放",
"internet": "互联网",
"systemApp": "系统应用",
"noNetworkApp": "无网络应用",
"contactMe": "联系我",
"restoreStrategy": "恢复策略",
"restoreStrategy_override": "覆盖",
"restoreStrategy_compatible": "兼容",
@@ -391,7 +339,6 @@
"rename": "重命名",
"unnamed": "未命名",
"pleaseEnterScriptName": "请输入脚本名称",
"overrideInvalidTip": "在脚本模式下不生效",
"mixedPort": "混合端口",
"socksPort": "Socks端口",
"redirPort": "Redir端口",
@@ -454,8 +401,6 @@
"accessControlSettings": "访问控制设置",
"turnOn": "开启",
"turnOff": "关闭",
"coreConfigChangeDetected": "检测到核心配置更改",
"reload": "重载",
"vpnConfigChangeDetected": "检测到VPN相关配置改动",
"restart": "重启",
"speedStatistics": "网速统计",
@@ -463,7 +408,6 @@
"overwriteTypeCustom": "自定义",
"overwriteTypeCustomDesc": "自定义模式,支持完全自定义修改代理组以及规则",
"unknownNetworkError": "未知网络错误",
"networkRequestException": "网络请求异常,请稍后再试。",
"restoreException": "恢复异常",
"networkException": "网络异常,请检查连接后重试",
"invalidBackupFile": "无效备份文件",
@@ -477,5 +421,121 @@
"restoreOnlyConfig": "仅恢复配置文件",
"restoreAllData": "恢复所有数据",
"addProfile": "添加配置",
"delayTest": "延迟测试"
"delayTest": "延迟测试",
"proxyGroupEmpty": "策略组为空",
"proxyGroupNameEmpty": "策略组名称不能为空",
"proxyGroupNameDuplicate": "策略组名称重复",
"confirmExitWindow": "确定要退出当前窗口吗?",
"dataChangedSave": "检测到数据有更改,是否保存",
"selectProxyProviders": "选择代理集",
"proxyFilter": "节点过滤器",
"optional": "可选",
"maxFailedTimes": "最大失败次数",
"testInterval": "测试间隔",
"excludeProxyFilter": "排除节点过滤器",
"excludeType": "排除类型",
"expectedStatus": "预期状态",
"selectProxies": "选择代理",
"inputProxyGroupName": "输入策略组名称",
"hideFromList": "从列表中隐藏",
"testWhenUsed": "使用时测试",
"disableUDP": "禁用UDP",
"confirmDeleteProxyGroup": "确定要删除当前策略组吗?",
"ruleEmpty": "规则为空",
"inputRuleContent": "输入规则内容",
"ruleSet": "规则集",
"selectRuleSet": "请选择规则集",
"splitStrategy": "分流策略",
"selectSplitStrategy": "请选择分流策略",
"selectSubRule": "请选择子规则",
"noResolveHostname": "不解析主机名",
"matchSourceIp": "匹配来源IP",
"basicInfo": "基础信息",
"additionalParameters": "附加参数",
"proxyType": "代理类型",
"basicStrategy": "基础策略",
"editProxy": "编辑代理",
"includeAllProxyProviders": "包含所有代理集",
"includeAllProxyProvidersTip": "开启后将覆盖引入的代理集",
"addProxyProviders": "添加代理集",
"includeAllProxies": "包含所有代理",
"includeAllProxiesTip": "引入不包含策略组的所有代理,可在下方额外添加策略组",
"proxiesEmpty": "代理为空",
"addProxies": "添加代理",
"addProxyGroup": "添加策略组",
"editProxyGroup": "编辑策略组",
"confirmOverwriteTip": "确定后将会覆盖已有数据",
"configDataDetected": "检测到配置中存在数据",
"quickFill": "一键填入",
"iconUrl": "图标链接",
"iconRecords": "图标记录",
"noRecords": "暂无记录",
"custom": "自定义",
"ruleActionDomainDesc": "匹配完整域名",
"ruleActionDomainSuffixDesc": "匹配域名后缀",
"ruleActionDomainKeywordDesc": "匹配域名关键字",
"ruleActionDomainRegexDesc": "通配符匹配,仅支持*和?通配符",
"ruleActionGeositeDesc": "匹配 Geosite 内的域名",
"ruleActionIpCidrDesc": "匹配 IP 地址范围",
"ruleActionIpCidr6Desc": "匹配 IP 地址范围, IP-CIDR6 只是一个别名",
"ruleActionIpSuffixDesc": "匹配 IP 后缀范围",
"ruleActionIpAsnDesc": "匹配 IP 所属 ASN",
"ruleActionGeoipDesc": "匹配 IP 所属国家代码",
"ruleActionSrcGeoipDesc": "匹配来源 IP 所属国家代码",
"ruleActionSrcIpAsnDesc": "匹配来源 IP 所属 ASN",
"ruleActionSrcIpCidrDesc": "匹配来源 IP 地址范围",
"ruleActionSrcIpSuffixDesc": "匹配来源 IP 后缀范围",
"ruleActionDstPortDesc": "匹配请求目标端口范围",
"ruleActionSrcPortDesc": "匹配请求来源端口范围",
"ruleActionInPortDesc": "匹配入站端口",
"ruleActionInTypeDesc": "匹配入站类型",
"ruleActionInUserDesc": "匹配入站用户名,支持使用 / 分隔多个用户名",
"ruleActionInNameDesc": "匹配入站名称",
"ruleActionProcessPathDesc": "使用完整进程路径匹配",
"ruleActionProcessPathRegexDesc": "使用进程路径正则表达式匹配",
"ruleActionProcessNameDesc": "使用进程匹配,在Android平台可以匹配包名",
"ruleActionProcessNameRegexDesc": "使用进程名称正则表达式匹配,在Android平台可以匹配包名",
"ruleActionUidDesc": "匹配 Linux USER ID",
"ruleActionNetworkDesc": "匹配TCP或者UDP",
"ruleActionDscpDesc": "匹配DSCP标记 (仅限 tproxy udp 入站)",
"ruleActionRuleSetDesc": "引用规则集合,需配置rule-providers",
"ruleActionAndDesc": "逻辑规则 AND",
"ruleActionOrDesc": "逻辑规则 OR",
"ruleActionNotDesc": "逻辑规则 NOT",
"ruleActionSubRuleDesc": "匹配至子规则,需要注意括号的使用",
"ruleActionMatchDesc": "匹配所有请求,无需条件",
"subRuleEmpty": "子规则为空",
"proxyProvidersNotEmpty": "代理集不能为空",
"contentNotEmpty": "内容不能为空",
"subRuleNotEmpty": "子规则不能为空",
"splitStrategyNotEmpty": "分流策略不能为空",
"proxyProvidersEmpty": "代理集为空",
"timeout": "超时",
"invalidSubRule": "{subRule} 是一个无效的SUB_RULE",
"invalidPolicy": "{target} 是一个无效的策略",
"invalidProxyProvider": "{providerName} 是一个无效的代理集",
"invalidProxy": "{proxyName} 是一个无效的代理",
"proxyGroupDetectedAbnormal": "检测到当前策略组异常",
"proxyProviderDetectedAbnormal": "检测到选中的代理集存在异常",
"proxyDetectedAbnormal": "检测到选中的代理存在异常",
"createProfile": "创建配置",
"locationPermissionRequired": "需要位置权限",
"locationPermissionGuide": "1. 打开 系统设置 > 隐私与安全性\n2. 选择 定位服务\n3. 在右侧列表中找到并勾选 {appName}\n\n完成设置后,返回应用即可正常使用。感谢您的配合。",
"prerequisites": "前置条件",
"ignoreBatteryOptimization": "忽略电池优化",
"batteryOptimizationDesc": "为保证后台运行,请关闭本应用的电池优化。点击前往设置。",
"batteryOptimizationStatusTip": "受系统影响,不代表一定准确",
"locationPermission": "位置权限",
"locationPermissionDesc": "根据系统要求,获取Wi-Fi名称需要您授予位置权限。",
"excludeSsids": "排除SSIDs",
"excludeSsidsDesc": "连接到被排除SSID的WIFI时,将会自动切换应用运行状态",
"ssidsEmpty": "SSIDs为空",
"onDemand": "按需运行",
"onDemandDesc": "配置程序特定场景运行状态",
"locationPermissionDeniedMessage": "位置权限已被拒绝,无法获取当前 Wi-Fi 名称。请前往系统设置手动开启位置权限。",
"addSsid": "添加SSID",
"editSsid": "编辑SSID",
"authorized": "已授权",
"tapToAuthorize": "点击授权",
"suspended": "挂起中..."
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.0 KiB

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 10 KiB

+10
View File
@@ -2,6 +2,8 @@ package main
import (
"encoding/json"
"fmt"
"runtime"
"unsafe"
)
@@ -37,6 +39,14 @@ func (result ActionResult) error(data interface{}) {
}
func handleAction(action *Action, result ActionResult) {
defer func() {
if r := recover(); r != nil {
buf := make([]byte, 4096)
n := runtime.Stack(buf, false)
logError("panic in handleAction(%s): %v\n%s", action.Method, r, buf[:n])
result.error(fmt.Sprintf("internal panic: %v", r))
}
}()
switch action.Method {
case initClashMethod:
paramsString := action.Data.(string)
+10 -1
View File
@@ -5,6 +5,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/adapter/outboundgroup"
@@ -35,6 +36,7 @@ var (
isRunning = false
runLock sync.Mutex
mBatch, _ = batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](50))
debugError = false
)
func getExternalProvidersRaw() map[string]cp.Provider {
@@ -138,7 +140,7 @@ func stopListeners() {
}
func patchSelectGroup(mapping map[string]string) {
for name, proxy := range tunnel.ProxiesWithProviders() {
for name, proxy := range tunnel.AllProxies() {
outbound, ok := proxy.(*adapter.Proxy)
if !ok {
continue
@@ -257,3 +259,10 @@ func UnmarshalJson(data []byte, v any) error {
err := decoder.Decode(v)
return err
}
func logError(format string, args ...interface{}) {
log.Errorln(format, args...)
if debugError {
fmt.Fprintf(os.Stderr, "[ERROR] "+format+"\n", args...)
}
}
+1 -1
View File
@@ -87,7 +87,7 @@ const (
asyncTestDelayMethod Method = "asyncTestDelay"
getConnectionsMethod Method = "getConnections"
closeConnectionsMethod Method = "closeConnections"
resetConnectionsMethod Method = "resetConnectionsMethod"
resetConnectionsMethod Method = "resetConnections"
closeConnectionMethod Method = "closeConnection"
getExternalProvidersMethod Method = "getExternalProviders"
getExternalProviderMethod Method = "getExternalProvider"
+13
View File
@@ -0,0 +1,13 @@
//go:build windows && !cgo
package main
import (
"io"
"github.com/Microsoft/go-winio"
)
func dial(path string) (io.ReadWriteCloser, error) {
return winio.DialPipe(path, nil)
}
+18
View File
@@ -0,0 +1,18 @@
//go:build !cgo && !windows
package main
import (
"fmt"
"io"
"net"
"strconv"
)
func dial(arg string) (io.ReadWriteCloser, error) {
_, err := strconv.Atoi(arg)
if err != nil {
return net.Dial("unix", arg)
}
return net.Dial("tcp", fmt.Sprintf("127.0.0.1:%s", arg))
}
+48 -23
View File
@@ -1,48 +1,56 @@
module core
go 1.20
go 1.21
replace github.com/metacubex/mihomo => ./Clash.Meta
require (
github.com/Microsoft/go-winio v0.6.2
github.com/metacubex/mihomo v0.0.0-00010101000000-000000000000
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e
golang.org/x/sync v0.11.0
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/RyuaNerin/go-krypto v1.3.0 // indirect
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect
github.com/ajg/form v1.5.1 // indirect
github.com/akutz/memconn v0.1.0 // indirect
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa // indirect
github.com/andybalholm/brotli v1.0.6 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/coder/websocket v1.8.12 // indirect
github.com/coreos/go-iptables v0.8.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/enfein/mieru/v3 v3.26.2 // indirect
github.com/dlclark/regexp2 v1.12.0 // indirect
github.com/dunglas/httpsfv v1.0.2 // indirect
github.com/enfein/mieru/v3 v3.31.0 // indirect
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/gaukas/godicttls v0.0.4 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.4.0 // indirect
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 // indirect
github.com/gofrs/uuid/v5 v5.4.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect
github.com/josharian/native v1.1.0 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.6 // indirect
github.com/klauspost/reedsolomon v1.12.3 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect
github.com/mdlayher/socket v0.5.1 // indirect
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d // indirect
github.com/metacubex/ascon v0.1.0 // indirect
github.com/metacubex/bart v0.26.0 // indirect
@@ -50,60 +58,77 @@ require (
github.com/metacubex/blake3 v0.1.0 // indirect
github.com/metacubex/chacha v0.1.5 // indirect
github.com/metacubex/chi v0.1.0 // indirect
github.com/metacubex/cpu v0.1.0 // indirect
github.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a // indirect
github.com/metacubex/cpu v0.1.1 // indirect
github.com/metacubex/edwards25519 v1.2.0 // indirect
github.com/metacubex/fswatch v0.1.1 // indirect
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect
github.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8 // indirect
github.com/metacubex/hkdf v0.1.0 // indirect
github.com/metacubex/hpke v0.1.0 // indirect
github.com/metacubex/http v0.1.0 // indirect
github.com/metacubex/http v0.1.6 // indirect
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c // indirect
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 // indirect
github.com/metacubex/mhurl v0.1.0 // indirect
github.com/metacubex/mlkem v0.1.0 // indirect
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 // indirect
github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb // indirect
github.com/metacubex/qpack v0.6.0 // indirect
github.com/metacubex/quic-go v0.59.1-0.20260112033758-aa29579f2001 // indirect
github.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 // indirect
github.com/metacubex/randv2 v0.2.0 // indirect
github.com/metacubex/restls-client-go v0.1.7 // indirect
github.com/metacubex/sing v0.5.6 // indirect
github.com/metacubex/sing-mux v0.3.4 // indirect
github.com/metacubex/sing-quic v0.0.0-20260112044712-65d17608159e // indirect
github.com/metacubex/sing v0.5.7 // indirect
github.com/metacubex/sing-mux v0.3.9 // indirect
github.com/metacubex/sing-quic v0.0.0-20260512151354-8475655be853 // indirect
github.com/metacubex/sing-shadowsocks v0.2.12 // indirect
github.com/metacubex/sing-shadowsocks2 v0.2.7 // indirect
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect
github.com/metacubex/sing-tun v0.4.11 // indirect
github.com/metacubex/sing-vmess v0.2.4 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f // indirect
github.com/metacubex/sing-tun v0.4.18 // indirect
github.com/metacubex/sing-vmess v0.2.5 // indirect
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 // indirect
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 // indirect
github.com/metacubex/ssh v0.1.0 // indirect
github.com/metacubex/tailscale v0.0.0-20260516120020-a21c2c99dcbe // indirect
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04 // indirect
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 // indirect
github.com/metacubex/tls v0.1.1 // indirect
github.com/metacubex/tls v0.1.5 // indirect
github.com/metacubex/utls v1.8.4 // indirect
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/mitchellh/go-ps v1.0.0 // indirect
github.com/mroth/weightedrand/v2 v2.1.0 // indirect
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect
github.com/openacid/low v0.1.21 // indirect
github.com/oschwald/maxminddb-golang v1.12.0 // indirect
github.com/pierrec/lz4/v4 v4.1.14 // indirect
github.com/pires/go-proxyproto v0.8.0 // indirect
github.com/safchain/ethtool v0.3.0 // indirect
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect
github.com/samber/lo v1.52.0 // indirect
github.com/samber/lo v1.53.0 // indirect
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sirupsen/logrus v1.9.4 // indirect
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d // indirect
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 // indirect
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a // indirect
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc // indirect
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect
github.com/vishvananda/netns v0.0.4 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 // indirect
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 // indirect
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect
golang.org/x/crypto v0.33.0 // indirect
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
golang.org/x/mod v0.20.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.10.0 // indirect
golang.org/x/tools v0.24.0 // indirect
+105 -44
View File
@@ -1,41 +1,55 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=
github.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=
github.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss=
github.com/RyuaNerin/testingutil v0.1.0/go.mod h1:yTqj6Ta/ycHMPJHRyO12Mz3VrvTloWOsy23WOZH19AA=
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=
github.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/akutz/memconn v0.1.0 h1:NawI0TORU4hcOMsMr11g7vwlCdkYeLKXBcxWu2W/P8A=
github.com/akutz/memconn v0.1.0/go.mod h1:Jo8rI7m0NieZyLI5e2CDlRdRqRRB4S7Xp77ukDjH+Fw=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=
github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/cilium/ebpf v0.12.3 h1:8ht6F9MquybnY97at+VDZb3eQQr8ev79RueWeVaEcG4=
github.com/cilium/ebpf v0.12.3/go.mod h1:TctK1ivibvI3znr66ljgi4hqOT8EYQjz1KWBfb1UVgM=
github.com/coder/websocket v1.8.12 h1:5bUXkEPPIbewrnkU8LTCLVaxi4N4J8ahufH2vlo4NAo=
github.com/coder/websocket v1.8.12/go.mod h1:LNVeNrXQZfe5qhS9ALED3uA+l5pPqvwXg3CKoDBB2gs=
github.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=
github.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/enfein/mieru/v3 v3.26.2 h1:U/2XJc+3vrJD9r815FoFdwToQFEcqSOzzzWIPPhjfEU=
github.com/enfein/mieru/v3 v3.26.2/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=
github.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0=
github.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=
github.com/enfein/mieru/v3 v3.31.0 h1:Fl2ocRCRXJzMygzdRjBHgqI996ZuIDHUmyQyovSf9sA=
github.com/enfein/mieru/v3 v3.31.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=
github.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=
github.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=
github.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c/go.mod h1:ETASDWf/FmEb6Ysrtd1QhjNedUU/ZQxBCRLh60bQ/UI=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=
github.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=
github.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
github.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=
github.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=
github.com/github/fakeca v0.1.0 h1:Km/MVOFvclqxPM9dZBC4+QE564nU4gz4iZ0D9pMw28I=
github.com/github/fakeca v0.1.0/go.mod h1:+bormgoGMMuamOscx7N91aOuUST7wdaJ2rNjeohylyo=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@@ -44,8 +58,12 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=
github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466 h1:sQspH8M4niEijh3PFscJRLDnkL547IeP7kpPe3uUhEg=
github.com/godbus/dbus/v5 v5.1.1-0.20230522191255-76236955d466/go.mod h1:ZiQxhyQ+bbbfxUKVvjfO498oPYvtYhZzycal3G/NHmU=
github.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=
github.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
@@ -56,24 +74,26 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=
github.com/google/tink/go v1.6.1/go.mod h1:IGW53kTgag+st5yPhKKwJ6u2l+SSp5/v9XF7spovjlY=
github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc=
github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=
github.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I=
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=
github.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d h1:vAJ0ZT4aO803F1uw2roIA9yH7Sxzox34tVVyye1bz6c=
github.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=
github.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=
@@ -88,8 +108,12 @@ github.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2
github.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=
github.com/metacubex/chi v0.1.0 h1:rjNDyDj50nRpicG43CNkIw4ssiCbmDL8d7wJXKlUCsg=
github.com/metacubex/chi v0.1.0/go.mod h1:zM5u5oMQt8b2DjvDHvzadKrP6B2ztmasL1YHRMbVV+g=
github.com/metacubex/cpu v0.1.0 h1:8PeTdV9j6UKbN1K5Jvtbi/Jock7dknvzyYuLb8Conmk=
github.com/metacubex/cpu v0.1.0/go.mod h1:09VEt4dSRLR+bOA8l4w4NDuzGZ8n5dkMv7e8axgEeTU=
github.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a h1:Ph5UfTWDsGruZ+v95Df1ycTflQFmpZBFg2LUvj2kx/M=
github.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a/go.mod h1:xYC8Ik7/rN6no+vTRuWMEziGwm3brA0wNM/zZP9qhOQ=
github.com/metacubex/cpu v0.1.1 h1:rRV5HGmeuGzjiKI3hYbL0dCd0qGwM7VUtk4ICXD06mI=
github.com/metacubex/cpu v0.1.1/go.mod h1:09VEt4dSRLR+bOA8l4w4NDuzGZ8n5dkMv7e8axgEeTU=
github.com/metacubex/edwards25519 v1.2.0 h1:pIQZLBsjQgg3Nl/c86YYFEUAbL5qQRnPq4LrgIw0KK4=
github.com/metacubex/edwards25519 v1.2.0/go.mod h1:NCQF3J/Ki7382FJuokwsywEIIEI/gro/3smyXgQJsx0=
github.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=
github.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=
github.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=
@@ -100,46 +124,56 @@ github.com/metacubex/hkdf v0.1.0 h1:fPA6VzXK8cU1foc/TOmGCDmSa7pZbxlnqhl3RNsthaA=
github.com/metacubex/hkdf v0.1.0/go.mod h1:3seEfds3smgTAXqUGn+tgEJH3uXdsUjOiduG/2EtvZ4=
github.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ=
github.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U=
github.com/metacubex/http v0.1.0 h1:Jcy0I9zKjYijSUaksZU34XEe2xNdoFkgUTB7z7K5q0o=
github.com/metacubex/http v0.1.0/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
github.com/metacubex/http v0.1.6 h1:xvXuvXMCMxCWMF5nEJF4yiKvXL+p2atWMzs37e80m1I=
github.com/metacubex/http v0.1.6/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c h1:KGhBHDe6FveU0ury+9RyX329nclM1CHODa0Fi+uOAYM=
github.com/metacubex/jsonv2 v0.0.0-20260513175203-1c6abea7534c/go.mod h1:F4sVXat6QjPXkNsKRDyyG3BhSkxPFFnRPEIwmmyCgbg=
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=
github.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=
github.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=
github.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=
github.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=
github.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793 h1:1Qpuy+sU3DmyX9HwI+CrBT/oLNJngvBorR2RbajJcqo=
github.com/metacubex/nftables v0.0.0-20250503052935-30a69ab87793/go.mod h1:RjRNb4G52yAgfR+Oe/kp9G4PJJ97Fnj89eY1BFO3YyA=
github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb h1:wk6mHYPURSUvWcUv72gNP79oiylFsscBSDPJ6ieV6Iw=
github.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb/go.mod h1:73ZrCfhdkW4F2E2GAlta3km/S2RHhFNogCMtWZV2anQ=
github.com/metacubex/qpack v0.6.0 h1:YqClGIMOpiRYLjV1qOs483Od08MdPgRnHjt90FuaAKw=
github.com/metacubex/qpack v0.6.0/go.mod h1:lKGSi7Xk94IMvHGOmxS9eIei3bvIqpOAImEBsaOwTkA=
github.com/metacubex/quic-go v0.59.1-0.20260112033758-aa29579f2001 h1:RlT3bFCIDM/NR9GWaDbFCrweOwpHRfgaT9c0zuRlPhY=
github.com/metacubex/quic-go v0.59.1-0.20260112033758-aa29579f2001/go.mod h1:oNzMrmylS897M3zSMuapIdwSwfq6F2qW01Z3NhVRJhk=
github.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 h1:HlGLmLsWJMLSu0CMI9z/BmEnithB4oXM5Rom6/0Qxtg=
github.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306/go.mod h1:oNzMrmylS897M3zSMuapIdwSwfq6F2qW01Z3NhVRJhk=
github.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=
github.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=
github.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=
github.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=
github.com/metacubex/sing v0.5.6 h1:mEPDCadsCj3DB8gn+t/EtposlYuALEkExa/LUguw6/c=
github.com/metacubex/sing v0.5.6/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.4 h1:tf4r27CIkzaxq9kBlAXQkgMXq2HPp5Mta60Kb4RCZF0=
github.com/metacubex/sing-mux v0.3.4/go.mod h1:SEJfAuykNj/ozbPqngEYqyggwSr81+L7Nu09NRD5mh4=
github.com/metacubex/sing-quic v0.0.0-20260112044712-65d17608159e h1:MLxp42z9Jd6LtY2suyawnl24oNzIsFxWc15bNeDIGxA=
github.com/metacubex/sing-quic v0.0.0-20260112044712-65d17608159e/go.mod h1:+lgKTd52xAarGtqugALISShyw4KxnoEpYe2u0zJh26w=
github.com/metacubex/sing v0.5.7 h1:8OC+fhKFSv/l9ehEhJRaZZAOuthfZo68SteBVLe8QqM=
github.com/metacubex/sing v0.5.7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=
github.com/metacubex/sing-mux v0.3.9 h1:/aoBD2+sK2qsXDlNDe3hkR0GZuFDtwIZhOeGUx9W0Yk=
github.com/metacubex/sing-mux v0.3.9/go.mod h1:8bT7ZKT3clRrJjYc/x5CRYibC1TX/bK73a3r3+2E+Fc=
github.com/metacubex/sing-quic v0.0.0-20260512151354-8475655be853 h1:nZ5WNU6kjj6kBu4+2eMySFkUVGCop64rZnLMm+HPh8w=
github.com/metacubex/sing-quic v0.0.0-20260512151354-8475655be853/go.mod h1:6ayFGfzzBE85csgQkM3gf4neFq6s0losHlPRSxY+nuk=
github.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=
github.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=
github.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A=
github.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=
github.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=
github.com/metacubex/sing-tun v0.4.11 h1:NG5zpvYPbBXf+9GSUmDaGCDwl3hZXV677tbRAw0QtCM=
github.com/metacubex/sing-tun v0.4.11/go.mod h1:L/TjQY5JEGy8nvsuYmy/XgMFMCPiF0+AWSFCYfS6r9w=
github.com/metacubex/sing-vmess v0.2.4 h1:Tx6AGgCiEf400E/xyDuYyafsel6sGbR8oF7RkAaus6I=
github.com/metacubex/sing-vmess v0.2.4/go.mod h1:21R5R1u90uUvBQF0owoooEu96/SAYYD56nDrwm6nFaM=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f h1:Sr/DYKYofKHKc4GF3qkRGNuj6XA6c0eqPgEDN+VAsYU=
github.com/metacubex/sing-wireguard v0.0.0-20250503063753-2dc62acc626f/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
github.com/metacubex/sing-tun v0.4.18 h1:WRzAosG0YkT3aZq5RJWtF+RdCgeJ8EpooS5ZM1lkXo0=
github.com/metacubex/sing-tun v0.4.18/go.mod h1:g4I/JNplDBhXLF+aQWgFbhNeJPSXQOWS9HvLeNvkgeA=
github.com/metacubex/sing-vmess v0.2.5 h1:m9Zt5I27lB9fmLMZfism9sH2LcnAfShZfwSkf6/KJoE=
github.com/metacubex/sing-vmess v0.2.5/go.mod h1:AwtlzUgf8COe9tRYAKqWZ+leDH7p5U98a0ZUpYehl8Q=
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 h1:IB03BvRQtvjWScyOK5jSQVJYY8osmZXHL+4VCEFMWcM=
github.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2BhiAPgbJygiWhesPlfGmF+9Vw6ARdk=
github.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=
github.com/metacubex/ssh v0.1.0 h1:iGfr99qk/eMHzUnQ/0bTxXT8+8SWqLSHBWDHoAhngzw=
github.com/metacubex/ssh v0.1.0/go.mod h1:NUtl0d+/f2cG9ECEpMM8iCVOpmggQlC13oLeDUONDlU=
github.com/metacubex/tailscale v0.0.0-20260516120020-a21c2c99dcbe h1:ZdAKshacNruZGuKTE8WMTuxyGgpv/LySLoE/EEmgF9c=
github.com/metacubex/tailscale v0.0.0-20260516120020-a21c2c99dcbe/go.mod h1:2G1V82OGXgxT7m7046GA80I9SlcvczljCK0C7NQ3c10=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04 h1:zk+mDDSBl5lv80WWtaFUbpj8XLb7AhjCUbn2pB37N0U=
github.com/metacubex/tailscale-wireguard-go v0.0.0-20260513233728-8bc7ee255d04/go.mod h1:pKUKBy7IcQ5r0i66gWENHgxKvBn8tlgAGx0DZMq8h5M=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=
github.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=
github.com/metacubex/tls v0.1.1 h1:BEcZrsPTTfNf4sKZ02EbZodv4UIj7fgHWa1Eqo12Bc0=
github.com/metacubex/tls v0.1.1/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/tls v0.1.5 h1:ECcB83dj+zadnhlKcLnUUf1Sq6+vU0f/zoyU0+9oPTc=
github.com/metacubex/tls v0.1.5/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=
github.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=
github.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=
github.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=
@@ -148,6 +182,8 @@ github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 h1:lhlqpYHopuTLx9x
github.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49/go.mod h1:MBeEa9IVBphH7vc3LNtW6ZujVXFizotPo3OEiHQ+TNU=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=
github.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=
github.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=
@@ -161,45 +197,64 @@ github.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq5
github.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=
github.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/safchain/ethtool v0.3.0 h1:gimQJpsI6sc1yIqP/y8GYgiXn/NjgvpM0RNoWLVVmP0=
github.com/safchain/ethtool v0.3.0/go.mod h1:SA9BwrgyAqNo7M+uaL6IYbxpm5wk3L7Mm6ocLW+CJUs=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=
github.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=
github.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=
github.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=
github.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d h1:JcGKBZAL7ePLwOhUdN8qGQZlP5GueEiIZwY7R62pejE=
github.com/tailscale/certstore v0.1.1-0.20260409135935-3638fb84b77d/go.mod h1:XrBNfAFN+pwoWuksbFS9Ccxnopa15zJGgXRFN90l3K4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55 h1:Gzfnfk2TWrk8Jj4P4c1a3CtQyMaTVCznlkLZI++hok4=
github.com/tailscale/go-winio v0.0.0-20231025203758-c4f33415bf55/go.mod h1:4k4QO+dQ3R5FofL+SanAUZe+/QfeK0+OIuwDIRu2vSg=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a h1:SJy1Pu0eH1C29XwJucQo73FrleVK6t4kYz4NVhp34Yw=
github.com/tailscale/hujson v0.0.0-20221223112325-20486734a56a/go.mod h1:DFSS3NAGHthKo1gTlmEcSBiZrRJXi28rLNd/1udP1c8=
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc h1:24heQPtnFR+yfntqhI3oAu9i27nEojcQ4NuBQOo5ZFA=
github.com/tailscale/peercred v0.0.0-20250107143737-35a0c7bd7edc/go.mod h1:f93CXfllFsO9ZQVq+Zocb1Gp4G5Fz0b0rXHLOzt/Djc=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=
github.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=
gitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=
gitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
go4.org/mem v0.0.0-20240501181205-ae6ca9944745 h1:Tl++JLUCe4sxGu8cTpDzRLd3tN7US4hOxG5YpKCzkek=
go4.org/mem v0.0.0-20240501181205-ae6ca9944745/go.mod h1:reUoABIJ9ikfM5sgtSF3Wushcza7+WeD01VB9Lirh3g=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=
go4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
@@ -216,7 +271,10 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -225,13 +283,14 @@ golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
@@ -248,3 +307,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
software.sslmate.com/src/go-pkcs12 v0.2.1 h1:tbT1jjaeFOF230tzOIRJ6U5S1jNqpsSyNjzDd58H3J8=
software.sslmate.com/src/go-pkcs12 v0.2.1/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
+8 -17
View File
@@ -102,18 +102,10 @@ func handleGetProxies() ProxiesData {
nameList := config.GetProxyNameList()
proxies := make(map[string]constant.Proxy)
for name, proxy := range tunnel.Proxies() {
proxies[name] = proxy
}
for _, p := range tunnel.Providers() {
for _, proxy := range p.Proxies() {
proxies[proxy.Name()] = proxy
}
}
proxies := tunnel.AllProxies()
hasGlobal := false
allNames := make([]string, 0, len(nameList)+1)
for _, name := range nameList {
@@ -156,7 +148,7 @@ func handleChangeProxy(data string, fn func(string string)) {
}
groupName := *params.GroupName
proxyName := *params.ProxyName
proxies := tunnel.ProxiesWithProviders()
proxies := tunnel.AllProxies()
group, ok := proxies[groupName]
if !ok {
fn("Not found group")
@@ -191,7 +183,7 @@ func handleGetTraffic(onlyStatisticsProxy bool) string {
}
data, err := json.Marshal(traffic)
if err != nil {
log.Errorln("Error: %s", err)
logError("Error: %s", err)
return ""
}
return string(data)
@@ -205,7 +197,7 @@ func handleGetTotalTraffic(onlyStatisticsProxy bool) string {
}
data, err := json.Marshal(traffic)
if err != nil {
log.Errorln("Error: %s", err)
logError("Error: %s", err)
return ""
}
return string(data)
@@ -233,7 +225,7 @@ func handleAsyncTestDelay(paramsString string, fn func(string)) {
ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout))
defer cancel()
proxies := tunnel.ProxiesWithProviders()
proxies := tunnel.AllProxies()
proxy := proxies[params.ProxyName]
delayData := &Delay{
@@ -253,7 +245,6 @@ func handleAsyncTestDelay(paramsString string, fn func(string)) {
testUrl = params.TestUrl
}
delayData.Url = testUrl
delay, err := proxy.URLTest(ctx, testUrl, expectedStatus)
if err != nil || delay == 0 {
delayData.Value = -1
@@ -275,7 +266,7 @@ func handleGetConnections() string {
snapshot := statistic.DefaultManager.Snapshot()
data, err := json.Marshal(snapshot)
if err != nil {
log.Errorln("Error: %s", err)
logError("Error: %s", err)
return ""
}
return string(data)
@@ -538,7 +529,7 @@ func handleSetupConfig(bytes []byte) string {
var params = defaultSetupParams()
err := UnmarshalJson(bytes, params)
if err != nil {
log.Errorln("unmarshalRawConfig error %v", err)
logError("unmarshalRawConfig error %v", err)
_ = applyConfig(defaultSetupParams())
return err.Error()
}
+45 -20
View File
@@ -3,18 +3,21 @@
package main
import (
"bufio"
"encoding/binary"
"encoding/json"
"fmt"
"net"
"strconv"
"io"
"sync"
)
var conn net.Conn
var (
conn io.ReadWriteCloser
connMu sync.Mutex
)
func (result ActionResult) send() {
data, err := result.Json()
if err != nil {
logError("ActionResult marshal error: method=%s id=%s err=%v", result.Method, result.Id, err)
return
}
send(data)
@@ -28,43 +31,65 @@ func sendMessage(message Message) {
result.send()
}
func writeFrame(w io.Writer, data []byte) error {
frame := make([]byte, 4+len(data))
binary.LittleEndian.PutUint32(frame, uint32(len(data)))
copy(frame[4:], data)
_, err := w.Write(frame)
return err
}
func readFrame(r io.Reader) ([]byte, error) {
lenBuf := make([]byte, 4)
if _, err := io.ReadFull(r, lenBuf); err != nil {
return nil, err
}
length := binary.LittleEndian.Uint32(lenBuf)
data := make([]byte, length)
if _, err := io.ReadFull(r, data); err != nil {
return nil, err
}
return data, nil
}
func send(data []byte) {
if conn == nil {
logError("send conn nil")
return
}
_, _ = conn.Write(append(data, []byte("\n")...))
connMu.Lock()
defer connMu.Unlock()
if err := writeFrame(conn, data); err != nil {
logError("server write error: %v", err)
}
}
func startServer(arg string) {
_, err := strconv.Atoi(arg)
if err != nil {
conn, err = net.Dial("unix", arg)
} else {
conn, err = net.Dial("tcp", fmt.Sprintf("127.0.0.1:%s", arg))
}
var err error
conn, err = dial(arg)
if err != nil {
panic(err.Error())
}
defer func(conn net.Conn) {
defer func(conn io.Closer) {
_ = conn.Close()
}(conn)
reader := bufio.NewReader(conn)
for {
data, err := reader.ReadString('\n')
data, err := readFrame(conn)
if err != nil {
if err != io.EOF {
logError("server read error: %v", err)
}
return
}
var action = &Action{}
err = json.Unmarshal([]byte(data), action)
err = json.Unmarshal(data, action)
if err != nil {
return
logError("server unmarshal error: %v (data: %q)", err, data)
continue
}
result := ActionResult{
+31 -9
View File
@@ -14,7 +14,6 @@ import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'controller.dart';
import 'pages/pages.dart';
class Application extends ConsumerStatefulWidget {
@@ -48,21 +47,44 @@ class ApplicationState extends ConsumerState<Application> {
void initState() {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
final currentContext = globalState.navigatorKey.currentContext;
if (currentContext != null) {
await appController.attach(currentContext, ref);
if (globalState.navigatorKey.currentContext != null) {
await globalState.attach();
} else {
exit(0);
}
_autoUpdateProfilesTask();
appController.initLink();
_initLink();
app?.initShortcuts();
});
}
void _initLink() {
linkManager.initAppLinksListen((url) async {
final res = await globalState.showMessage(
title: currentAppLocalizations.addProfile,
message: TextSpan(
children: [
TextSpan(text: currentAppLocalizations.doYouWantToPass),
TextSpan(
text: ' $url ',
style: TextStyle(
color: context.colorScheme.primary,
decoration: TextDecoration.underline,
decorationColor: context.colorScheme.primary,
),
),
TextSpan(text: currentAppLocalizations.createProfile),
],
),
);
if (res != true) return;
ref.read(profilesActionProvider.notifier).addProfileFormURL(url);
});
}
void _autoUpdateProfilesTask() {
_autoUpdateProfilesTaskTimer = Timer(const Duration(minutes: 20), () async {
await appController.autoUpdateProfiles();
await ref.read(profilesActionProvider.notifier).autoUpdateProfiles();
_autoUpdateProfilesTask();
});
}
@@ -84,10 +106,10 @@ class ApplicationState extends ConsumerState<Application> {
child: ConnectivityManager(
onConnectivityChanged: (results) async {
commonPrint.log('connectivityChanged ${results.toString()}');
appController.updateLocalIp();
ref.read(systemActionProvider.notifier).updateLocalIp();
final hasVpn = results.contains(ConnectivityResult.vpn);
if (_preHasVpn == hasVpn) {
appController.addCheckIp();
ref.read(checkIpNumProvider.notifier).add();
}
_preHasVpn = hasVpn;
},
@@ -167,7 +189,7 @@ class ApplicationState extends ConsumerState<Application> {
linkManager.destroy();
_autoUpdateProfilesTaskTimer?.cancel();
await coreController.destroy();
await appController.handleExit();
await ref.read(systemActionProvider.notifier).handleExit();
super.dispose();
}
}
+1 -1
View File
@@ -1,3 +1,3 @@
import 'package:fl_clash/l10n/l10n.dart';
final appLocalizations = AppLocalizations.current;
final currentAppLocalizations = AppLocalizations.current;
+43 -173
View File
@@ -1,185 +1,55 @@
import 'dart:io';
import 'dart:async';
import 'dart:ui';
import 'package:dio/dio.dart';
import 'package:fl_clash/common/common.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
class LocalImageCacheManager extends CacheManager {
static const key = 'ImageCaches';
static final LocalImageCacheManager _instance = LocalImageCacheManager._();
factory LocalImageCacheManager() {
return _instance;
}
LocalImageCacheManager._()
: super(Config(key, fileService: _LocalImageCacheFileService()));
}
class _LocalImageCacheFileService extends FileService {
_LocalImageCacheFileService();
@override
Future<FileServiceResponse> get(
extension CacheManagerExt on CacheManager {
Stream<FileInfo> getFileStreamV2(
String url, {
String? key,
Map<String, String>? headers,
}) async {
final response = await request.dio.get<ResponseBody>(
url,
options: Options(headers: headers, responseType: ResponseType.stream),
);
return _LocalImageResponse(response);
}
}
class _LocalImageResponse implements FileServiceResponse {
_LocalImageResponse(this._response);
final DateTime _receivedTime = DateTime.now();
final Response<ResponseBody> _response;
String? _header(String name) {
return _response.headers.value(name);
VoidCallback? onRemoteNewLoaded,
}) {
key ??= url;
final streamController = StreamController<FileInfo>();
_pushFileToStream(streamController, url, key, headers, onRemoteNewLoaded);
return streamController.stream;
}
@override
int get statusCode => _response.statusCode ?? 0;
@override
Stream<List<int>> get content =>
_response.data!.stream.transform(uint8ListToListIntConverter);
@override
int? get contentLength => _response.data?.contentLength;
@override
DateTime get validTill {
var ageDuration = const Duration(days: 7);
final controlHeader = _header(HttpHeaders.cacheControlHeader);
if (controlHeader != null) {
final controlSettings = controlHeader.split(',');
for (final setting in controlSettings) {
final sanitizedSetting = setting.trim().toLowerCase();
if (sanitizedSetting == 'no-cache') {
ageDuration = Duration.zero;
}
if (sanitizedSetting.startsWith('max-age=')) {
final validSeconds =
int.tryParse(sanitizedSetting.split('=')[1]) ?? 0;
if (validSeconds > 0) {
ageDuration = Duration(seconds: validSeconds);
}
Future<void> _pushFileToStream(
StreamController<dynamic> streamController,
String url,
String? key,
Map<String, String>? headers,
VoidCallback? onRemoteNewLoaded,
) async {
key ??= url;
FileInfo? cacheFile;
try {
cacheFile = await getFileFromCache(key);
if (cacheFile != null) {
streamController.add(cacheFile);
}
} on Object catch (e) {
cacheLogger.log(
'CacheManager: Failed to load cached file for $url with error:\n$e',
CacheManagerLogLevel.debug,
);
}
if (cacheFile == null || cacheFile.validTill.isBefore(DateTime.now())) {
try {
final res = (await downloadFile(url, key: key, authHeaders: headers));
streamController.add(res);
if (cacheFile == null) {
onRemoteNewLoaded?.call();
}
} on Object catch (e) {
cacheLogger.log(
'CacheManager: Failed to download file from $url with error:\n$e',
CacheManagerLogLevel.debug,
);
}
}
if (ageDuration > const Duration(days: 7)) {
return _receivedTime.add(ageDuration);
}
return _receivedTime.add(const Duration(days: 7));
}
@override
String? get eTag => _header(HttpHeaders.etagHeader);
@override
String get fileExtension {
var fileExtension = '';
final contentTypeHeader = _header(HttpHeaders.contentTypeHeader);
if (contentTypeHeader != null) {
final contentType = ContentType.parse(contentTypeHeader);
fileExtension = contentType.fileExtension;
}
return fileExtension;
streamController.close();
}
}
extension ContentTypeConverter on ContentType {
String get fileExtension => mimeTypes[mimeType] ?? '.$subType';
}
const mimeTypes = {
'application/vnd.android.package-archive': '.apk',
'application/epub+zip': '.epub',
'application/gzip': '.gz',
'application/java-archive': '.jar',
'application/json': '.json',
'application/ld+json': '.jsonld',
'application/msword': '.doc',
'application/octet-stream': '.bin',
'application/ogg': '.ogx',
'application/pdf': '.pdf',
'application/php': '.php',
'application/rtf': '.rtf',
'application/vnd.amazon.ebook': '.azw',
'application/vnd.apple.installer+xml': '.mpkg',
'application/vnd.mozilla.xul+xml': '.xul',
'application/vnd.ms-excel': '.xls',
'application/vnd.ms-fontobject': '.eot',
'application/vnd.ms-powerpoint': '.ppt',
'application/vnd.oasis.opendocument.presentation': '.odp',
'application/vnd.oasis.opendocument.spreadsheet': '.ods',
'application/vnd.oasis.opendocument.text': '.odt',
'application/vnd.openxmlformats-officedocument.presentationml.presentation':
'.pptx',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': '.xlsx',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document':
'.docx',
'application/vnd.rar': '.rar',
'application/vnd.visio': '.vsd',
'application/x-7z-compressed': '.7z',
'application/x-abiword': '.abw',
'application/x-bzip': '.bz',
'application/x-bzip2': '.bz2',
'application/x-csh': '.csh',
'application/x-freearc': '.arc',
'application/x-sh': '.sh',
'application/x-shockwave-flash': '.swf',
'application/x-tar': '.tar',
'application/xhtml+xml': '.xhtml',
'application/xml': '.xml',
'application/zip': '.zip',
'audio/3gpp': '.3gp',
'audio/3gpp2': '.3g2',
'audio/aac': '.aac',
'audio/x-aac': '.aac',
'audio/midi': '.midi',
'audio/x-midi': '.midi',
'audio/x-m4a': '.m4a',
'audio/m4a': '.m4a',
'audio/mpeg': '.mp3',
'audio/ogg': '.oga',
'audio/opus': '.opus',
'audio/wav': '.wav',
'audio/x-wav': '.wav',
'audio/webm': '.weba',
'font/otf': '.otf',
'font/ttf': '.ttf',
'font/woff': '.woff',
'font/woff2': '.woff2',
'image/bmp': '.bmp',
'image/gif': '.gif',
'image/jpeg': '.jpg',
'image/png': '.png',
'image/svg+xml': '.svg',
'image/tiff': '.tiff',
'image/vnd.microsoft.icon': '.ico',
'image/webp': '.webp',
'text/calendar': '.ics',
'text/css': '.css',
'text/csv': '.csv',
'text/html': '.html',
'text/javascript': '.js',
'text/plain': '.txt',
'text/xml': '.xml',
'video/3gpp': '.3gp',
'video/3gpp2': '.3g2',
'video/mp2t': '.ts',
'video/mpeg': '.mpeg',
'video/ogg': '.ogv',
'video/webm': '.webm',
'video/x-msvideo': '.avi',
'video/quicktime': '.mov',
};
+3 -11
View File
@@ -93,10 +93,7 @@ extension ColorExtension on Color {
.toColor();
}
Color blendDarken(
BuildContext context, {
double factor = 0.1,
}) {
Color blendDarken(BuildContext context, {double factor = 0.1}) {
final brightness = Theme.of(context).brightness;
return Color.lerp(
this,
@@ -105,10 +102,7 @@ extension ColorExtension on Color {
)!;
}
Color blendLighten(
BuildContext context, {
double factor = 0.1,
}) {
Color blendLighten(BuildContext context, {double factor = 0.1}) {
final brightness = Theme.of(context).brightness;
return Color.lerp(
this,
@@ -122,9 +116,7 @@ extension ColorSchemeExtension on ColorScheme {
ColorScheme toPureBlack(bool isPrueBlack) => isPrueBlack
? copyWith(
surface: Colors.black,
surfaceContainer: surfaceContainer.darken(
5,
),
surfaceContainer: surfaceContainer.darken(5),
)
: this;
}
+3 -2
View File
@@ -1,5 +1,4 @@
export 'app_localizations.dart';
export 'cache.dart';
export 'color.dart';
export 'compute.dart';
export 'constant.dart';
@@ -10,11 +9,11 @@ export 'file.dart';
export 'fixed.dart';
export 'function.dart';
export 'future.dart';
export 'hive.dart';
export 'http.dart';
export 'icons.dart';
export 'indexing.dart';
export 'iterable.dart';
export 'javascript.dart';
export 'keyboard.dart';
export 'launch.dart';
export 'link.dart';
@@ -28,7 +27,9 @@ export 'network.dart';
export 'num.dart';
export 'package.dart';
export 'path.dart';
export 'permission.dart';
export 'picker.dart';
export 'pipe.dart';
export 'preferences.dart';
export 'print.dart';
export 'protocol.dart';
+7 -4
View File
@@ -16,6 +16,7 @@ const browserUa =
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36';
const packageName = 'com.follow.clash';
final unixSocketPath = '/tmp/FlClashSocket_${Random().nextInt(10000)}.sock';
final windowsPipeName = '\\\\.\\pipe\\FlClashCore_${Random().nextInt(10000)}';
const helperPort = 47890;
const maxTextScale = 1.4;
const minTextScale = 0.8;
@@ -29,8 +30,9 @@ final listHeaderPadding = EdgeInsets.only(
top: 24.mAp,
bottom: 8.mAp,
);
const sheetAppBarHeight = 68.0;
const watchExecution = true;
const watchExecution = false;
final defaultTextScaleFactor =
WidgetsBinding.instance.platformDispatcher.textScaleFactor;
@@ -62,7 +64,7 @@ const defaultTestUrl = 'https://www.gstatic.com/generate_204';
final commonFilter = ImageFilter.blur(
sigmaX: 5,
sigmaY: 5,
tileMode: TileMode.mirror,
tileMode: TileMode.clamp,
);
const listEquality = ListEquality();
@@ -77,6 +79,7 @@ const scriptListEquality = ListEquality<Script>();
const externalProviderListEquality = ListEquality<ExternalProvider>();
const packageListEquality = ListEquality<Package>();
const profileListEquality = ListEquality<Profile>();
const proxyGroupsEquality = ListEquality<ProxyGroup>();
const hotKeyActionListEquality = ListEquality<HotKeyAction>();
const stringAndStringMapEquality = MapEquality<String, String>();
const stringAndStringMapEntryListEquality =
@@ -108,9 +111,9 @@ double getWidgetHeight(num lines) {
const maxLength = 1000;
final mainIsolate = 'FlClashMainIsolate';
const mainIsolate = 'FlClashMainIsolate';
final serviceIsolate = 'FlClashServiceIsolate';
const serviceIsolate = 'FlClashServiceIsolate';
const defaultPrimaryColors = [
0xFF795548,
+22 -1
View File
@@ -1,7 +1,10 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/l10n/l10n.dart';
import 'package:fl_clash/manager/manager.dart';
import 'package:fl_clash/models/state.dart';
import 'package:fl_clash/widgets/inherited.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:fl_clash/widgets/sheet.dart';
import 'package:flutter/material.dart';
extension BuildContextExtension on BuildContext {
@@ -9,6 +12,24 @@ extension BuildContextExtension on BuildContext {
return findAncestorStateOfType<CommonScaffoldState>();
}
void safeNestedPop<T extends Object?>([T? result]) {
final nestedPop = SheetProvider.of(this)?.nestedNavigatorPop;
if (nestedPop != null) {
return nestedPop(result);
} else {
return Navigator.of(this).pop(result);
}
}
double get sheetTopPadding {
final sheetType = SheetProvider.of(this)!.type;
if (sheetType == SheetType.bottomSheet) {
return sheetAppBarHeight;
} else {
return 10;
}
}
void showNotifier(String text, {MessageActionState? actionState}) {
return findAncestorStateOfType<StatusManagerState>()?.message(
text,
@@ -52,7 +73,7 @@ extension BuildContextExtension on BuildContext {
T? findLastStateOfType<T extends State>() {
T? state;
visitor(Element element) {
void visitor(Element element) {
if (!element.mounted) {
return;
}
+5 -2
View File
@@ -1,4 +1,6 @@
import 'package:fl_clash/common/app_localizations.dart';
import 'package:flutter/cupertino.dart';
import 'context.dart';
extension DateTimeExtension on DateTime {
bool get isBeforeNow {
@@ -12,7 +14,8 @@ extension DateTimeExtension on DateTime {
return true;
}
String get lastUpdateTimeDesc {
String getLastUpdateTimeDesc(BuildContext context) {
final appLocalizations = context.appLocalizations;
final currentDateTime = DateTime.now();
final difference = currentDateTime.difference(this);
final days = difference.inDays;
+1 -3
View File
@@ -6,7 +6,6 @@ import 'package:webdav_client/webdav_client.dart';
class DAVClient {
late Client client;
Completer<bool> pingCompleter = Completer();
late String fileName;
DAVClient(DAVProps dav) {
@@ -16,10 +15,9 @@ class DAVClient {
client.setConnectTimeout(8000);
client.setSendTimeout(60000);
client.setReceiveTimeout(60000);
pingCompleter.complete(_ping());
}
Future<bool> _ping() async {
Future<bool> ping() async {
try {
await client.ping();
return true;
+2 -2
View File
@@ -17,14 +17,14 @@ extension FileExt on File {
if (!await exists()) {
await create(recursive: true);
}
return await writeAsString(str);
return writeAsString(str);
}
Future<File> safeWriteAsBytes(List<int> bytes) async {
if (!await exists()) {
await create(recursive: true);
}
return await writeAsBytes(bytes);
return writeAsBytes(bytes);
}
}
+1 -1
View File
@@ -27,7 +27,7 @@ class FixedList<T> {
FixedList<T> copyWith() {
return FixedList(
maxLength,
list: _list,
list: List.of(_list),
);
}
}
+6 -6
View File
@@ -1,13 +1,12 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
class Debouncer {
final Map<FunctionTag, Timer?> _operations = {};
final Map<dynamic, Timer?> _operations = {};
void call(
FunctionTag tag,
dynamic tag,
Function func, {
List<dynamic>? args,
Duration? duration,
@@ -30,10 +29,10 @@ class Debouncer {
}
class Throttler {
final Map<FunctionTag, Timer?> _operations = {};
final Map<dynamic, Timer?> _operations = {};
bool call(
FunctionTag tag,
dynamic tag,
Function func, {
List<dynamic>? args,
Duration duration = const Duration(milliseconds: 600),
@@ -74,10 +73,11 @@ Future<T> retry<T>({
int attempts = 0;
while (attempts < maxAttempts) {
final res = await task();
attempts++;
if (!retryIf(res) || attempts >= maxAttempts) {
return res;
}
attempts++;
await Future.delayed(delay);
}
throw 'retry error';
}
View File
+12 -6
View File
@@ -1,18 +1,24 @@
import 'dart:io';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class FlClashHttpOverrides extends HttpOverrides {
static String handleFindProxy(Uri url) {
if ([localhost].contains(url.host)) {
return 'DIRECT';
}
final port = appController.config.patchClashConfig.mixedPort;
final isStart = appController.isStart;
commonPrint.log('find $url proxy:$isStart');
if (!isStart) return 'DIRECT';
return 'PROXY localhost:$port';
final ref = globalState.container;
final isStart = ref.read(isStartProvider);
final suspend = ref.read(suspendProvider);
commonPrint.log('find $url proxy: $isStart');
if (!isStart || suspend) return 'DIRECT';
final mixedPort = ref.read(
patchClashConfigProvider.select((state) => state.mixedPort),
);
return 'PROXY localhost:$mixedPort';
}
@override
+32 -28
View File
@@ -35,13 +35,13 @@ class Indexing {
String? _incrementInteger(String x) {
_validateInteger(x);
String head = x[0];
List<String> digs = x.substring(1).split('');
final String head = x[0];
final List<String> digs = x.substring(1).split('');
bool carry = true;
for (int i = digs.length - 1; carry && i >= 0; i--) {
int d = digits.indexOf(digs[i]) + 1;
final int d = digits.indexOf(digs[i]) + 1;
if (d == digits.length) {
digs[i] = '0';
} else {
@@ -57,7 +57,7 @@ class Indexing {
if (head == 'z') {
return null;
}
String h = String.fromCharCode(head.codeUnitAt(0) + 1);
final String h = String.fromCharCode(head.codeUnitAt(0) + 1);
if (h.compareTo('a') > 0) {
digs.add('0');
} else {
@@ -71,13 +71,13 @@ class Indexing {
String? _decrementInteger(String x) {
_validateInteger(x);
String head = x[0];
List<String> digs = x.substring(1).split('');
final String head = x[0];
final List<String> digs = x.substring(1).split('');
bool borrow = true;
for (int i = digs.length - 1; borrow && i >= 0; i--) {
int d = digits.indexOf(digs[i]) - 1;
final int d = digits.indexOf(digs[i]) - 1;
if (d == -1) {
digs[i] = digits[digits.length - 1];
} else {
@@ -93,7 +93,7 @@ class Indexing {
if (head == 'A') {
return null;
}
String h = String.fromCharCode(head.codeUnitAt(0) - 1);
final String h = String.fromCharCode(head.codeUnitAt(0) - 1);
if (h.compareTo('Z') < 0) {
digs.add(digits[digits.length - 1]);
} else {
@@ -132,13 +132,13 @@ class Indexing {
}
}
int digitA = (a.isNotEmpty) ? digits.indexOf(a[0]) : 0;
int digitB = (b != null && b.isNotEmpty)
final int digitA = (a.isNotEmpty) ? digits.indexOf(a[0]) : 0;
final int digitB = (b != null && b.isNotEmpty)
? digits.indexOf(b[0])
: digits.length;
if (digitB - digitA > 1) {
int midDigit = (digitA + digitB + 1) ~/ 2;
final int midDigit = (digitA + digitB + 1) ~/ 2;
return digits[midDigit];
} else {
if (b != null && b.length > 1) {
@@ -151,7 +151,7 @@ class Indexing {
}
String _getIntegerPart(String key) {
int integerPartLength = _getIntegerLength(key[0]);
final int integerPartLength = _getIntegerLength(key[0]);
if (integerPartLength > key.length) {
throw Exception('Invalid order key: $key');
}
@@ -163,8 +163,8 @@ class Indexing {
throw Exception('Invalid order key: $key');
}
String i = _getIntegerPart(key);
String f = key.substring(i.length);
final String i = _getIntegerPart(key);
final String f = key.substring(i.length);
if (f.isNotEmpty && f[f.length - 1] == '0') {
throw Exception('Invalid order key: $key');
}
@@ -192,8 +192,8 @@ class Indexing {
if (a == null) {
b = b!;
String ib = _getIntegerPart(b);
String fb = b.substring(ib.length);
final String ib = _getIntegerPart(b);
final String fb = b.substring(ib.length);
if (ib == smallestInteger) {
return ib + _midpoint('', fb);
}
@@ -201,22 +201,22 @@ class Indexing {
}
if (b == null) {
String ia = _getIntegerPart(a);
String fa = a.substring(ia.length);
String? i = _incrementInteger(ia);
final String ia = _getIntegerPart(a);
final String fa = a.substring(ia.length);
final String? i = _incrementInteger(ia);
return i ?? ia + _midpoint(fa, null);
}
String ia = _getIntegerPart(a);
String fa = a.substring(ia.length);
String ib = _getIntegerPart(b);
String fb = b.substring(ib.length);
final String ia = _getIntegerPart(a);
final String fa = a.substring(ia.length);
final String ib = _getIntegerPart(b);
final String fb = b.substring(ib.length);
if (ia == ib) {
return ia + _midpoint(fa, fb);
}
String? i = _incrementInteger(ia);
final String? i = _incrementInteger(ia);
return (i == null || i.compareTo(b) < 0) ? i : ia + _midpoint(fa, null);
}
@@ -230,7 +230,7 @@ class Indexing {
if (b == null) {
String? c = generateKeyBetween(a, b);
List<String?> result = [c];
final List<String?> result = [c];
for (int i = 1; i < n; i++) {
c = generateKeyBetween(c, b);
result.add(c);
@@ -240,7 +240,7 @@ class Indexing {
if (a == null) {
String? c = generateKeyBetween(a, b);
List<String?> result = [c];
final List<String?> result = [c];
for (int i = 1; i < n; i++) {
c = generateKeyBetween(a, c);
result.add(c);
@@ -248,13 +248,17 @@ class Indexing {
return result.reversed.toList();
}
int mid = n ~/ 2;
String? c = generateKeyBetween(a, b);
final int mid = n ~/ 2;
final String? c = generateKeyBetween(a, b);
return generateNKeysBetween(a, c, mid)
.followedBy([c])
.followedBy(generateNKeysBetween(c, b, n - mid - 1))
.toList();
}
List<String?> generateNKeys(int n) {
return generateNKeysBetween(null, null, n);
}
}
final indexing = Indexing();
+15 -4
View File
@@ -13,9 +13,9 @@ extension IterableExt<E> on Iterable<E> {
Iterable<List<E>> chunks(int size) sync* {
if (length == 0) return;
var iterator = this.iterator;
final iterator = this.iterator;
while (iterator.moveNext()) {
var chunk = [iterator.current];
final chunk = [iterator.current];
for (var i = 1; i < size && iterator.moveNext(); i++) {
chunk.add(iterator.current);
}
@@ -25,7 +25,7 @@ extension IterableExt<E> on Iterable<E> {
Iterable<E> fill(int length, {required E Function(int count) filler}) sync* {
int count = 0;
for (var item in this) {
for (final item in this) {
yield item;
count++;
if (count >= length) return;
@@ -69,6 +69,17 @@ extension ListExt<T> on List<T> {
return res;
}
List<T> copyAndPut(T data, bool Function(T element) test) {
final newList = List<T>.from(this);
final index = newList.indexWhere(test);
if (index != -1) {
newList[index] = data;
} else {
newList.insert(0, data);
}
return newList;
}
List<T> safeSublist(int start, [int? end]) {
if (start <= 0) return this;
if (start > length) return [];
@@ -121,7 +132,7 @@ extension DoubleListExt on List<double> {
int right = length - 1;
while (left <= right) {
int mid = left + (right - left) ~/ 2;
final int mid = left + (right - left) ~/ 2;
if (mid == length - 1 ||
(this[mid] <= target && target < this[mid + 1])) {
+27
View File
@@ -0,0 +1,27 @@
import 'dart:convert';
import 'dart:ffi' as ffi;
import 'package:flutter_js/flutter_js.dart';
Future<Map<String, dynamic>> handleEvaluate(
String scriptContent,
Map<String, dynamic> config,
) async {
if (config['proxy-providers'] == null) {
config['proxy-providers'] = {};
}
final configJs = json.encode(config);
final runtime = getJavascriptRuntime();
final res = await runtime.evaluateAsync('''
$scriptContent
main($configJs)
''');
if (res.isError) {
throw res.stringResult;
}
final value = switch (res.rawResult is ffi.Pointer) {
true => runtime.convertValue<Map<String, dynamic>>(res),
false => Map<String, dynamic>.from(res.rawResult),
};
return value ?? config;
}
+3 -3
View File
@@ -23,15 +23,15 @@ class AutoLaunch {
}
Future<bool> get isEnable async {
return await launchAtStartup.isEnabled();
return launchAtStartup.isEnabled();
}
Future<bool> enable() async {
return await launchAtStartup.enable();
return launchAtStartup.enable();
}
Future<bool> disable() async {
return await launchAtStartup.disable();
return launchAtStartup.disable();
}
Future<void> updateStatus(bool isAutoLaunch) async {
+15 -4
View File
@@ -11,16 +11,27 @@ class Measure {
: _measureMap = {},
_textScaler = TextScaler.linear(textScaleFactor);
Size computeTextSize(Text text, {double maxWidth = double.infinity}) {
final textPainter = TextPainter(
text: TextSpan(text: text.data, style: text.style),
TextPainter computeText(Text text, {TextStyle? style, double? maxWidth}) {
return TextPainter(
text: TextSpan(text: text.data, style: text.style ?? style),
maxLines: text.maxLines,
textScaler: _textScaler,
ellipsis: '...',
locale: Localizations.localeOf(context),
textDirection: text.textDirection ?? TextDirection.ltr,
)..layout(maxWidth: maxWidth);
)..layout(maxWidth: maxWidth ?? double.infinity);
}
Size computeTextSize(Text text, {TextStyle? style, double? maxWidth}) {
final textPainter = computeText(text, style: style, maxWidth: maxWidth);
return textPainter.size;
}
bool computeTextIsOverflow(Text text, {TextStyle? style, double? maxWidth}) {
final textPainter = computeText(text, style: style, maxWidth: maxWidth);
return textPainter.didExceedMaxLines;
}
double get bodyMediumHeight {
return _measureMap.updateCacheValue(
'bodyMediumHeight',
+1 -1
View File
@@ -46,7 +46,7 @@ class Migration {
}
Future<MigrationData> _oldToNow(Map<String, Object?> configMap) async {
return await oldToNowTask(configMap);
return oldToNowTask(configMap);
}
}
+9 -2
View File
@@ -1,6 +1,9 @@
import 'package:flutter/cupertino.dart';
import 'package:riverpod/riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'utils.dart';
mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
T get value => state;
@@ -25,9 +28,9 @@ mixin AutoDisposeNotifierMixin<T> on AnyNotifier<T, T> {
void onUpdate(T value) {}
void update(T? Function(T) builder) {
void update(T Function(T) builder) {
final res = builder(value);
if (res == null) {
if (res == value) {
return;
}
value = res;
@@ -41,3 +44,7 @@ mixin AsyncNotifierMixin<T> on AnyNotifier<AsyncValue<T>, T> {
state = AsyncData(value);
}
}
mixin UniqueKeyStateMixin<T extends StatefulWidget> on State<T> {
final key = utils.id;
}
+6 -6
View File
@@ -13,7 +13,7 @@ class Navigation {
return [
NavigationItem(
keep: false,
icon: Icon(Icons.space_dashboard),
icon: const Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
builder: (_) =>
const DashboardView(key: GlobalObjectKey(PageLabel.dashboard)),
@@ -28,13 +28,13 @@ class Navigation {
: [],
),
NavigationItem(
icon: Icon(Icons.folder),
icon: const Icon(Icons.folder),
label: PageLabel.profiles,
builder: (_) =>
const ProfilesView(key: GlobalObjectKey(PageLabel.profiles)),
),
NavigationItem(
icon: Icon(Icons.view_timeline),
icon: const Icon(Icons.view_timeline),
label: PageLabel.requests,
builder: (_) =>
const RequestsView(key: GlobalObjectKey(PageLabel.requests)),
@@ -42,7 +42,7 @@ class Navigation {
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
NavigationItem(
icon: Icon(Icons.ballot),
icon: const Icon(Icons.ballot),
label: PageLabel.connections,
builder: (_) =>
const ConnectionsView(key: GlobalObjectKey(PageLabel.connections)),
@@ -50,7 +50,7 @@ class Navigation {
modes: [NavigationItemMode.desktop, NavigationItemMode.more],
),
NavigationItem(
icon: Icon(Icons.storage),
icon: const Icon(Icons.storage),
label: PageLabel.resources,
description: 'resourcesDesc',
builder: (_) =>
@@ -67,7 +67,7 @@ class Navigation {
: [],
),
NavigationItem(
icon: Icon(Icons.construction),
icon: const Icon(Icons.construction),
label: PageLabel.tools,
builder: (_) => const ToolsView(key: GlobalObjectKey(PageLabel.tools)),
modes: [NavigationItemMode.desktop, NavigationItemMode.mobile],
+10 -9
View File
@@ -1,16 +1,17 @@
import 'package:animations/animations.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
class BaseNavigator {
static Future<T?> push<T>(BuildContext context, Widget child) async {
if (!appController.isMobile) {
return await Navigator.of(
if (!globalState.container.read(isMobileViewProvider)) {
return Navigator.of(
context,
).push<T>(CommonDesktopRoute(builder: (context) => child));
}
return await Navigator.of(
return Navigator.of(
context,
).push<T>(CommonRoute(builder: (context) => child));
}
@@ -65,10 +66,10 @@ class CommonDesktopRoute<T> extends PageRoute<T> {
bool get maintainState => true;
@override
Duration get transitionDuration => Duration(milliseconds: 200);
Duration get transitionDuration => const Duration(milliseconds: 200);
@override
Duration get reverseTransitionDuration => Duration(milliseconds: 200);
Duration get reverseTransitionDuration => const Duration(milliseconds: 200);
}
class CommonRoute<T> extends PageRoute<T> {
@@ -106,10 +107,10 @@ class CommonRoute<T> extends PageRoute<T> {
}
@override
Duration get transitionDuration => Duration(milliseconds: 300);
Duration get transitionDuration => const Duration(milliseconds: 300);
@override
Duration get reverseTransitionDuration => Duration(milliseconds: 300);
Duration get reverseTransitionDuration => const Duration(milliseconds: 300);
}
final Animatable<Offset> _kRightMiddleTween = Tween<Offset>(
@@ -262,7 +263,7 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
(_primaryShadowCurve ?? widget.primaryRouteAnimation).drive(
DecorationTween(
begin: const _CommonEdgeShadowDecoration(),
end: _CommonEdgeShadowDecoration(<Color>[
end: const _CommonEdgeShadowDecoration(<Color>[
Color(0x04000000),
Colors.transparent,
]),
+2 -2
View File
@@ -27,7 +27,7 @@ extension NumExt on num {
}
TrafficShow get traffic {
final units = TrafficUnit.values;
const units = TrafficUnit.values;
var size = toDouble();
var unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
@@ -41,7 +41,7 @@ extension NumExt on num {
}
TrafficShow get shortTraffic {
final units = TrafficUnit.values;
const units = TrafficUnit.values;
var size = toDouble();
var unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
+88
View File
@@ -0,0 +1,88 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:wifi_ssid/wifi_ssid_manager.dart';
class Permissions {
static Permissions? _instance;
Permissions._internal();
factory Permissions() {
_instance ??= Permissions._internal();
return _instance!;
}
bool _isRequestingLocation = false;
bool needWaitingBatteryOptimizationSettings = false;
void check() {
checkLocationPermissions();
checkBatteryOptimizationDisable();
}
Future<void> checkBatteryOptimizationDisable() async {
await _checkBatteryOptimizationDisable();
}
Future<void> _checkBatteryOptimizationDisable() async {
const tag = LoadingTag.batteryOptimization;
try {
if (needWaitingBatteryOptimizationSettings) {
globalState.container.read(loadingProvider(tag).notifier).value = true;
}
globalState.container
.read(batteryOptimizationDisableProvider.notifier)
.value = await retry<bool>(
task: () async {
return await app?.isBatteryOptimizationDisabled() ?? false;
},
retryIf: (res) => res == false,
delay: const Duration(milliseconds: 500),
maxAttempts: needWaitingBatteryOptimizationSettings ? 5 : 1,
);
} finally {
globalState.container.read(loadingProvider(tag).notifier).value = false;
needWaitingBatteryOptimizationSettings = false;
}
}
Future<void> checkLocationPermissions() async {
if (!(system.isAndroid || system.isMacOS)) {
return;
}
final res = await WifiSsidManager.instance.checkPermission();
final current = globalState.container.read(locationPermissionsProvider);
if (res == WifiSsidPermission.granted ||
current != WifiSsidPermission.permanentlyDenied) {
globalState.container.read(locationPermissionsProvider.notifier).value =
res;
}
final needRequestPermission = globalState.container.read(
excludeSSIDsProvider.select((state) => state.isNotEmpty),
);
if (res == WifiSsidPermission.denied &&
needRequestPermission &&
!_isRequestingLocation) {
try {
_isRequestingLocation = true;
final res = await WifiSsidManager.instance.requestPermission();
globalState.container.read(locationPermissionsProvider.notifier).value =
res;
if (res != WifiSsidPermission.granted) {
final ssid = await WifiSsidManager.instance.getSsid();
globalState.container.read(currentSSIDProvider.notifier).value = ssid;
}
} finally {
_isRequestingLocation = false;
}
}
}
}
final permissions = Permissions();
+4 -4
View File
@@ -8,7 +8,7 @@ import 'package:mobile_scanner/mobile_scanner.dart';
class Picker {
Future<PlatformFile?> pickerFile({bool withData = true}) async {
final filePickerResult = await FilePicker.platform.pickFiles(
final filePickerResult = await FilePicker.pickFiles(
withData: withData,
allowMultiple: false,
initialDirectory: await appPath.downloadDirPath,
@@ -17,7 +17,7 @@ class Picker {
}
Future<String?> saveFile(String fileName, Uint8List bytes) async {
final path = await FilePicker.platform.saveFile(
final path = await FilePicker.saveFile(
fileName: fileName,
initialDirectory: await appPath.downloadDirPath,
bytes: bytes,
@@ -35,7 +35,7 @@ class Picker {
await localFile.create(recursive: true);
}
final bytes = Platform.isAndroid ? await localFile.readAsBytes() : null;
final path = await FilePicker.platform.saveFile(
final path = await FilePicker.saveFile(
fileName: fileName,
initialDirectory: await appPath.downloadDirPath,
bytes: bytes,
@@ -59,7 +59,7 @@ class Picker {
);
final result = capture?.barcodes.first.rawValue;
if (result == null || !result.isUrl) {
throw appLocalizations.pleaseUploadValidQrcode;
throw currentAppLocalizations.pleaseUploadValidQrcode;
}
return result;
}
+234
View File
@@ -0,0 +1,234 @@
import 'dart:async';
import 'dart:ffi';
import 'dart:isolate';
import 'dart:typed_data';
import 'package:ffi/ffi.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:win32/win32.dart';
const _pipeBufferSize = 256 * 1024;
class _PipeCmd {
final int type; // 0 = write, 1 = shutdown
final Uint8List? data;
const _PipeCmd(this.type, this.data);
}
void _pipeIsolate(List<Object> args) {
final pipeName = args[0] as String;
final dataPort = args[1] as SendPort;
final namePtr = pipeName.toNativeUtf16();
final handle = CreateNamedPipe(
namePtr,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT,
1,
_pipeBufferSize,
_pipeBufferSize,
0,
nullptr,
);
calloc.free(namePtr);
if (handle == INVALID_HANDLE_VALUE) {
dataPort.send(
'error:CreateNamedPipe failed, Win32 error=${GetLastError()}',
);
return;
}
final writeRx = ReceivePort();
final readBuf = calloc<Uint8>(_pipeBufferSize);
final bytesRead = calloc<Uint32>();
final overlapped = calloc<OVERLAPPED>();
final readEvent = CreateEvent(nullptr, 1, 0, nullptr);
overlapped.ref.hEvent = readEvent;
var running = true;
var clientConnected = false;
writeRx.listen((msg) {
if (msg is _PipeCmd) {
if (msg.type == 0) {
if (clientConnected && msg.data != null) {
final buf = calloc<Uint8>(msg.data!.length);
final written = calloc<Uint32>();
buf.asTypedList(msg.data!.length).setAll(0, msg.data!);
WriteFile(handle, buf, msg.data!.length, written, nullptr);
calloc.free(written);
calloc.free(buf);
}
} else if (msg.type == 1) {
running = false;
if (clientConnected) {
DisconnectNamedPipe(handle);
clientConnected = false;
}
writeRx.close();
}
}
});
final connectEvent = CreateEvent(nullptr, 1, 0, nullptr);
final connectOverlapped = calloc<OVERLAPPED>();
connectOverlapped.ref.hEvent = connectEvent;
while (running) {
final cr = ConnectNamedPipe(handle, connectOverlapped);
if (cr == 0) {
final err = GetLastError();
if (err == ERROR_IO_PENDING) {
while (running) {
final wr = WaitForSingleObject(connectEvent, 100);
if (wr == WAIT_OBJECT_0) break;
}
if (!running) break;
} else if (err != ERROR_PIPE_CONNECTED) {
dataPort.send('error:ConnectNamedPipe failed, Win32 error=$err');
break;
}
}
if (!running) break;
clientConnected = true;
dataPort.send(writeRx.sendPort);
var readPending = false;
while (running && clientConnected) {
if (!readPending) {
bytesRead.value = 0;
final readResult = ReadFile(
handle,
readBuf,
_pipeBufferSize,
bytesRead,
overlapped,
);
if (readResult != 0) {
if (bytesRead.value > 0) {
dataPort.send(
Uint8List.fromList(readBuf.asTypedList(bytesRead.value)),
);
} else {
break;
}
} else {
final err = GetLastError();
if (err == ERROR_IO_PENDING) {
readPending = true;
} else if (bytesRead.value > 0) {
dataPort.send(
Uint8List.fromList(readBuf.asTypedList(bytesRead.value)),
);
} else {
break;
}
}
}
if (readPending) {
final wr = WaitForSingleObject(readEvent, 100);
if (wr == WAIT_OBJECT_0) {
final got = GetOverlappedResult(handle, overlapped, bytesRead, 0);
if (got != 0 && bytesRead.value > 0) {
dataPort.send(
Uint8List.fromList(readBuf.asTypedList(bytesRead.value)),
);
readPending = false;
} else {
break;
}
} else if (wr != WAIT_TIMEOUT) {
break;
}
}
}
if (running && clientConnected) {
DisconnectNamedPipe(handle);
clientConnected = false;
dataPort.send('closed');
}
}
CloseHandle(readEvent);
CloseHandle(connectEvent);
calloc.free(connectOverlapped);
calloc.free(overlapped);
calloc.free(bytesRead);
calloc.free(readBuf);
CloseHandle(handle);
}
class NamedPipeServer {
final String pipeName;
Isolate? _isolate;
SendPort? _writePort;
ReceivePort? _responsePort;
StreamSubscription? _responseSub;
final _dataController = StreamController<Uint8List>.broadcast();
Completer<void> _connectionCompleter = Completer<void>();
void Function()? onDisconnect;
NamedPipeServer._(this.pipeName);
Stream<Uint8List> get dataStream => _dataController.stream;
Completer<void> get connectionCompleter => _connectionCompleter;
static Future<NamedPipeServer> bind(String pipeName) async {
final server = NamedPipeServer._(pipeName);
server._responsePort = ReceivePort();
server._responseSub = server._responsePort!.listen((message) {
if (message is SendPort) {
server._writePort = message;
if (server._connectionCompleter.isCompleted) {
server._connectionCompleter = Completer<void>();
}
server._connectionCompleter.complete();
} else if (message is Uint8List) {
server._dataController.add(message);
} else if (message == 'closed') {
server._connectionCompleter = Completer<void>();
server.onDisconnect?.call();
} else if (message is String && message.startsWith('error:')) {
commonPrint.log('[NamedPipeServer] $message', logLevel: LogLevel.error);
}
});
server._isolate = await Isolate.spawn(_pipeIsolate, [
pipeName,
server._responsePort!.sendPort,
]);
return server;
}
void writeln(String message) {
final wp = _writePort;
if (wp == null) return;
final str = '$message\n';
wp.send(_PipeCmd(0, Uint8List.fromList(str.codeUnits)));
}
Future<void> close() async {
final wp = _writePort;
if (wp == null) return;
_writePort = null;
wp.send(const _PipeCmd(1, null));
_responseSub?.cancel();
_responsePort?.close();
await Future.delayed(const Duration(milliseconds: 50));
_isolate?.kill();
_isolate = null;
}
}
+6 -3
View File
@@ -1,6 +1,7 @@
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
class CommonPrint {
@@ -16,10 +17,12 @@ class CommonPrint {
void log(String? text, {LogLevel logLevel = LogLevel.info}) {
final payload = '[APP] $text';
debugPrint(payload);
if (!appController.isAttach) {
if (!globalState.isAttach) {
return;
}
appController.addLog(Log.app(payload).copyWith(logLevel: logLevel));
globalState.container
.read(logsProvider.notifier)
.add(Log.app(payload).copyWith(logLevel: logLevel));
}
}
+4 -4
View File
@@ -13,13 +13,13 @@ class Protocol {
}
void register(String scheme) {
String protocolRegKey = 'Software\\Classes\\$scheme';
RegistryValue protocolRegValue = RegistryValue.string(
final String protocolRegKey = 'Software\\Classes\\$scheme';
const RegistryValue protocolRegValue = RegistryValue.string(
'URL Protocol',
'',
);
String protocolCmdRegKey = 'shell\\open\\command';
RegistryValue protocolCmdRegValue = RegistryValue.string(
const String protocolCmdRegKey = 'shell\\open\\command';
final RegistryValue protocolCmdRegValue = RegistryValue.string(
'',
'"${Platform.resolvedExecutable}" "%1"',
);
+1 -1
View File
@@ -25,7 +25,7 @@ class Render {
throttler.call(
FunctionTag.renderPause,
_pause,
duration: Duration(seconds: 5),
duration: const Duration(seconds: 5),
);
}
+37 -34
View File
@@ -1,16 +1,15 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:dio/io.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
class Request {
late final Dio dio;
@@ -24,7 +23,7 @@ class Request {
createHttpClient: () {
final client = HttpClient();
client.findProxy = (Uri uri) {
client.userAgent = appController.ua;
client.userAgent = globalState.ua;
return FlClashHttpOverrides.handleFindProxy(uri);
};
return client;
@@ -42,13 +41,13 @@ class Request {
commonPrint.log('getFileResponseForUrl error ${e.toString()}');
if (e is DioException) {
if (e.type == DioExceptionType.unknown) {
throw appLocalizations.unknownNetworkError;
throw currentAppLocalizations.unknownNetworkError;
} else if (e.type == DioExceptionType.badResponse) {
throw appLocalizations.networkException;
throw currentAppLocalizations.networkException;
}
rethrow;
}
throw appLocalizations.unknownNetworkError;
throw currentAppLocalizations.unknownNetworkError;
}
}
@@ -106,7 +105,7 @@ class Request {
final token = cancelToken ?? CancelToken();
final futures = _ipInfoSources.entries.map((source) async {
final Completer<Result<IpInfo?>> completer = Completer();
handleFailRes() {
void handleFailRes() {
if (!completer.isCompleted && failureCount == _ipInfoSources.length) {
completer.complete(Result.success(null));
}
@@ -114,27 +113,30 @@ class Request {
final future = dio
.get<Map<String, dynamic>>(
source.key,
cancelToken: token,
options: Options(responseType: ResponseType.json),
)
source.key,
cancelToken: token,
options: Options(responseType: ResponseType.json),
)
.timeout(const Duration(seconds: 10));
future
.then((res) {
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
return;
}
failureCount++;
handleFailRes();
})
if (res.statusCode == HttpStatus.ok && res.data != null) {
completer.complete(Result.success(source.value(res.data!)));
return;
}
commonPrint.log('checkIp data empty', logLevel: LogLevel.info);
failureCount++;
handleFailRes();
})
.catchError((e) {
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
}
handleFailRes();
});
failureCount++;
if (e is DioException && e.type == DioExceptionType.cancel) {
completer.complete(Result.error('cancelled'));
return;
}
commonPrint.log('checkIp error $e', logLevel: LogLevel.warning);
handleFailRes();
});
return completer.future;
});
final res = await Future.any(futures);
@@ -143,12 +145,13 @@ class Request {
}
Future<bool> pingHelper() async {
if (kDebugMode) return true;
try {
final response = await dio
.get(
'http://$localhost:$helperPort/ping',
options: Options(responseType: ResponseType.plain),
)
'http://$localhost:$helperPort/ping',
options: Options(responseType: ResponseType.plain),
)
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
@@ -163,10 +166,10 @@ class Request {
try {
final response = await dio
.post(
'http://$localhost:$helperPort/start',
data: json.encode({'path': appPath.corePath, 'arg': arg}),
options: Options(responseType: ResponseType.plain),
)
'http://$localhost:$helperPort/start',
data: json.encode({'path': appPath.corePath, 'arg': arg}),
options: Options(responseType: ResponseType.plain),
)
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
@@ -182,9 +185,9 @@ class Request {
try {
final response = await dio
.post(
'http://$localhost:$helperPort/stop',
options: Options(responseType: ResponseType.plain),
)
'http://$localhost:$helperPort/stop',
options: Options(responseType: ResponseType.plain),
)
.timeout(const Duration(milliseconds: 2000));
if (response.statusCode != HttpStatus.ok) {
return false;
+17 -11
View File
@@ -8,18 +8,24 @@ class Snowflake {
return _instance!;
}
static const int twepoch = 1704067200000;
static const int _twepoch = 1704067200000;
static const int workerIdBits = 10;
static const int sequenceBits = 12;
static const int _workerIdBits = 10;
static const int _sequenceBits = 12;
static const int maxWorkerId = -1 ^ (-1 << workerIdBits);
static const int sequenceMask = -1 ^ (-1 << sequenceBits);
static const int _sequenceMask = -1 ^ (-1 << _sequenceBits);
static const int workerIdShift = sequenceBits;
static const int timestampLeftShift = sequenceBits + workerIdBits;
static const int _workerIdShift = _sequenceBits;
static const int _timestampLeftShift = _sequenceBits + _workerIdBits;
final int workerId = 1;
static int buildId(int? id) {
if (id != null) {
return id;
}
return snowflake.id;
}
final int _workerId = 1;
int _lastTimestamp = -1;
int _sequence = 0;
@@ -31,7 +37,7 @@ class Snowflake {
);
}
if (timestamp == _lastTimestamp) {
_sequence = (_sequence + 1) & sequenceMask;
_sequence = (_sequence + 1) & _sequenceMask;
if (_sequence == 0) {
timestamp = _getNextMillis(_lastTimestamp);
}
@@ -41,8 +47,8 @@ class Snowflake {
_lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) |
(workerId << workerIdShift) |
return ((timestamp - _twepoch) << _timestampLeftShift) |
(_workerId << _workerIdShift) |
_sequence;
}
+16 -4
View File
@@ -6,7 +6,12 @@ import 'package:fl_clash/common/common.dart';
extension StringExtension on String {
bool get isUrl {
return RegExp(r'^(http|https|ftp)://').hasMatch(this);
final uri = Uri.tryParse(this);
return uri != null &&
(uri.scheme == 'http' ||
uri.scheme == 'https' ||
uri.scheme == 'ftp') &&
uri.host.isNotEmpty;
}
dynamic get splitByMultipleSeparators {
@@ -35,7 +40,7 @@ extension StringExtension on String {
final byteData = ByteData(length * 2);
final bom = [0xFF, 0xFE];
for (int i = 0; i < length; i++) {
int charCode = codeUnitAt(i);
final int charCode = codeUnitAt(i);
byteData.setUint16(i * 2, charCode, Endian.little);
}
return bom + byteData.buffer.asUint8List();
@@ -79,13 +84,20 @@ extension StringExtension on String {
// }
Future<T> commonToJSON<T>() async {
final thresholdLimit = 51200;
const thresholdLimit = 51200;
if (length < thresholdLimit) {
return json.decode(this);
} else {
return await decodeJSONTask<T>(this);
return decodeJSONTask<T>(this);
}
}
String? get value {
if (isEmpty) {
return null;
}
return this;
}
}
extension StringNullExt on String? {
+17 -7
View File
@@ -64,11 +64,14 @@ class System {
return true;
}
static String _shellEscape(String value) {
return "'${value.replaceAll("'", "'\\''")}'";
}
Future<AuthorizeCode> authorizeCore() async {
if (system.isAndroid) {
return AuthorizeCode.error;
}
final corePath = appPath.corePath.replaceAll(' ', '\\\\ ');
final isAdmin = await checkIsAdmin();
if (isAdmin) {
return AuthorizeCode.none;
@@ -83,7 +86,8 @@ class System {
}
if (system.isMacOS) {
final shell = 'chown root:admin $corePath; chmod +sx $corePath';
final escapedPath = _shellEscape(appPath.corePath);
final shell = 'chown root:admin $escapedPath && chmod +sx $escapedPath';
final arguments = [
'-e',
'do shell script "$shell" with administrator privileges',
@@ -98,13 +102,18 @@ class System {
final password = await globalState.showCommonDialog<String>(
child: InputDialog(
obscureText: true,
title: appLocalizations.pleaseInputAdminPassword,
title: currentAppLocalizations.pleaseInputAdminPassword,
value: '',
),
);
if (password == null || password.isEmpty) {
return AuthorizeCode.error;
}
final escapedPassword = _shellEscape(password);
final escapedCorePath = _shellEscape(appPath.corePath);
final arguments = [
'-c',
'echo "$password" | sudo -S chown root:root "$corePath" && echo "$password" | sudo -S chmod +sx "$corePath"',
'echo $escapedPassword | sudo -S chown root:root $escapedCorePath && echo $escapedPassword | sudo -S chmod +sx $escapedCorePath',
];
final result = await Process.run(shell, arguments);
if (result.exitCode != 0) {
@@ -125,6 +134,7 @@ class System {
await SystemNavigator.pop();
}
await window?.close();
window?.forceExit();
}
}
@@ -257,12 +267,12 @@ class Windows {
final res = runas('cmd.exe', command);
await Future.delayed(Duration(milliseconds: 300));
await Future.delayed(const Duration(milliseconds: 300));
final retryStatus = await retry(
task: checkService,
maxAttempts: 5,
retryIf: (status) => status != WindowsHelperServiceStatus.running,
delay: Duration(seconds: 1),
delay: const Duration(seconds: 1),
);
return res && retryStatus == WindowsHelperServiceStatus.running;
}
@@ -404,7 +414,7 @@ class MacOS {
if (originDns == null) {
return;
}
final needAddDns = '223.5.5.5';
const needAddDns = '223.5.5.5';
if (originDns.contains(needAddDns)) {
return;
}
+88 -58
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@@ -13,7 +14,7 @@ import 'package:flutter/services.dart';
import 'package:path/path.dart';
Future<T> decodeJSONTask<T>(String data) async {
return await compute<String, T>(_decodeJSON, data);
return compute<String, T>(_decodeJSON, data);
}
Future<T> _decodeJSON<T>(String content) async {
@@ -21,7 +22,7 @@ Future<T> _decodeJSON<T>(String content) async {
}
Future<String> encodeJSONTask<T>(T data) async {
return await compute<T, String>(_encodeJSON, data);
return compute<T, String>(_encodeJSON, data);
}
Future<String> _encodeJSON<T>(T content) async {
@@ -29,15 +30,23 @@ Future<String> _encodeJSON<T>(T content) async {
}
Future<String> encodeYamlTask<T>(T data) async {
return await compute<T, String>(_encodeYaml, data);
return compute<T, String>(_encodeYaml, data);
}
Future<String> _encodeYaml<T>(T content) async {
return yaml.encode(content);
}
Future<String> encodeMD5Task(String data) async {
return compute<String, String>(_encodeMD5, data);
}
Future<String> _encodeMD5<T>(String content) async {
return content.toMd5();
}
Future<List<Group>> toGroupsTask(ComputeGroupsState data) async {
return await compute<ComputeGroupsState, List<Group>>(_toGroupsTask, data);
return compute<ComputeGroupsState, List<Group>>(_toGroupsTask, data);
}
Future<List<Group>> _toGroupsTask(ComputeGroupsState state) async {
@@ -73,16 +82,16 @@ Future<List<Group>> _toGroupsTask(ComputeGroupsState state) async {
);
}
Future<Map<String, dynamic>> makeRealProfileTask(
Future<VM2<String, String>> makeRealProfileTask(
MakeRealProfileState data,
) async {
return await compute<MakeRealProfileState, Map<String, dynamic>>(
return compute<MakeRealProfileState, VM2<String, String>>(
_makeRealProfileTask,
data,
);
}
Future<Map<String, dynamic>> _makeRealProfileTask(
Future<VM2<String, String>> _makeRealProfileTask(
MakeRealProfileState data,
) async {
final rawConfig = Map.from(data.rawConfig);
@@ -186,7 +195,7 @@ Future<Map<String, dynamic>> _makeRealProfileTask(
rawConfig['dns'] = {};
}
final isEnableDns = rawConfig['dns']['enable'] == true;
final systemDns = 'system://';
const systemDns = 'system://';
if (overrideDns || !isEnableDns) {
final dns = switch (!isEnableDns) {
true => realPatchConfig.dns.copyWith(
@@ -210,58 +219,62 @@ Future<Map<String, dynamic>> _makeRealProfileTask(
}
}
List<String> rules = [];
if (rawConfig['rules'] != null) {
rules = List<String>.from(rawConfig['rules']);
}
rawConfig.remove('rules');
if (addedRules.isNotEmpty) {
final parsedNewRules = addedRules
.map((item) => ParsedRule.parseString(item.value))
.toList();
final hasMatchPlaceholder = parsedNewRules.any(
(item) => item.ruleTarget?.toUpperCase() == 'MATCH',
);
String? replacementTarget;
if (data.rules.isEmpty) {
if (rawConfig['rules'] != null) {
rules = List<String>.from(rawConfig['rules']);
}
if (addedRules.isNotEmpty) {
final hasMatchPlaceholder = addedRules.any(
(item) => item.ruleTarget?.toUpperCase() == 'MATCH',
);
String? replacementTarget;
if (hasMatchPlaceholder) {
for (int i = rules.length - 1; i >= 0; i--) {
final parsed = ParsedRule.parseString(rules[i]);
if (parsed.ruleAction == RuleAction.MATCH) {
final target = parsed.ruleTarget;
if (target != null && target.isNotEmpty) {
replacementTarget = target;
break;
if (hasMatchPlaceholder) {
for (int i = rules.length - 1; i >= 0; i--) {
final parsed = Rule.parse(rules[i]);
if (parsed.ruleAction == RuleAction.MATCH) {
final target = parsed.ruleTarget;
if (target != null && target.isNotEmpty) {
replacementTarget = target;
break;
}
}
}
}
}
final List<String> finalAddedRules;
final List<String> finalAddedRules;
if (replacementTarget?.isNotEmpty == true) {
finalAddedRules = [];
for (int i = 0; i < parsedNewRules.length; i++) {
final parsed = parsedNewRules[i];
if (parsed.ruleTarget?.toUpperCase() == 'MATCH') {
finalAddedRules.add(
parsed.copyWith(ruleTarget: replacementTarget).value,
);
} else {
finalAddedRules.add(addedRules[i].value);
if (replacementTarget?.isNotEmpty == true) {
finalAddedRules = [];
for (int i = 0; i < addedRules.length; i++) {
final parsed = addedRules[i];
if (parsed.ruleTarget?.toUpperCase() == 'MATCH') {
finalAddedRules.add(
parsed.copyWith(ruleTarget: replacementTarget).rawValue,
);
} else {
finalAddedRules.add(addedRules[i].rawValue);
}
}
} else {
finalAddedRules = addedRules.map((e) => e.rawValue).toList();
}
} else {
finalAddedRules = addedRules.map((e) => e.value).toList();
rules = [...finalAddedRules, ...rules];
}
rules = [...finalAddedRules, ...rules];
} else {
rules = data.rules.map((item) => item.rawValue).toList();
}
if (data.proxyGroups.isNotEmpty) {
rawConfig['proxy-groups'] = data.proxyGroups;
}
rawConfig['rules'] = rules;
return Map<String, dynamic>.from(rawConfig);
final yaml = await _encodeYaml(Map<String, dynamic>.from(rawConfig));
return VM2(yaml, yaml.toMd5());
}
Future<List<String>> shakingProfileTask(
VM2<Iterable<int>, Iterable<int>> data,
) async {
return await compute<
return compute<
VM3<Iterable<int>, Iterable<int>, RootIsolateToken>,
List<String>
>(_shakingProfileTask, VM3(data.a, data.b, RootIsolateToken.instance!));
@@ -307,7 +320,7 @@ Future<List<String>> _shakingProfileTask(
}
Future<String> encodeLogsTask(List<Log> data) async {
return await compute<List<Log>, String>(_encodeLogsTask, data);
return compute<List<Log>, String>(_encodeLogsTask, data);
}
Future<String> _encodeLogsTask(List<Log> data) async {
@@ -318,7 +331,7 @@ Future<String> _encodeLogsTask(List<Log> data) async {
Future<MigrationData> oldToNowTask(Map<String, Object?> data) async {
final homeDir = await appPath.homeDirPath;
return await compute<
return compute<
VM3<Map<String, Object?>, String, String>,
MigrationData
>(_oldToNowTask, VM3(data, homeDir, homeDir));
@@ -345,7 +358,8 @@ Future<MigrationData> _oldToNowTask(
vpnPropsRaw['accessControlProps'] = vpnPropsRaw['accessControl'];
}
configMap['davProps'] = configMap['dav'];
final appSettingProps = configMap['appSetting'] as Map? ?? {};
final appSettingProps =
configMap['appSetting'] as Map<String, dynamic>? ?? {};
appSettingProps['restoreStrategy'] = appSettingProps['recoveryStrategy'];
configMap['appSettingProps'] = appSettingProps;
configMap['proxiesStyleProps'] = configMap['proxiesStyle'];
@@ -377,16 +391,17 @@ Future<MigrationData> _oldToNowTask(
Script(id: newId, label: label, lastUpdateTime: DateTime.now()),
);
}
List rawRules = configMap['rules'] as List<dynamic>? ?? [];
final List rawRules = configMap['rules'] as List<dynamic>? ?? [];
final List<Rule> rules = [];
final List<ProfileRuleLink> links = [];
for (final rawRule in rawRules) {
final id = idMap.updateCacheValue(rawRule['id'], () => snowflake.id);
rawRule['id'] = id;
rules.add(Rule.fromJson(rawRule));
final value = rawRule['value'] ?? '';
rules.add(Rule.parse(value, id: id));
links.add(ProfileRuleLink(ruleId: id));
}
List rawProfiles = configMap['profiles'] as List<dynamic>? ?? [];
final List rawProfiles = configMap['profiles'] as List<dynamic>? ?? [];
final List<Profile> profiles = [];
for (final rawProfile in rawProfiles) {
final rawId = rawProfile['id'] as String?;
@@ -402,8 +417,8 @@ Future<MigrationData> _oldToNowTask(
final addedRules = standardOverwrite['addedRules'] as List? ?? [];
for (final addRule in addedRules) {
final id = idMap.updateCacheValue(addRule['id'], () => snowflake.id);
addRule['id'] = id;
rules.add(Rule.fromJson(addRule));
final value = addRule['value'] ?? '';
rules.add(Rule.parse(value, id: id));
links.add(
ProfileRuleLink(
profileId: profileId,
@@ -458,7 +473,7 @@ Future<String> backupTask(
Map<String, dynamic> configMap,
Iterable<String> fileNames,
) async {
return await compute<
return compute<
VM3<Map<String, dynamic>, Iterable<String>, RootIsolateToken>,
String
>(_backupTask, VM3(configMap, fileNames, RootIsolateToken.instance!));
@@ -516,7 +531,7 @@ Future<String> _backupTask<T>(
}
Future<MigrationData> restoreTask() async {
return await compute<RootIsolateToken, MigrationData>(
return compute<RootIsolateToken, MigrationData>(
_restoreTask,
RootIsolateToken.instance!,
);
@@ -541,7 +556,7 @@ Future<MigrationData> _restoreTask(RootIsolateToken token) async {
await input.close();
final restoreConfigFile = File(join(restoreDirPath, configJsonName));
if (!await restoreConfigFile.exists()) {
throw appLocalizations.invalidBackupFile;
throw currentAppLocalizations.invalidBackupFile;
}
final restoreConfigMap =
json.decode(await restoreConfigFile.readAsString())
@@ -567,10 +582,11 @@ Future<MigrationData> _restoreTask(RootIsolateToken token) async {
),
);
final results = await Future.wait([
database.profilesDao.all().get(),
database.scriptsDao.all().get(),
database.profilesDao.query().get(),
database.scriptsDao.query().get(),
database.rules.all().map((item) => item.toRule()).get(),
database.profileRuleLinks.all().map((item) => item.toLink()).get(),
database.proxyGroups.all().map((item) => item.toProxyGroup()).get(),
]);
final profiles = results[0].cast<Profile>();
final scripts = results[1].cast<Script>();
@@ -592,6 +608,7 @@ Future<MigrationData> _restoreTask(RootIsolateToken token) async {
scripts: scripts,
rules: results[2].cast<Rule>(),
links: results[3].cast<ProfileRuleLink>(),
proxyGroups: results[4].cast<ProxyGroup>(),
);
await database.close();
return migrationData;
@@ -610,3 +627,16 @@ String _getScriptPath(String root, String fileName) {
String _getProfilePath(String root, String fileName) {
return join(root, 'profiles', '$fileName.yaml');
}
Future<List<T>> mapListTask<T, S>(List<S> results, T Function(S) mapper) async {
return compute<VM2<List<S>, T Function(S)>, List<T>>(
_mapListTask,
VM2(results, mapper),
);
}
Future<List<T>> _mapListTask<T, S>(VM2<List<S>, T Function(S)> vm2) async {
final results = vm2.a;
final mapper = vm2.b;
return results.map((item) => mapper(item)).toList();
}
+13 -10
View File
@@ -6,16 +6,15 @@ class CommonTheme {
final Map<String, Color> _colorMap;
final double textScaleFactor;
CommonTheme.of(
this.context,
this.textScaleFactor,
) : _colorMap = {};
CommonTheme.of(this.context, this.textScaleFactor) : _colorMap = {};
Color get darkenSecondaryContainer {
return _colorMap.updateCacheValue(
'darkenSecondaryContainer',
() => context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.1),
() => context.colorScheme.secondaryContainer.blendDarken(
context,
factor: 0.1,
),
);
}
@@ -31,16 +30,20 @@ class CommonTheme {
Color get darken2SecondaryContainer {
return _colorMap.updateCacheValue(
'darken2SecondaryContainer',
() => context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.2),
() => context.colorScheme.secondaryContainer.blendDarken(
context,
factor: 0.2,
),
);
}
Color get darken3PrimaryContainer {
return _colorMap.updateCacheValue(
'darken3PrimaryContainer',
() => context.colorScheme.primaryContainer
.blendDarken(context, factor: 0.3),
() => context.colorScheme.primaryContainer.blendDarken(
context,
factor: 0.3,
),
);
}
}
+24 -17
View File
@@ -1,8 +1,9 @@
import 'dart:io';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/services.dart';
import 'package:intl/intl.dart';
import 'package:tray_manager/tray_manager.dart';
@@ -49,7 +50,7 @@ class Tray {
}
await trayManager.setIcon(
getTryIcon(isStart: isStart, tunEnable: tunEnable),
isTemplate: true,
isTemplate: system.isMacOS,
);
if (!Platform.isLinux) {
await trayManager.setToolTip(appName);
@@ -69,7 +70,12 @@ class Tray {
tunEnable: trayState.tunEnable,
);
}
List<MenuItem> menuItems = [];
final List<MenuItem> menuItems = [];
final ref = globalState.container;
final commonAction = ref.read(commonActionProvider.notifier);
final systemAction = ref.read(systemActionProvider.notifier);
final setupAction = ref.read(setupActionProvider.notifier);
final appLocalizations = currentAppLocalizations;
final showMenuItem = MenuItem(
label: appLocalizations.show,
onClick: (_) {
@@ -80,7 +86,7 @@ class Tray {
final startMenuItem = MenuItem.checkbox(
label: trayState.isStart ? appLocalizations.stop : appLocalizations.start,
onClick: (_) async {
appController.updateStart();
commonAction.updateStart();
},
checked: false,
);
@@ -89,7 +95,7 @@ class Tray {
final speedStatistics = MenuItem.checkbox(
label: appLocalizations.speedStatistics,
onClick: (_) async {
appController.updateSpeedStatistics();
commonAction.updateSpeedStatistics();
},
checked: trayState.showTrayTitle,
);
@@ -101,7 +107,7 @@ class Tray {
MenuItem.checkbox(
label: Intl.message(mode.name),
onClick: (_) {
appController.changeMode(mode);
setupAction.changeMode(mode);
},
checked: mode == trayState.mode,
),
@@ -110,19 +116,20 @@ class Tray {
menuItems.add(MenuItem.separator());
if (system.isMacOS) {
for (final group in trayState.groups) {
List<MenuItem> subMenuItems = [];
final List<MenuItem> subMenuItems = [];
for (final proxy in group.all) {
subMenuItems.add(
MenuItem.checkbox(
label: proxy.name,
checked:
appController.getSelectedProxyName(group.name) == proxy.name,
ref.read(selectedProxyNameProvider(group.name)) == proxy.name,
onClick: (_) {
appController.updateCurrentSelectedMap(group.name, proxy.name);
appController.changeProxy(
groupName: group.name,
proxyName: proxy.name,
);
ref
.read(profilesActionProvider.notifier)
.updateCurrentSelectedMap(group.name, proxy.name);
ref
.read(proxiesActionProvider.notifier)
.changeProxy(groupName: group.name, proxyName: proxy.name);
},
),
);
@@ -143,7 +150,7 @@ class Tray {
MenuItem.checkbox(
label: appLocalizations.tun,
onClick: (_) {
appController.updateTun();
systemAction.updateTun();
},
checked: trayState.tunEnable,
),
@@ -152,7 +159,7 @@ class Tray {
MenuItem.checkbox(
label: appLocalizations.systemProxy,
onClick: (_) {
appController.updateSystemProxy();
systemAction.updateSystemProxy();
},
checked: trayState.systemProxy,
),
@@ -162,7 +169,7 @@ class Tray {
final autoStartMenuItem = MenuItem.checkbox(
label: appLocalizations.autoLaunch,
onClick: (_) async {
appController.updateAutoLaunch();
systemAction.updateAutoLaunch();
},
checked: trayState.autoLaunch,
);
@@ -178,7 +185,7 @@ class Tray {
final exitMenuItem = MenuItem(
label: appLocalizations.exit,
onClick: (_) async {
await appController.handleExit();
await systemAction.handleExit();
},
);
menuItems.add(exitMenuItem);
+27 -21
View File
@@ -36,7 +36,7 @@ class Utils {
}
String getDateStringLast2(int value) {
var valueRaw = '0$value';
final valueRaw = '0$value';
return valueRaw.substring(valueRaw.length - 2);
}
@@ -45,7 +45,7 @@ class Utils {
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
final random = Random();
int length = minLength + random.nextInt(maxLength - minLength + 1);
final int length = minLength + random.nextInt(maxLength - minLength + 1);
String result = '';
for (int i = 0; i < length; i++) {
@@ -76,11 +76,11 @@ class Utils {
}
String getTimeDifference(DateTime dateTime) {
var currentDateTime = DateTime.now();
var difference = currentDateTime.difference(dateTime);
var inHours = difference.inHours;
var inMinutes = difference.inMinutes;
var inSeconds = difference.inSeconds;
final currentDateTime = DateTime.now();
final difference = currentDateTime.difference(dateTime);
final inHours = difference.inHours;
final inMinutes = difference.inMinutes;
final inSeconds = difference.inSeconds;
return '${getDateStringLast2(inHours)}:${getDateStringLast2(inMinutes)}:${getDateStringLast2(inSeconds)}';
}
@@ -102,7 +102,7 @@ class Utils {
Locale? getLocaleForString(String? localString) {
if (localString == null) return null;
var localSplit = localString.split('_');
final localSplit = localString.split('_');
if (localSplit.length == 1) {
return Locale(localSplit[0]);
}
@@ -157,25 +157,29 @@ class Utils {
}
int compareVersions(String version1, String version2) {
List<String> v1 = version1.split('+')[0].split('.');
List<String> v2 = version2.split('+')[0].split('.');
int major1 = int.parse(v1[0]);
int major2 = int.parse(v2[0]);
final List<String> v1 = version1.split('+')[0].split('.');
final List<String> v2 = version2.split('+')[0].split('.');
final int major1 = int.parse(v1[0]);
final int major2 = int.parse(v2[0]);
if (major1 != major2) {
return major1.compareTo(major2);
}
int minor1 = v1.length > 1 ? int.parse(v1[1]) : 0;
int minor2 = v2.length > 1 ? int.parse(v2[1]) : 0;
final int minor1 = v1.length > 1 ? int.parse(v1[1]) : 0;
final int minor2 = v2.length > 1 ? int.parse(v2[1]) : 0;
if (minor1 != minor2) {
return minor1.compareTo(minor2);
}
int patch1 = v1.length > 2 ? int.parse(v1[2]) : 0;
int patch2 = v2.length > 2 ? int.parse(v2[2]) : 0;
final int patch1 = v1.length > 2 ? int.parse(v1[2]) : 0;
final int patch2 = v2.length > 2 ? int.parse(v2[2]) : 0;
if (patch1 != patch2) {
return patch1.compareTo(patch2);
}
int build1 = version1.contains('+') ? int.parse(version1.split('+')[1]) : 0;
int build2 = version2.contains('+') ? int.parse(version2.split('+')[1]) : 0;
final int build1 = version1.contains('+')
? int.parse(version1.split('+')[1])
: 0;
final int build2 = version2.contains('+')
? int.parse(version2.split('+')[1])
: 0;
return build1.compareTo(build2);
}
@@ -295,7 +299,7 @@ class Utils {
}
Future<String?> getLocalIpAddress() async {
List<NetworkInterface> interfaces =
final List<NetworkInterface> interfaces =
await NetworkInterface.list(includeLoopback: false)
..sort((a, b) {
if (a.isWifi && !b.isWifi) return -1;
@@ -326,13 +330,15 @@ class Utils {
FutureOr<T> handleWatch<T>({
required Function function,
required void Function(T data, int elapsedMilliseconds) onWatch,
required void Function() onStart,
required void Function(T data, int elapsedMilliseconds) onEnd,
}) async {
if (kDebugMode && watchExecution) {
onStart();
final stopwatch = Stopwatch()..start();
final res = await function();
stopwatch.stop();
onWatch(res, stopwatch.elapsedMilliseconds);
onEnd(res, stopwatch.elapsedMilliseconds);
return res;
}
return await function();
+5 -2
View File
@@ -28,14 +28,14 @@ class Window {
}
await windowManager.ensureInitialized();
// kDebugMode ? Size(680, 580) :
WindowOptions windowOptions = WindowOptions(
final WindowOptions windowOptions = WindowOptions(
size: props.size,
minimumSize: const Size(380, 400),
);
if (!system.isMacOS || version > 10) {
await windowManager.setTitleBarStyle(TitleBarStyle.hidden);
}
await windowManager.setMaximizable(false);
await windowManager.setMaximizable(true);
await _windowPosition(props);
await windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.setPreventClose(true);
@@ -84,6 +84,9 @@ class Window {
Future<void> close() async {
await windowManager.close();
}
void forceExit() {
exit(0);
}
-1202
View File
File diff suppressed because it is too large Load Diff
+28 -17
View File
@@ -7,6 +7,7 @@ import 'package:fl_clash/core/core.dart';
import 'package:fl_clash/core/interface.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/foundation.dart' show visibleForTesting;
import 'package:flutter/services.dart';
import 'package:path/path.dart';
@@ -22,6 +23,14 @@ class CoreController {
}
}
@visibleForTesting
CoreController.test(this._interface);
@visibleForTesting
static void resetInstance() {
_instance = null;
}
factory CoreController() {
_instance ??= CoreController._internal();
return _instance!;
@@ -49,20 +58,22 @@ class CoreController {
continue;
}
final data = await rootBundle.load('assets/data/$geoFileName');
List<int> bytes = data.buffer.asUint8List();
final List<int> bytes = data.buffer.asUint8List();
await geoFile.writeAsBytes(bytes, flush: true);
}
} catch (e) {
exit(0);
commonPrint.log(
'Failed to initialize geo data: $e',
logLevel: LogLevel.error,
);
rethrow;
}
}
Future<bool> init(int version) async {
await initGeo();
final homeDirPath = await appPath.homeDirPath;
return await _interface.init(
InitParams(homeDir: homeDirPath, version: version),
);
return _interface.init(InitParams(homeDir: homeDirPath, version: version));
}
Future<void> shutdown(bool isUser) async {
@@ -86,7 +97,7 @@ class CoreController {
}
Future<String> updateConfig(UpdateParams updateParams) async {
return await _interface.updateConfig(updateParams);
return _interface.updateConfig(updateParams);
}
Future<String> setupConfig({
@@ -130,16 +141,16 @@ class CoreController {
return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();
}
void closeConnection(String id) {
_interface.closeConnection(id);
Future<void> closeConnection(String id) async {
await _interface.closeConnection(id);
}
void closeConnections() {
_interface.closeConnections();
Future<void> closeConnections() async {
await _interface.closeConnections();
}
void resetConnections() {
_interface.resetConnections();
Future<void> resetConnections() async {
await _interface.resetConnections();
}
Future<List<ExternalProvider>> getExternalProviders() async {
@@ -185,11 +196,11 @@ class CoreController {
}
Future<bool> startListener() async {
return await _interface.startListener();
return _interface.startListener();
}
Future<bool> stopListener() async {
return await _interface.stopListener();
return _interface.stopListener();
}
Future<Delay> getDelay(String url, String proxyName) async {
@@ -213,7 +224,7 @@ class CoreController {
Future<Traffic> getTraffic(bool onlyStatisticsProxy) async {
final trafficString = await _interface.getTraffic(onlyStatisticsProxy);
if (trafficString.isEmpty) {
return Traffic();
return const Traffic();
}
return Traffic.fromJson(json.decode(trafficString));
}
@@ -231,7 +242,7 @@ class CoreController {
onlyStatisticsProxy,
);
if (totalTrafficString.isEmpty) {
return Traffic();
return const Traffic();
}
return Traffic.fromJson(json.decode(totalTrafficString));
}
@@ -269,7 +280,7 @@ class CoreController {
}
Future<String> deleteFile(String path) async {
return await _interface.deleteFile(path);
return _interface.deleteFile(path);
}
}
+11 -13
View File
@@ -4,7 +4,6 @@ import 'dart:convert';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/foundation.dart';
mixin CoreInterface {
Future<bool> init(InitParams params);
@@ -94,15 +93,14 @@ abstract class CoreHandlerInterface with CoreInterface {
);
return null;
}
if (kDebugMode && watchExecution) {
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
}
return await utils.handleWatch(
function: () async {
return await invoke<T>(method: method, data: data, timeout: timeout);
onStart: () {
commonPrint.log('Invoke ${method.name} ${DateTime.now()} $data');
},
onWatch: (data, elapsedMilliseconds) {
function: () async {
return invoke<T>(method: method, data: data, timeout: timeout);
},
onEnd: (data, elapsedMilliseconds) {
commonPrint.log('Invoke ${method.name} ${elapsedMilliseconds}ms');
},
);
@@ -188,7 +186,7 @@ abstract class CoreHandlerInterface with CoreInterface {
);
return data != null
? ProxiesData.fromJson(data)
: ProxiesData(proxies: {}, all: []);
: const ProxiesData(proxies: {}, all: []);
}
@override
@@ -294,17 +292,17 @@ abstract class CoreHandlerInterface with CoreInterface {
}
@override
resetTraffic() {
FutureOr<void> resetTraffic() {
_invoke(method: ActionMethod.resetTraffic);
}
@override
startLog() {
FutureOr<void> startLog() {
_invoke(method: ActionMethod.startLog);
}
@override
stopLog() {
FutureOr<void> stopLog() {
_invoke<bool>(method: ActionMethod.stopLog);
}
@@ -328,7 +326,7 @@ abstract class CoreHandlerInterface with CoreInterface {
return await _invoke<String>(
method: ActionMethod.asyncTestDelay,
data: json.encode(delayParams),
timeout: Duration(seconds: 6),
timeout: const Duration(seconds: 6),
) ??
json.encode(Delay(name: proxyName, value: -1, url: url));
}
+23 -3
View File
@@ -1,10 +1,11 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/core.dart';
import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'interface.dart';
@@ -17,12 +18,17 @@ class CoreLib extends CoreHandlerInterface {
@override
Future<String> preload() async {
if (_connectedCompleter.isCompleted) {
return 'core is connected';
}
final res = await service?.init();
if (res?.isEmpty != true) {
return res ?? '';
}
_connectedCompleter.complete(true);
final syncRes = await service?.syncState(appController.sharedState);
final syncRes = await service?.syncState(
globalState.container.read(sharedStateProvider),
);
return syncRes ?? '';
}
@@ -32,7 +38,7 @@ class CoreLib extends CoreHandlerInterface {
}
@override
destroy() async {
FutureOr<bool> destroy() async {
return true;
}
@@ -45,6 +51,20 @@ class CoreLib extends CoreHandlerInterface {
return service?.shutdown() ?? true;
}
@override
Future<bool> startListener() async {
await super.startListener();
await service?.start();
return true;
}
@override
Future<bool> stopListener() async {
await super.stopListener();
await service?.stop();
return true;
}
@override
Future<T?> invoke<T>({
required ActionMethod method,
+55 -78
View File
@@ -8,13 +8,12 @@ import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/core.dart';
import 'interface.dart';
import 'transport.dart';
class CoreService extends CoreHandlerInterface {
static CoreService? _instance;
final Completer<ServerSocket> _serverCompleter = Completer();
Completer<Socket> _socketCompleter = Completer();
late final IPCCoreTransport _transport;
Completer<bool> _shutdownCompleter = Completer();
@@ -28,6 +27,9 @@ class CoreService extends CoreHandlerInterface {
}
CoreService._internal() {
_transport = IPCCoreTransport(
address: system.isWindows ? windowsPipeName : unixSocketPath,
);
_initServer();
}
@@ -44,52 +46,42 @@ class CoreService extends CoreHandlerInterface {
}
Future<void> _initServer() async {
final server = await retry(
task: () async {
try {
final address = !system.isWindows
? InternetAddress(unixSocketPath, type: InternetAddressType.unix)
: InternetAddress(localhost, type: InternetAddressType.IPv4);
await _deleteSocketFile();
final server = await ServerSocket.bind(address, 0, shared: true);
server.listen((socket) async {
await _attachSocket(socket);
});
return server;
} catch (_) {
return null;
}
},
retryIf: (server) => server == null,
);
if (server == null) {
exit(0);
}
_serverCompleter.complete(server);
}
await _transport.init();
Future<void> _attachSocket(Socket socket) async {
await _destroySocket();
_socketCompleter.complete(socket);
socket
_transport.onDisconnect = () {
_handleInvokeCrashEvent();
if (!_shutdownCompleter.isCompleted) {
_shutdownCompleter.complete(true);
}
};
_transport.dataStream
.transform(uint8ListToListIntConverter)
.transform(utf8.decoder)
.transform(LineSplitter())
.listen((data) async {
final dataJson = await data.trim().commonToJSON<dynamic>();
handleResult(ActionResult.fromJson(dataJson));
})
.onDone(() {
_handleInvokeCrashEvent();
if (!_shutdownCompleter.isCompleted) {
_shutdownCompleter.complete(true);
}
});
.listen(
(data) async {
try {
final dataJson = await data.trim().commonToJSON<dynamic>();
handleResult(ActionResult.fromJson(dataJson));
} catch (e) {
commonPrint.log(
'Failed to parse transport data: $e',
logLevel: LogLevel.error,
);
}
},
onError: (error) {
commonPrint.log(
'Transport data stream error: $error',
logLevel: LogLevel.error,
);
},
);
}
void _handleInvokeCrashEvent() {
coreEventManager.sendEvent(
CoreEvent(type: CoreEventType.crash, data: 'socket done'),
const CoreEvent(type: CoreEventType.crash, data: 'core done'),
);
}
@@ -97,17 +89,23 @@ class CoreService extends CoreHandlerInterface {
if (_process != null) {
await shutdown(false);
}
final serverSocket = await _serverCompleter.future;
final arg = system.isWindows
? '${serverSocket.port}'
: serverSocket.address.address;
if (system.isWindows && await system.checkIsAdmin()) {
final isSuccess = await request.startCoreByHelper(arg);
final isSuccess = await request.startCoreByHelper(_transport.address);
if (isSuccess) {
await _transport.connectionCompleter.future;
return;
}
}
_process = await Process.start(appPath.corePath, [arg]);
try {
_process = await Process.start(appPath.corePath, [_transport.address]);
} catch (e) {
commonPrint.log(
'Failed to start core process: $e',
logLevel: LogLevel.error,
);
_handleInvokeCrashEvent();
return;
}
_process?.stdout.listen((_) {});
_process?.stderr.listen((e) {
final error = utf8.decode(e);
@@ -115,51 +113,31 @@ class CoreService extends CoreHandlerInterface {
commonPrint.log(error, logLevel: LogLevel.warning);
}
});
await _socketCompleter.future;
await _transport.connectionCompleter.future;
}
@override
destroy() async {
final server = await _serverCompleter.future;
FutureOr<bool> destroy() async {
await shutdown(false);
await server.close();
await _deleteSocketFile();
await _transport.close();
return true;
}
Future<void> sendMessage(String message) async {
final socket = await _socketCompleter.future;
socket.writeln(message);
}
Future<void> _deleteSocketFile() async {
if (!system.isWindows) {
final file = File(unixSocketPath);
await file.safeDelete();
}
}
Future<void> _destroySocket() async {
if (_socketCompleter.isCompleted) {
final socket = await _socketCompleter.future;
_socketCompleter = Completer();
await socket.close();
}
await _transport.connectionCompleter.future;
_transport.send(message);
}
@override
shutdown(bool isUser) async {
if (!_socketCompleter.isCompleted && _process == null) {
return false;
}
Future<bool> shutdown(bool isUser) async {
_shutdownCompleter = Completer();
await _destroySocket();
_clearCompleter();
if (system.isWindows) {
await request.stopCoreByHelper();
}
_transport.disconnected();
_process?.kill();
_process = null;
_clearCompleter();
if (isUser) {
return _shutdownCompleter.future;
} else {
@@ -175,7 +153,6 @@ class CoreService extends CoreHandlerInterface {
@override
Future<String> preload() async {
await _serverCompleter.future;
await start();
return '';
}
@@ -202,7 +179,7 @@ class CoreService extends CoreHandlerInterface {
}
@override
Completer get completer => _socketCompleter;
Completer get completer => _transport.connectionCompleter;
}
final coreService = system.isDesktop ? CoreService() : null;
+106
View File
@@ -0,0 +1,106 @@
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:rust_api/rust_api.dart';
// ── Binary frame types (mirrors Rust ipc.rs) ────────────────────────────────
const _typeReady = 0x00;
const _typeConnected = 0x01;
const _typeDisconnected = 0x02;
const _typeData = 0x03;
const _typeError = 0x04;
class IPCCoreTransport {
final String address;
final StreamController<Uint8List> _dataController =
StreamController<Uint8List>();
StreamSubscription<Uint8List>? _subscription;
Completer<void> _completer = Completer<void>();
Completer<void> _readyCompleter = Completer<void>();
void Function()? onDisconnect;
IPCCoreTransport({required this.address});
Completer<void> get connectionCompleter => _completer;
Stream<Uint8List> get dataStream => _dataController.stream;
Future<void> init() async {
try {
final stream = restartIpcServer(name: address);
_subscription = stream.listen(
(data) {
if (data.isEmpty) return;
final type = data[0];
final payload = data.length > 1 ? data.sublist(1) : Uint8List(0);
switch (type) {
case _typeReady:
commonPrint.log('IPC Ready');
if (_readyCompleter.isCompleted) {
break;
}
_readyCompleter.complete();
break;
case _typeConnected:
commonPrint.log('IPC Connected');
if (_completer.isCompleted) {
break;
}
_completer.complete();
break;
case _typeDisconnected:
commonPrint.log('IPC Disconnected');
_completer = Completer<void>();
onDisconnect?.call();
break;
case _typeData:
_dataController.add(payload);
break;
case _typeError:
final msg = utf8.decode(payload);
commonPrint.log('IPC error: $msg', logLevel: LogLevel.error);
break;
default:
commonPrint.log(
'IPC unknown frame type: $type',
logLevel: LogLevel.warning,
);
}
},
onError: (error) {
commonPrint.log('IPC error: $error', logLevel: LogLevel.error);
},
cancelOnError: false,
);
await _readyCompleter.future;
} catch (e) {
commonPrint.log(
'Failed to start IPC server: $e',
logLevel: LogLevel.error,
);
rethrow;
}
}
void send(String message) {
sendIpcMessage(data: utf8.encode(message));
}
void disconnected() {
_completer = Completer<void>();
}
Future<void> close() async {
await _subscription?.cancel();
_subscription = null;
await stopIpcServer();
_readyCompleter = Completer<void>();
_completer = Completer<void>();
await _dataController.close();
}
}
+43
View File
@@ -0,0 +1,43 @@
part of 'database.dart';
class StringMapConverter extends TypeConverter<Map<String, String>, String> {
const StringMapConverter();
@override
Map<String, String> fromSql(String fromDb) {
return Map<String, String>.from(json.decode(fromDb));
}
@override
String toSql(Map<String, String> value) {
return json.encode(value);
}
}
class StringListConverter extends TypeConverter<List<String>, String> {
const StringListConverter();
@override
List<String> fromSql(String fromDb) {
return List<String>.from(json.decode(fromDb));
}
@override
String toSql(List<String> value) {
return json.encode(value.toList());
}
}
class StringSetConverter extends TypeConverter<Set<String>, String> {
const StringSetConverter();
@override
Set<String> fromSql(String fromDb) {
return Set<String>.from(json.decode(fromDb));
}
@override
String toSql(Set<String> value) {
return json.encode(value.toList());
}
}
+133 -7
View File
@@ -1,3 +1,4 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
@@ -8,21 +9,31 @@ import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
part 'converter.dart';
part 'generated/database.g.dart';
part 'groups.dart';
part 'icons.dart';
part 'links.dart';
part 'profiles.dart';
part 'rules.dart';
part 'scripts.dart';
@DriftDatabase(
tables: [Profiles, Scripts, Rules, ProfileRuleLinks],
daos: [ProfilesDao, ScriptsDao, RulesDao],
tables: [
Profiles,
Scripts,
Rules,
ProfileRuleLinks,
ProxyGroups,
IconRecords,
],
daos: [ProfilesDao, ScriptsDao, RulesDao, ProxyGroupsDao, IconRecordsDao],
)
class Database extends _$Database {
Database([QueryExecutor? executor]) : super(executor ?? _openConnection());
@override
int get schemaVersion => 1;
int get schemaVersion => 2;
static LazyDatabase _openConnection() {
return LazyDatabase(() async {
@@ -31,11 +42,84 @@ class Database extends _$Database {
});
}
@override
MigrationStrategy get migration {
return MigrationStrategy(
onUpgrade: (m, from, to) async {
if (from < 2) {
await m.createTable(proxyGroups);
await m.createTable(iconRecords);
await _resetOrders();
await _migrateRules(m);
}
},
beforeOpen: (details) async {
// final m = Migrator(this);
// await m.createTable(iconRecords);
// await _migrateRules(m);
// await m.deleteTable('proxy_groups');
// await m.createTable(proxyGroups);
},
);
}
Future<void> _migrateRules(Migrator m) async {
final tableInfo = await customSelect('PRAGMA table_info(rules)').get();
final columnNames = tableInfo
.map((row) => row.read<String>('name'))
.toList();
if (columnNames.isEmpty) {
await m.createTable(rules);
return;
} else if (columnNames.contains('rule_action')) {
return;
}
await customStatement(
'ALTER TABLE rules ADD COLUMN rule_action TEXT NOT NULL DEFAULT ""',
);
await customStatement('ALTER TABLE rules ADD COLUMN content TEXT');
await customStatement('ALTER TABLE rules ADD COLUMN rule_target TEXT');
await customStatement('ALTER TABLE rules ADD COLUMN rule_provider TEXT');
await customStatement('ALTER TABLE rules ADD COLUMN sub_rule TEXT');
await customStatement(
'ALTER TABLE rules ADD COLUMN no_resolve INTEGER NOT NULL DEFAULT 0',
);
await customStatement(
'ALTER TABLE rules ADD COLUMN src INTEGER NOT NULL DEFAULT 0',
);
final oldRows = await customSelect('SELECT id, value FROM rules').get();
for (final row in oldRows) {
final id = row.read<int>('id');
final value = row.read<String>('value');
final parsed = Rule.parse(value, id: id);
await customStatement(
'UPDATE rules SET rule_action = ?, content = ?, rule_target = ?, rule_provider = ?, sub_rule = ?, no_resolve = ?, src = ? WHERE id = ?',
[
parsed.ruleAction.name,
parsed.content,
parsed.ruleTarget,
parsed.ruleProvider,
parsed.subRule,
parsed.noResolve ? 1 : 0,
parsed.src ? 1 : 0,
id,
],
);
}
await customStatement('ALTER TABLE rules DROP COLUMN value');
await m.createIndex(idxRuleTarget);
}
Future<void> _resetOrders() async {
await rulesDao.resetOrders();
}
Future<void> restore(
List<Profile> profiles,
List<Script> scripts,
List<Rule> rules,
List<ProfileRuleLink> links, {
List<ProfileRuleLink> links,
List<ProxyGroup> proxyGroups, {
bool isOverride = false,
}) async {
if (profiles.isNotEmpty ||
@@ -51,9 +135,21 @@ class Database extends _$Database {
);
scriptsDao.setAllWithBatch(b, scripts);
rulesDao.restoreWithBatch(b, rules, links);
proxyGroupsDao.setAllWithBatch(null, b, proxyGroups);
});
}
}
Future<void> setProfileCustomData(
int profileId,
List<ProxyGroup> groups,
List<Rule> rules,
) async {
await batch((b) {
proxyGroupsDao.setAllWithBatch(profileId, b, groups);
rulesDao.setCustomRulesWithBatch(profileId, b, rules);
});
}
}
extension TableInfoExt<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
@@ -61,17 +157,47 @@ extension TableInfoExt<Tbl extends Table, Row> on TableInfo<Tbl, Row> {
Batch batch,
Iterable<Insertable<Row>> items, {
required Expression<bool> Function(Tbl tbl) deleteFilter,
bool preDelete = false,
}) async {
if (preDelete) {
batch.deleteWhere(this, deleteFilter);
}
batch.insertAllOnConflictUpdate(this, items);
batch.deleteWhere(this, deleteFilter);
if (!preDelete) {
batch.deleteWhere(this, deleteFilter);
}
}
Selectable<int?> get count {
final countExp = countAll();
final query = select().addColumns([countExp]);
return query.map((row) => row.read(countExp));
}
Future<int> remove(Expression<bool> Function(Tbl tbl) filter) async {
return await (delete()..where(filter)).go();
return (delete()..where(filter)).go();
}
Future<int> put(Insertable<Row> item) async {
return await insertOnConflictUpdate(item);
return insertOnConflictUpdate(item);
}
}
extension SimpleSelectStatementExt<T extends HasResultSet, D>
on SimpleSelectStatement<T, D> {
Selectable<int> get count {
final countExp = countAll();
final query = addColumns([countExp]);
return query.map((row) => row.read(countExp)!);
}
}
extension JoinedSelectStatementExt<T extends HasResultSet, D>
on JoinedSelectStatement<T, D> {
Selectable<int> get count {
final countExp = countAll();
addColumns([countExp]);
return map((row) => row.read(countExp)!);
}
}
File diff suppressed because it is too large Load Diff
+190
View File
@@ -0,0 +1,190 @@
part of 'database.dart';
@DataClassName('RawProxyGroup')
@TableIndex(
name: 'idx_profile_name_order',
columns: {#profileId, #name, #order},
)
class ProxyGroups extends Table {
@override
String get tableName => 'proxy_groups';
IntColumn get id => integer()();
IntColumn get profileId => integer().nullable().references(
Profiles,
#id,
onDelete: KeyAction.cascade,
)();
TextColumn get name => text()();
TextColumn get type => text()();
TextColumn get proxies =>
text().map(const StringListConverter()).nullable()();
TextColumn get use => text().map(const StringListConverter()).nullable()();
TextColumn get url => text().nullable()();
IntColumn get interval => integer().nullable()();
IntColumn get timeout => integer().nullable()();
IntColumn get maxFailedTimes => integer().nullable()();
BoolColumn get lazy => boolean().nullable()();
BoolColumn get disableUDP => boolean().nullable()();
TextColumn get filter => text().nullable()();
TextColumn get excludeFilter => text().nullable()();
TextColumn get excludeType => text().nullable()();
TextColumn get expectedStatus => text().nullable()();
BoolColumn get includeAll => boolean().nullable()();
BoolColumn get includeAllProxies => boolean().nullable()();
BoolColumn get includeAllProviders => boolean().nullable()();
BoolColumn get hidden => boolean().nullable()();
TextColumn get icon => text().nullable()();
TextColumn get order => text().nullable()();
@override
Set<Column> get primaryKey => {id};
}
@DriftAccessor(tables: [ProxyGroups])
class ProxyGroupsDao extends DatabaseAccessor<Database>
with _$ProxyGroupsDaoMixin {
ProxyGroupsDao(super.attachedDatabase);
Selectable<ProxyGroup> query(int profileId) {
final stmt = proxyGroups.select();
stmt.where((row) => row.profileId.equals(profileId));
stmt.orderBy([
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
]);
return stmt.map((item) => item.toProxyGroup());
}
Selectable<int> count(int profileId) {
final stmt = proxyGroups.select();
stmt.where((row) => row.profileId.equals(profileId));
stmt.orderBy([
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
]);
return stmt.count;
}
Future<int> order(
int profileId, {
required ProxyGroup proxyGroup,
required String order,
}) async {
return proxyGroups.insertOnConflictUpdate(
proxyGroup.toCompanion(profileId, order),
);
}
Future<void> renameProxies(
int profileId, {
required String oldName,
required String newName,
}) {
return customUpdate(
'UPDATE ${proxyGroups.entityName} '
'SET ${proxyGroups.proxies.name} = REPLACE(${proxyGroups.proxies.name}, ?, ?) '
'WHERE ${proxyGroups.profileId.name} = ?',
variables: [
Variable.withString('"$oldName"'),
Variable.withString('"$newName"'),
Variable.withInt(profileId),
],
);
}
void setAllWithBatch(
int? profileId,
Batch batch,
Iterable<ProxyGroup> proxyGroups,
) async {
final keys = indexing.generateNKeys(proxyGroups.length);
this.proxyGroups.setAll(
batch,
proxyGroups.mapIndexed(
(index, item) => item.toCompanion(profileId, keys[index]),
),
deleteFilter: (row) => profileId == null
? const Constant(true)
: row.profileId.equals(profileId),
preDelete: true,
);
}
}
extension RawProxyGroupExt on RawProxyGroup {
ProxyGroup toProxyGroup() {
return ProxyGroup(
profileId: profileId,
id: id,
name: name,
type: GroupType.parse(type),
proxies: proxies,
use: use,
url: url,
interval: interval,
timeout: timeout,
maxFailedTimes: maxFailedTimes,
lazy: lazy,
disableUDP: disableUDP,
filter: filter,
excludeFilter: excludeFilter,
excludeType: excludeType,
expectedStatus: expectedStatus,
includeAll: includeAll,
includeAllProxies: includeAllProxies,
includeAllProviders: includeAllProviders,
hidden: hidden,
icon: icon,
order: order,
);
}
}
extension ProxyGroupsCompanionExt on ProxyGroup {
ProxyGroupsCompanion toCompanion([int? profileId, String? order]) {
return ProxyGroupsCompanion.insert(
id: Value(id),
profileId: Value(this.profileId ?? profileId),
name: name,
type: type.value,
proxies: Value(proxies),
use: Value(use),
url: Value(url),
interval: Value(interval),
timeout: Value(timeout),
maxFailedTimes: Value(maxFailedTimes),
lazy: Value(lazy),
disableUDP: Value(disableUDP),
filter: Value(filter),
excludeFilter: Value(excludeFilter),
excludeType: Value(excludeType),
expectedStatus: Value(expectedStatus),
includeAll: Value(includeAll),
includeAllProxies: Value(includeAllProxies),
includeAllProviders: Value(includeAllProviders),
hidden: Value(hidden),
icon: Value(icon),
order: Value(order ?? this.order),
);
}
}
+87
View File
@@ -0,0 +1,87 @@
part of 'database.dart';
@DataClassName('IconRecord')
@TableIndex(name: 'last_accessed_url', columns: {#lastAccessed, #url})
class IconRecords extends Table {
@override
String get tableName => 'icon_records';
TextColumn get url => text()();
IntColumn get lastAccessed => integer()();
@override
Set<Column> get primaryKey => {url};
}
@DriftAccessor(tables: [IconRecords])
class IconRecordsDao extends DatabaseAccessor<Database>
with _$IconRecordsDaoMixin {
IconRecordsDao(super.attachedDatabase);
final int maxCapacity = 1000;
Future<IconRecord?> get(String url) async {
final now = DateTime.now().millisecondsSinceEpoch;
return transaction(() async {
final query = select(iconRecords)..where((t) => t.url.equals(url));
final record = await query.getSingleOrNull();
if (record != null) {
await (update(iconRecords)..where((t) => t.url.equals(url))).write(
IconRecordsCompanion(lastAccessed: Value(now)),
);
}
return record;
});
}
Future<void> putIfAbsent(String url) async {
final existing = await (select(
iconRecords,
)..where((t) => t.url.equals(url))).getSingleOrNull();
if (existing != null) return;
await put(url);
}
Future<void> put(String url) async {
final now = DateTime.now().millisecondsSinceEpoch;
await transaction(() async {
await into(iconRecords).insertOnConflictUpdate(
IconRecordsCompanion.insert(url: url, lastAccessed: now),
);
final count = await iconRecords.count.getSingle() ?? 0;
if (count > maxCapacity) {
final oldestRecords =
await (select(iconRecords)
..orderBy([
(t) => OrderingTerm(
expression: t.lastAccessed,
mode: OrderingMode.asc,
),
])
..limit(count - maxCapacity))
.get();
final oldestUrls = oldestRecords.map((e) => e.url).toList();
await (delete(iconRecords)..where((t) => t.url.isIn(oldestUrls))).go();
}
});
}
Future<List<IconRecord>> query(String query) {
return (select(iconRecords)
..where((t) => t.url.contains(query))
..orderBy([
(t) => OrderingTerm(
expression: t.lastAccessed,
mode: OrderingMode.desc,
),
]))
.get();
}
}
+2 -2
View File
@@ -40,13 +40,13 @@ extension RawProfileRuleLinkExt on RawProfileRuleLink {
}
extension ProfileRuleLinksCompanionExt on ProfileRuleLink {
ProfileRuleLinksCompanion toCompanion() {
ProfileRuleLinksCompanion toCompanion([String? order]) {
return ProfileRuleLinksCompanion.insert(
id: key,
ruleId: ruleId,
scene: Value(scene),
profileId: Value(profileId),
order: Value(order),
order: Value(order ?? this.order),
);
}
}
+6 -29
View File
@@ -57,7 +57,7 @@ class SubscriptionInfoConverter
class ProfilesDao extends DatabaseAccessor<Database> with _$ProfilesDaoMixin {
ProfilesDao(super.attachedDatabase);
Selectable<Profile> all() {
Selectable<Profile> query() {
final stmt = profiles.select();
stmt.orderBy([
(t) => OrderingTerm(expression: t.order, nulls: NullsOrder.last),
@@ -87,6 +87,11 @@ class ProfilesDao extends DatabaseAccessor<Database> with _$ProfilesDaoMixin {
batch.insertAllOnConflictUpdate(profiles, items);
}
Selectable<String> fileNames() {
final query = profiles.selectOnly()..addColumns([profiles.id]);
return query.map((row) => '${row.read(profiles.id)}.yaml');
}
void setAllWithBatch(Batch batch, Iterable<Profile> profiles) {
final List<ProfilesCompanion> items = [];
final List<int> ids = [];
@@ -99,34 +104,6 @@ class ProfilesDao extends DatabaseAccessor<Database> with _$ProfilesDaoMixin {
}
}
class StringMapConverter extends TypeConverter<Map<String, String>, String> {
const StringMapConverter();
@override
Map<String, String> fromSql(String fromDb) {
return Map<String, String>.from(json.decode(fromDb));
}
@override
String toSql(Map<String, String> value) {
return json.encode(value);
}
}
class StringSetConverter extends TypeConverter<Set<String>, String> {
const StringSetConverter();
@override
Set<String> fromSql(String fromDb) {
return Set<String>.from(json.decode(fromDb));
}
@override
String toSql(Set<String> value) {
return json.encode(value.toList());
}
}
extension RawProfilExt on RawProfile {
Profile toProfile() {
return Profile(
+162 -79
View File
@@ -1,13 +1,26 @@
part of 'database.dart';
@DataClassName('RawRule')
@TableIndex(name: 'idx_rule_target', columns: {#ruleTarget})
class Rules extends Table {
@override
String get tableName => 'rules';
IntColumn get id => integer()();
TextColumn get value => text()();
TextColumn get ruleAction => textEnum<RuleAction>()();
TextColumn get content => text().nullable()();
TextColumn get ruleTarget => text().nullable()();
TextColumn get ruleProvider => text().nullable()();
TextColumn get subRule => text().nullable()();
BoolColumn get noResolve => boolean().withDefault(const Constant(false))();
BoolColumn get src => boolean().withDefault(const Constant(false))();
@override
Set<Column> get primaryKey => {id};
@@ -17,19 +30,31 @@ class Rules extends Table {
class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
RulesDao(super.attachedDatabase);
Selectable<Rule> allGlobalAddedRules() {
return _get();
Selectable<Rule> queryGlobalAddedRules() {
return _query();
}
Selectable<Rule> allProfileAddedRules(int profileId) {
return _get(profileId: profileId, scene: RuleScene.added);
Selectable<Rule> queryProfileAddedRules(int profileId) {
return _query(profileId: profileId, scene: RuleScene.added);
}
Selectable<Rule> allProfileDisabledRules(int profileId) {
return _get(profileId: profileId, scene: RuleScene.disabled);
Selectable<Rule> queryProfileDisabledRules(int profileId) {
return _query(profileId: profileId, scene: RuleScene.disabled);
}
Selectable<Rule> allAddedRules(int profileId) {
Selectable<Rule> queryProfileCustomRules(int profileId) {
return _query(profileId: profileId, scene: RuleScene.custom);
}
Selectable<int> profileCustomRulesCount(int profileId) {
final query = _getSelectStatement(
profileId: profileId,
scene: RuleScene.custom,
);
return query.count;
}
Selectable<Rule> queryAddedRules(int profileId) {
final disabledIdsQuery = selectOnly(profileRuleLinks)
..addColumns([profileRuleLinks.ruleId])
..where(
@@ -65,6 +90,26 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
});
}
Future<void> resetOrders() async {
final stmt = profileRuleLinks.select();
stmt.orderBy([
(t) => OrderingTerm.asc(t.scene),
//v0.8.92 ordering desc
(t) => OrderingTerm.desc(t.order),
(t) => OrderingTerm.desc(t.id),
]);
final links = await stmt.map((item) => item.toLink()).get();
final keys = indexing.generateNKeys(links.length);
await batch((b) {
b.insertAllOnConflictUpdate(
profileRuleLinks,
links.mapIndexed((index, item) => item.toCompanion(keys[index])),
);
});
}
void restoreWithBatch(
Batch batch,
Iterable<Rule> rules,
@@ -76,9 +121,10 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
);
final ruleIds = rules.map((item) => item.id);
batch.deleteWhere(this.rules, (t) => t.id.isNotIn(ruleIds));
final keys = indexing.generateNKeys(links.length);
batch.insertAllOnConflictUpdate(
profileRuleLinks,
links.map((item) => item.toCompanion()),
links.mapIndexed((index, item) => item.toCompanion(keys[index])),
);
final linkKeys = links.map((item) => item.key);
batch.deleteWhere(profileRuleLinks, (t) => t.id.isNotIn(linkKeys));
@@ -96,20 +142,20 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
return _put(rule, profileId: profileId, scene: RuleScene.added);
}
Future<void> putProfileCustomRule(int profileId, Rule rule) {
return _put(rule, profileId: profileId, scene: RuleScene.custom);
}
Future<void> putProfileDisabledRule(int profileId, Rule rule) {
return _put(rule, profileId: profileId, scene: RuleScene.added);
}
Future<void> putGlobalRules(Iterable<Rule> rules) {
return _putAll(rules);
}
Future<void> setGlobalRules(Iterable<Rule> rules) {
return _set(rules);
void setCustomRulesWithBatch(int profileId, Batch b, Iterable<Rule> rules) {
_setWithBatch(b, rules, profileId: profileId, scene: RuleScene.custom);
}
Future<int> putDisabledLink(int profileId, int ruleId) async {
return await profileRuleLinks.insertOnConflictUpdate(
return profileRuleLinks.insertOnConflictUpdate(
ProfileRuleLink(
ruleId: ruleId,
profileId: profileId,
@@ -119,7 +165,7 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
}
Future<bool> delDisabledLink(int profileId, int ruleId) async {
return await profileRuleLinks.deleteOne(
return profileRuleLinks.deleteOne(
ProfileRuleLink(
profileId: profileId,
ruleId: ruleId,
@@ -132,7 +178,7 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
required int ruleId,
required String order,
}) async {
return await _order(ruleId: ruleId, order: order);
return _order(ruleId: ruleId, order: order);
}
Future<int> orderProfileAddedRule(
@@ -140,7 +186,7 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
required int ruleId,
required String order,
}) async {
return await _order(
return _order(
ruleId: ruleId,
order: order,
profileId: profileId,
@@ -148,7 +194,43 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
);
}
Selectable<Rule> _get({int? profileId, RuleScene? scene}) {
Future<int> orderProfileCustomRule(
int profileId, {
required int ruleId,
required String order,
}) async {
return _order(
ruleId: ruleId,
order: order,
profileId: profileId,
scene: RuleScene.custom,
);
}
Future<int> renameCustomRuleTarget(
int profileId, {
required String oldName,
required String newName,
}) {
final stmt = rules.update()
..where((t) => t.ruleTarget.equals(oldName))
..where(
(t) => t.id.isInQuery(
selectOnly(profileRuleLinks)
..addColumns([profileRuleLinks.ruleId])
..where(
profileRuleLinks.profileId.equals(profileId) &
profileRuleLinks.scene.equalsValue(RuleScene.custom),
),
),
);
return stmt.write(RulesCompanion(ruleTarget: Value(newName)));
}
JoinedSelectStatement<HasResultSet, dynamic> _getSelectStatement({
int? profileId,
RuleScene? scene,
}) {
final query = select(rules).join([
innerJoin(profileRuleLinks, profileRuleLinks.ruleId.equalsExp(rules.id)),
]);
@@ -160,10 +242,13 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
profileRuleLinks.scene.equalsValue(scene),
);
query.orderBy([
OrderingTerm.desc(profileRuleLinks.order),
OrderingTerm.desc(profileRuleLinks.id),
]);
query.orderBy([OrderingTerm.asc(profileRuleLinks.order)]);
return query;
}
Selectable<Rule> _query({int? profileId, RuleScene? scene}) {
final query = _getSelectStatement(profileId: profileId, scene: scene);
return query.map((row) {
return row.readTable(rules).toRule(row.read(profileRuleLinks.order));
@@ -184,7 +269,7 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
t.ruleId.equals(ruleId) &
t.scene.equalsValue(scene);
});
return await stmt.write(ProfileRuleLinksCompanion(order: Value(order)));
return stmt.write(ProfileRuleLinksCompanion(order: Value(order)));
}
Future<int> _put(Rule rule, {int? profileId, RuleScene? scene}) async {
@@ -193,11 +278,12 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
if (row == 0) {
return 0;
}
return await profileRuleLinks.insertOnConflictUpdate(
return profileRuleLinks.insertOnConflictUpdate(
ProfileRuleLink(
ruleId: rule.id,
profileId: profileId,
scene: scene,
order: rule.order,
).toCompanion(),
);
});
@@ -207,77 +293,74 @@ class RulesDao extends DatabaseAccessor<Database> with _$RulesDaoMixin {
await rules.deleteWhere((t) => t.id.isIn(ruleIds));
}
Future<void> _putAll(
void _setWithBatch(
Batch b,
Iterable<Rule> rules, {
int? profileId,
RuleScene? scene,
}) async {
await batch((b) {
b.insertAllOnConflictUpdate(
this.rules,
rules.map((item) => item.toCompanion()),
);
b.insertAllOnConflictUpdate(
profileRuleLinks,
rules.map(
(item) => ProfileRuleLink(
ruleId: item.id,
profileId: profileId,
scene: scene,
).toCompanion(),
),
);
});
}
b.insertAllOnConflictUpdate(
this.rules,
rules.map((item) => item.toCompanion()),
);
Future<void> _set(
Iterable<Rule> rules, {
int? profileId,
RuleScene? scene,
}) async {
await batch((b) {
b.insertAllOnConflictUpdate(
this.rules,
rules.map((item) => item.toCompanion()),
);
b.deleteWhere(
profileRuleLinks,
(t) =>
(profileId == null
? t.profileId.isNull()
: t.profileId.equals(profileId)) &
(scene == null ? const Constant(true) : t.scene.equalsValue(scene)),
);
b.deleteWhere(
profileRuleLinks,
(t) =>
(profileId == null
? t.profileId.isNull()
: t.profileId.equals(profileId)) &
(scene == null ? const Constant(true) : t.scene.equalsValue(scene)),
);
final keys = indexing.generateNKeys(rules.length);
b.insertAllOnConflictUpdate(
profileRuleLinks,
rules.map(
(item) => ProfileRuleLink(
ruleId: item.id,
profileId: profileId,
scene: scene,
).toCompanion(),
),
);
b.insertAllOnConflictUpdate(
profileRuleLinks,
rules.mapIndexed(
(index, item) => ProfileRuleLink(
ruleId: item.id,
profileId: profileId,
scene: scene,
).toCompanion(keys[index]),
),
);
b.deleteWhere(this.rules, (r) {
final linkedIds = selectOnly(profileRuleLinks);
linkedIds.addColumns([profileRuleLinks.ruleId]);
return r.id.isNotInQuery(linkedIds);
});
b.deleteWhere(this.rules, (r) {
final linkedIds = selectOnly(profileRuleLinks);
linkedIds.addColumns([profileRuleLinks.ruleId]);
return r.id.isNotInQuery(linkedIds);
});
}
}
extension RawRuleExt on RawRule {
Rule toRule([String? order]) {
return Rule(id: id, value: value, order: order);
return Rule(
id: id,
ruleAction: ruleAction,
content: content,
ruleTarget: ruleTarget,
ruleProvider: ruleProvider,
subRule: subRule,
noResolve: noResolve,
src: src,
order: order,
);
}
}
extension RulesCompanionExt on Rule {
RulesCompanion toCompanion() {
return RulesCompanion.insert(id: Value(id), value: value);
return RulesCompanion.insert(
id: Value(id),
ruleAction: ruleAction,
content: Value(content),
ruleTarget: Value(ruleTarget),
ruleProvider: Value(ruleProvider),
subRule: Value(subRule),
noResolve: Value(noResolve),
src: Value(src),
);
}
}
+6 -1
View File
@@ -19,7 +19,7 @@ class Scripts extends Table {
class ScriptsDao extends DatabaseAccessor<Database> with _$ScriptsDaoMixin {
ScriptsDao(super.attachedDatabase);
Selectable<Script> all() {
Selectable<Script> query() {
return scripts.select().map((item) => item.toScript());
}
@@ -29,6 +29,11 @@ class ScriptsDao extends DatabaseAccessor<Database> with _$ScriptsDaoMixin {
return stmt.map((it) => it.toScript());
}
Selectable<String> fileNames() {
final query = scripts.selectOnly()..addColumns([scripts.id]);
return query.map((row) => '${row.read(scripts.id)}.js');
}
Future<void> setAll(Iterable<Script> scripts) async {
await batch((b) async {
await setAllWithBatch(b, scripts);
+135 -25
View File
@@ -2,7 +2,7 @@
import 'dart:io';
import 'package:fl_clash/common/color.dart';
import 'package:fl_clash/common/context.dart';
import 'package:fl_clash/common/system.dart';
import 'package:fl_clash/views/dashboard/widgets/widgets.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -37,27 +37,36 @@ const desktopPlatforms = [
SupportPlatform.Windows,
];
enum GroupType {
Selector,
URLTest,
Fallback,
LoadBalance,
Relay;
enum GroupName { GLOBAL }
static GroupType parseProfileType(String type) {
return switch (type) {
'url-test' => URLTest,
'select' => Selector,
enum GroupType {
@JsonValue('select')
Selector('select'),
@JsonValue('url-test')
URLTest('url-test'),
@JsonValue('fallback')
Fallback('fallback'),
@JsonValue('load-balance')
LoadBalance('load-balance'),
@JsonValue('relay')
Relay('relay');
final String value;
const GroupType(this.value);
static GroupType parse(String type) {
return switch (type.toLowerCase()) {
'url-test' || 'urltest' => URLTest,
'select' || 'selector' => Selector,
'fallback' => Fallback,
'load-balance' => LoadBalance,
'load-balance' || 'loadbalance' => LoadBalance,
'relay' => Relay,
String() => throw UnimplementedError(),
};
}
}
enum GroupName { GLOBAL, Proxy, Auto, Fallback }
extension GroupTypeExtension on GroupType {
static List<String> get valueList =>
GroupType.values.map((e) => e.toString().split('.').last).toList();
@@ -71,8 +80,6 @@ extension GroupTypeExtension on GroupType {
if (index == -1) return null;
return GroupType.values[index];
}
String get value => GroupTypeExtension.valueList[index];
}
enum UsedProxy { GLOBAL, DIRECT, REJECT }
@@ -91,13 +98,14 @@ enum ViewMode { mobile, laptop, desktop }
enum LogLevel { debug, info, warning, error, silent }
extension LogLevelExt on LogLevel {
Color? get color {
Color? color(BuildContext context) {
final colorScheme = Theme.of(context).colorScheme;
return switch (this) {
LogLevel.silent => Colors.grey.shade700,
LogLevel.debug => Colors.grey.shade400,
LogLevel.silent => colorScheme.outline,
LogLevel.debug => colorScheme.onSurfaceVariant,
LogLevel.info => null,
LogLevel.warning => Colors.orangeAccent.darken(),
LogLevel.error => Colors.redAccent,
LogLevel.warning => colorScheme.tertiary,
LogLevel.error => colorScheme.error,
};
}
}
@@ -282,6 +290,8 @@ enum FunctionTag {
autoScrollToEnd,
loadedProvider,
saveSharedFile,
removeProxy,
suspend,
}
enum DashboardWidget {
@@ -311,7 +321,7 @@ enum DashboardWidget {
const DashboardWidget(this.widget, {this.platforms = SupportPlatform.values});
static DashboardWidget getDashboardWidget(GridItem gridItem) {
final dashboardWidgets = DashboardWidget.values;
const dashboardWidgets = DashboardWidget.values;
final index = dashboardWidgets.indexWhere(
(item) => item.widget == gridItem,
);
@@ -394,6 +404,47 @@ extension RuleActionExt on RuleAction {
RuleAction.IP_SUFFIX,
RuleAction.RULE_SET,
].contains(this);
String getDesc(BuildContext context) {
final appLocalizations = context.appLocalizations;
return switch (this) {
RuleAction.DOMAIN => appLocalizations.ruleActionDomainDesc,
RuleAction.DOMAIN_SUFFIX => appLocalizations.ruleActionDomainSuffixDesc,
RuleAction.DOMAIN_KEYWORD => appLocalizations.ruleActionDomainKeywordDesc,
RuleAction.DOMAIN_REGEX => appLocalizations.ruleActionDomainRegexDesc,
RuleAction.GEOSITE => appLocalizations.ruleActionGeositeDesc,
RuleAction.IP_CIDR => appLocalizations.ruleActionIpCidrDesc,
RuleAction.IP_CIDR6 => appLocalizations.ruleActionIpCidr6Desc,
RuleAction.IP_SUFFIX => appLocalizations.ruleActionIpSuffixDesc,
RuleAction.IP_ASN => appLocalizations.ruleActionIpAsnDesc,
RuleAction.GEOIP => appLocalizations.ruleActionGeoipDesc,
RuleAction.SRC_GEOIP => appLocalizations.ruleActionSrcGeoipDesc,
RuleAction.SRC_IP_ASN => appLocalizations.ruleActionSrcIpAsnDesc,
RuleAction.SRC_IP_CIDR => appLocalizations.ruleActionSrcIpCidrDesc,
RuleAction.SRC_IP_SUFFIX => appLocalizations.ruleActionSrcIpSuffixDesc,
RuleAction.DST_PORT => appLocalizations.ruleActionDstPortDesc,
RuleAction.SRC_PORT => appLocalizations.ruleActionSrcPortDesc,
RuleAction.IN_PORT => appLocalizations.ruleActionInPortDesc,
RuleAction.IN_TYPE => appLocalizations.ruleActionInTypeDesc,
RuleAction.IN_USER => appLocalizations.ruleActionInUserDesc,
RuleAction.IN_NAME => appLocalizations.ruleActionInNameDesc,
RuleAction.PROCESS_PATH => appLocalizations.ruleActionProcessPathDesc,
RuleAction.PROCESS_PATH_REGEX =>
appLocalizations.ruleActionProcessPathRegexDesc,
RuleAction.PROCESS_NAME => appLocalizations.ruleActionProcessNameDesc,
RuleAction.PROCESS_NAME_REGEX =>
appLocalizations.ruleActionProcessNameRegexDesc,
RuleAction.UID => appLocalizations.ruleActionUidDesc,
RuleAction.NETWORK => appLocalizations.ruleActionNetworkDesc,
RuleAction.DSCP => appLocalizations.ruleActionDscpDesc,
RuleAction.RULE_SET => appLocalizations.ruleActionRuleSetDesc,
RuleAction.AND => appLocalizations.ruleActionAndDesc,
RuleAction.OR => appLocalizations.ruleActionOrDesc,
RuleAction.NOT => appLocalizations.ruleActionNotDesc,
RuleAction.SUB_RULE => appLocalizations.ruleActionSubRuleDesc,
RuleAction.MATCH => appLocalizations.ruleActionMatchDesc,
};
}
}
enum OverrideRuleType { override, added }
@@ -402,10 +453,23 @@ enum OverwriteType {
// none,
standard,
script,
// custom,
custom,
}
enum RuleTarget { DIRECT, REJECT, MATCH }
enum RuleTarget {
DIRECT,
REJECT;
static Set<String> get baseTargets =>
RuleTarget.values.map((item) => item.name).toSet();
// static bool isBaseRuleTarget(String? target) {
// return RuleTarget.values.indexWhere(
// (item) => item.name == target?.toUpperCase(),
// ) !=
// -1;
// }
}
enum RestoreStrategy { compatible, override }
@@ -419,8 +483,54 @@ enum ScrollPositionCacheKey { tools, profiles, proxiesList, proxiesTabList }
enum QueryTag { proxies, access }
enum LoadingTag { profiles, backup_restore, access, proxies }
enum LoadingTag {
profiles,
backup_restore,
access,
proxies,
batteryOptimization,
}
enum CoreStatus { connecting, connected, disconnected }
enum RuleScene { added, disabled, custom }
enum ItemPosition {
start,
middle,
end,
startAndEnd;
static ItemPosition get(int index, int length) {
ItemPosition position = ItemPosition.middle;
if (length == 1) {
position = ItemPosition.startAndEnd;
} else if (index == length - 1) {
position = ItemPosition.end;
} else if (index == 0) {
position = ItemPosition.start;
}
return position;
}
static ItemPosition calculateVisualPosition<T>(
int currentIndex,
List<T> items,
Set<T> deletedItems,
) {
final currentItem = items[currentIndex];
if (deletedItems.contains(currentItem)) {
return ItemPosition.middle;
}
final int visualLength = items.length - deletedItems.length;
if (visualLength <= 0) return ItemPosition.middle;
int deletedCountBeforeMe = 0;
for (int i = 0; i < currentIndex; i++) {
if (deletedItems.contains(items[i])) {
deletedCountBeforeMe++;
}
}
final int visualIndex = currentIndex - deletedCountBeforeMe;
return ItemPosition.get(visualIndex, visualLength);
}
}
+153 -48
View File
@@ -1,21 +1,27 @@
library;
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/clash_config.dart';
import 'package:fl_clash/models/state.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/input.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
final ruleItemHeight =
globalState.measure.bodyLargeHeight +
globalState.measure.bodyMediumHeight +
12;
class RuleItem extends StatelessWidget {
final bool isSelected;
final bool isEditing;
final Rule rule;
final bool hasMatch;
final void Function() onSelected;
final void Function(Rule rule) onEdit;
final bool Function(Rule rule)? checkInvalidHandler;
const RuleItem({
super.key,
@@ -23,19 +29,129 @@ class RuleItem extends StatelessWidget {
required this.rule,
required this.onSelected,
required this.onEdit,
this.checkInvalidHandler,
this.isEditing = false,
this.hasMatch = false,
});
VM2<bool, Color?> _checkInvalid(BuildContext context) {
if (rule.ruleAction != RuleAction.SUB_RULE) {
final ruleTarget = rule.ruleTarget ?? '';
if (ruleTarget.toUpperCase() == 'DIRECT') {
return VM2(
false,
Colors.green.harmonizeWith(context.colorScheme.primary),
);
} else if (ruleTarget.toUpperCase() == 'REJECT') {
return VM2(
false,
Colors.orange.harmonizeWith(context.colorScheme.primary),
);
} else if (hasMatch && ruleTarget.toUpperCase() == 'MATCH') {
return VM2(false, context.colorScheme.tertiary);
}
}
bool invalid = true;
if (checkInvalidHandler != null) {
invalid = checkInvalidHandler!(rule);
}
return VM2(
invalid,
invalid ? context.colorScheme.error : context.colorScheme.tertiary,
);
}
Widget _buildInfoWidget(BuildContext context) {
return CommonMinIconButtonTheme(
child: IconButton(
onPressed: () {
globalState.showMessage(
message: TextSpan(
text: rule.targetErrorTip(
context.appLocalizations.invalidSubRule(rule.subRule ?? ''),
context.appLocalizations.invalidPolicy(rule.ruleTarget ?? ''),
),
),
);
},
icon: Icon(Icons.info, size: 16.ap, color: context.colorScheme.error),
),
);
}
@override
Widget build(BuildContext context) {
return CommonSelectedListItem(
final vm2 = _checkInvalid(context);
final invalid = vm2.a;
return SelectedDecorationListItem(
minVerticalPadding: 0,
isSelected: isSelected,
isEditing: isEditing,
horizontalTitleGap: 0,
invalid: invalid,
onSelected: () {
onSelected();
},
title: Text(
rule.value,
style: context.textTheme.bodyMedium?.toJetBrainsMono,
title: Center(
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Builder(
builder: (context) {
final style = DefaultTextStyle.of(
context,
).style.toJetBrainsMono;
return Column(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
rule.ruleAction.name,
style: style.copyWith(
fontSize: context.textTheme.bodyLarge?.fontSize,
),
),
Flexible(
child: Builder(
builder: (context) {
return TooltipText(
text: Text(
rule.realContent ?? '',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: style.copyWith(
fontSize:
context.textTheme.bodyMedium?.fontSize,
color: style.color?.opacity60,
),
),
);
},
),
),
],
);
},
),
),
Row(
mainAxisSize: MainAxisSize.min,
children: [
if (invalid) _buildInfoWidget(context),
if (rule.realTarget != null)
Text(
rule.realTarget!,
style: context.textTheme.bodyMedium?.toJetBrainsMono
.copyWith(color: vm2.b),
),
],
),
],
),
),
onPressed: () {
onEdit(rule);
@@ -58,30 +174,19 @@ class RuleStatusItem extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.symmetric(vertical: 4),
child: CommonCard(
padding: EdgeInsets.zero,
radius: 18,
type: CommonCardType.filled,
onPressed: () {
onChange(!status);
},
child: ListTile(
minTileHeight: 0,
minVerticalPadding: 0,
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
trailing: Switch(value: status, onChanged: onChange),
title: Text(rule.value),
),
return DecorationListItem(
title: TooltipText(
text: Text(
rule.rawValue,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: context.textTheme.bodyMedium?.toJetBrainsMono,
),
),
trailing: Switch(value: status, onChanged: onChange),
onPressed: () {
onChange(!status);
},
);
}
}
@@ -115,14 +220,15 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
...RuleTarget.values.map(
(item) => DropdownMenuEntry(value: item.name, label: item.name),
),
const DropdownMenuEntry(value: 'MATCH', label: 'MATCH'),
];
if (widget.rule != null) {
final parsedRule = ParsedRule.parseString(widget.rule!.value);
_ruleAction = parsedRule.ruleAction;
_contentController.text = parsedRule.content ?? '';
_ruleTargetController.text = parsedRule.ruleTarget ?? '';
_noResolve = parsedRule.noResolve;
_src = parsedRule.src;
final rule = widget.rule;
if (rule != null) {
_ruleAction = rule.ruleAction;
_contentController.text = rule.content ?? '';
_ruleTargetController.text = rule.ruleTarget ?? '';
_noResolve = rule.noResolve;
_src = rule.src;
return;
}
_ruleAction = RuleAction.addedRuleActions.first;
@@ -144,21 +250,20 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
if (res == false) {
return;
}
final parsedRule = ParsedRule(
final rule = Rule(
id: widget.rule?.id ?? -1,
ruleAction: _ruleAction,
content: _contentController.text,
ruleTarget: _ruleTargetController.text,
noResolve: _noResolve,
src: _src,
);
final rule = widget.rule != null
? widget.rule!.copyWith(value: parsedRule.value)
: Rule.value(parsedRule.value);
Navigator.of(context).pop(rule);
}
@override
Widget build(BuildContext context) {
final appLocalizations = context.appLocalizations;
return CommonDialog(
title: widget.rule != null
? appLocalizations.editRule
@@ -172,7 +277,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
child: DropdownMenuTheme(
data: DropdownMenuThemeData(
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(),
border: const OutlineInputBorder(),
labelStyle: context.textTheme.bodyLarge?.copyWith(
overflow: TextOverflow.ellipsis,
),
@@ -202,7 +307,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
},
child: Text(_ruleAction.value),
),
SizedBox(height: 24),
const SizedBox(height: 24),
TextFormField(
keyboardType: TextInputType.text,
onFieldSubmitted: (_) {
@@ -222,7 +327,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
return null;
},
),
SizedBox(height: 24),
const SizedBox(height: 24),
FormField<String>(
validator: (_) {
if (_ruleTargetController.text.isEmpty) {
@@ -246,7 +351,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
},
),
if (_ruleAction.hasParams) ...[
SizedBox(height: 20),
const SizedBox(height: 20),
Wrap(
spacing: 8,
children: [
@@ -254,7 +359,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
radius: 8,
isSelected: _src,
child: Padding(
padding: EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
@@ -273,7 +378,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
radius: 8,
isSelected: _noResolve,
child: Padding(
padding: EdgeInsets.symmetric(
padding: const EdgeInsets.symmetric(
horizontal: 8,
vertical: 8,
),
@@ -291,7 +396,7 @@ class _AddOrEditRuleDialogState extends State<AddOrEditRuleDialog> {
],
),
],
SizedBox(height: 20),
const SizedBox(height: 20),
],
);
},
+284 -121
View File
@@ -38,23 +38,35 @@ class MessageLookup extends MessageLookupByLibrary {
static String m6(count) =>
"${Intl.plural(count, one: '1 hour ago', other: '${count} hours ago')}";
static String m7(count) =>
static String m7(target) => "${target} is an invalid policy";
static String m8(proxyName) => "${proxyName} is an invalid proxy";
static String m9(providerName) =>
"${providerName} is an invalid proxy provider";
static String m10(subRule) => "${subRule} is an invalid SUB_RULE";
static String m11(appName) =>
"1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check ${appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.";
static String m12(count) =>
"${Intl.plural(count, one: '1 minute ago', other: '${count} minutes ago')}";
static String m8(count) =>
static String m13(count) =>
"${Intl.plural(count, one: '1 month ago', other: '${count} months ago')}";
static String m9(label) => "No ${label} yet";
static String m14(label) => "No ${label} yet";
static String m10(label) => "${label} must be a number";
static String m15(label) => "${label} must be a number";
static String m11(label) => "${label} must be between 1024 and 49151";
static String m16(label) => "${label} must be between 1024 and 49151";
static String m12(count) => "${count} items have been selected";
static String m17(count) => "${count} items have been selected";
static String m13(label) => "${label} must be a url";
static String m18(label) => "${label} must be a url";
static String m14(count) =>
static String m19(count) =>
"${Intl.plural(count, one: '1 year ago', other: '${count} years ago')}";
final messages = _notInlinedMessages(_notInlinedMessages);
@@ -82,11 +94,17 @@ class MessageLookup extends MessageLookupByLibrary {
"action_view": MessageLookupByLibrary.simpleMessage("Show/Hide"),
"add": MessageLookupByLibrary.simpleMessage("Add"),
"addProfile": MessageLookupByLibrary.simpleMessage("Add Profile"),
"addRule": MessageLookupByLibrary.simpleMessage("Add rule"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
"Attach on the original rules",
"addProxies": MessageLookupByLibrary.simpleMessage("Add proxies"),
"addProxyGroup": MessageLookupByLibrary.simpleMessage("Add proxy group"),
"addProxyProviders": MessageLookupByLibrary.simpleMessage(
"Add proxy providers",
),
"addRule": MessageLookupByLibrary.simpleMessage("Add rule"),
"addSsid": MessageLookupByLibrary.simpleMessage("Add SSID"),
"addedRules": MessageLookupByLibrary.simpleMessage("Added rules"),
"additionalParameters": MessageLookupByLibrary.simpleMessage(
"Additional parameters",
),
"address": MessageLookupByLibrary.simpleMessage("Address"),
"addressHelp": MessageLookupByLibrary.simpleMessage(
"WebDAV server address",
@@ -94,21 +112,13 @@ class MessageLookup extends MessageLookupByLibrary {
"addressTip": MessageLookupByLibrary.simpleMessage(
"Please enter a valid WebDAV address",
),
"adminAutoLaunch": MessageLookupByLibrary.simpleMessage(
"Admin auto launch",
),
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage(
"Boot up by using admin mode",
),
"advancedConfig": MessageLookupByLibrary.simpleMessage(
"Advanced configuration",
),
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage(
"Provide diverse configuration options",
),
"ago": MessageLookupByLibrary.simpleMessage(" Ago"),
"agree": MessageLookupByLibrary.simpleMessage("Agree"),
"allApps": MessageLookupByLibrary.simpleMessage("All apps"),
"allowBypass": MessageLookupByLibrary.simpleMessage(
"Allow applications to bypass VPN",
),
@@ -123,9 +133,6 @@ class MessageLookup extends MessageLookupByLibrary {
"appAccessControl": MessageLookupByLibrary.simpleMessage(
"App access control",
),
"appDesc": MessageLookupByLibrary.simpleMessage(
"Processing app related settings",
),
"appendSystemDns": MessageLookupByLibrary.simpleMessage(
"Append System DNS",
),
@@ -136,6 +143,7 @@ class MessageLookup extends MessageLookupByLibrary {
"applicationDesc": MessageLookupByLibrary.simpleMessage(
"Modify application related settings",
),
"authorized": MessageLookupByLibrary.simpleMessage("Authorized"),
"auto": MessageLookupByLibrary.simpleMessage("Auto"),
"autoCheckUpdate": MessageLookupByLibrary.simpleMessage(
"Auto check updates",
@@ -176,6 +184,14 @@ class MessageLookup extends MessageLookupByLibrary {
"basicConfigDesc": MessageLookupByLibrary.simpleMessage(
"Modify the basic configuration globally",
),
"basicInfo": MessageLookupByLibrary.simpleMessage("Basic info"),
"basicStrategy": MessageLookupByLibrary.simpleMessage("Basic strategy"),
"batteryOptimizationDesc": MessageLookupByLibrary.simpleMessage(
"To ensure background operation, please disable battery optimization for this app. Tap to go to settings.",
),
"batteryOptimizationStatusTip": MessageLookupByLibrary.simpleMessage(
"Affected by the system, this status may not always be accurate.",
),
"bind": MessageLookupByLibrary.simpleMessage("Bind"),
"blacklistMode": MessageLookupByLibrary.simpleMessage("Blacklist mode"),
"bypassDomain": MessageLookupByLibrary.simpleMessage("Bypass domain"),
@@ -186,18 +202,13 @@ class MessageLookup extends MessageLookupByLibrary {
"The cache is corrupt. Do you want to clear it?",
),
"cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
"cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage(
"Cancel filter system app",
),
"cancelSelectAll": MessageLookupByLibrary.simpleMessage(
"Cancel select all",
),
"checkError": MessageLookupByLibrary.simpleMessage("Check error"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("Check for updates"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage(
"The current application is already the latest version",
),
"checking": MessageLookupByLibrary.simpleMessage("Checking..."),
"clearData": MessageLookupByLibrary.simpleMessage("Clear Data"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("Export clipboard"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("Clipboard import"),
@@ -205,16 +216,25 @@ class MessageLookup extends MessageLookupByLibrary {
"colorSchemes": MessageLookupByLibrary.simpleMessage("Color schemes"),
"columns": MessageLookupByLibrary.simpleMessage("Columns"),
"compatible": MessageLookupByLibrary.simpleMessage("Compatibility mode"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage(
"Opening it will lose part of its application ability and gain the support of full amount of Clash.",
"configDataDetected": MessageLookupByLibrary.simpleMessage(
"Data detected in configuration",
),
"confirm": MessageLookupByLibrary.simpleMessage("Confirm"),
"confirmClearAllData": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to clear all data?",
),
"confirmDeleteProxyGroup": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to delete the current proxy group?",
),
"confirmExitWindow": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to exit the current window?",
),
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to force crash the core?",
),
"confirmOverwriteTip": MessageLookupByLibrary.simpleMessage(
"Existing data will be overwritten after confirmation",
),
"connected": MessageLookupByLibrary.simpleMessage("Connected"),
"connecting": MessageLookupByLibrary.simpleMessage("Connecting..."),
"connection": MessageLookupByLibrary.simpleMessage("Connection"),
@@ -223,8 +243,10 @@ class MessageLookup extends MessageLookupByLibrary {
"View current connections data",
),
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity:"),
"contactMe": MessageLookupByLibrary.simpleMessage("Contact me"),
"content": MessageLookupByLibrary.simpleMessage("Content"),
"contentNotEmpty": MessageLookupByLibrary.simpleMessage(
"Content cannot be empty",
),
"contentScheme": MessageLookupByLibrary.simpleMessage("Content"),
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage(
"Control global added rules",
@@ -236,10 +258,6 @@ class MessageLookup extends MessageLookupByLibrary {
"copyLink": MessageLookupByLibrary.simpleMessage("Copy link"),
"copySuccess": MessageLookupByLibrary.simpleMessage("Copy success"),
"core": MessageLookupByLibrary.simpleMessage("Core"),
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"Core configuration change detected",
),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"coreStatus": MessageLookupByLibrary.simpleMessage("Core status"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"crashTest": MessageLookupByLibrary.simpleMessage("Crash test"),
@@ -248,17 +266,21 @@ class MessageLookup extends MessageLookupByLibrary {
"When enabled, automatically uploads crash logs without sensitive information when the app crashes",
),
"create": MessageLookupByLibrary.simpleMessage("Create"),
"createProfile": MessageLookupByLibrary.simpleMessage("Create Profile"),
"creationTime": MessageLookupByLibrary.simpleMessage("Creation time"),
"custom": MessageLookupByLibrary.simpleMessage("Custom"),
"cut": MessageLookupByLibrary.simpleMessage("Cut"),
"dark": MessageLookupByLibrary.simpleMessage("Dark"),
"dashboard": MessageLookupByLibrary.simpleMessage("Dashboard"),
"dataChangedSave": MessageLookupByLibrary.simpleMessage(
"Data changes detected, do you want to save?",
),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"This app uses Firebase Crashlytics to collect crash information to improve app stability.\nThe collected data includes device information and crash details, but does not contain personal sensitive data.\nYou can disable this feature in settings.",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage(
"Data Collection Notice",
),
"days": MessageLookupByLibrary.simpleMessage("Days"),
"daysAgo": m0,
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
"Default nameserver",
@@ -266,10 +288,8 @@ class MessageLookup extends MessageLookupByLibrary {
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage(
"For resolving DNS server",
),
"defaultSort": MessageLookupByLibrary.simpleMessage("Sort by default"),
"defaultText": MessageLookupByLibrary.simpleMessage("Default"),
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
"delayTest": MessageLookupByLibrary.simpleMessage("Delay Test"),
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
"deleteMultipTip": m1,
@@ -293,6 +313,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Developer mode is enabled.",
),
"direct": MessageLookupByLibrary.simpleMessage("Direct"),
"disableUDP": MessageLookupByLibrary.simpleMessage("Disable UDP"),
"disclaimer": MessageLookupByLibrary.simpleMessage("Disclaimer"),
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.",
@@ -301,9 +322,6 @@ class MessageLookup extends MessageLookupByLibrary {
"discoverNewVersion": MessageLookupByLibrary.simpleMessage(
"Discover the new version",
),
"discovery": MessageLookupByLibrary.simpleMessage(
"Discovery a new version",
),
"dnsDesc": MessageLookupByLibrary.simpleMessage(
"Update DNS related settings",
),
@@ -318,19 +336,29 @@ class MessageLookup extends MessageLookupByLibrary {
"editGlobalRules": MessageLookupByLibrary.simpleMessage(
"Edit global rules",
),
"editProxy": MessageLookupByLibrary.simpleMessage("Edit proxy"),
"editProxyGroup": MessageLookupByLibrary.simpleMessage("Edit proxy group"),
"editRule": MessageLookupByLibrary.simpleMessage("Edit rule"),
"editSsid": MessageLookupByLibrary.simpleMessage("Edit SSID"),
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("English"),
"enableOverride": MessageLookupByLibrary.simpleMessage("Enable override"),
"entries": MessageLookupByLibrary.simpleMessage(" entries"),
"exclude": MessageLookupByLibrary.simpleMessage("Hidden from recent tasks"),
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"When the app is in the background, the app is hidden from the recent task",
),
"excludeProxyFilter": MessageLookupByLibrary.simpleMessage(
"Exclude proxy filter",
),
"excludeSsids": MessageLookupByLibrary.simpleMessage("Exclude SSIDs"),
"excludeSsidsDesc": MessageLookupByLibrary.simpleMessage(
"When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
),
"excludeType": MessageLookupByLibrary.simpleMessage("Exclude type"),
"existsTip": m5,
"exit": MessageLookupByLibrary.simpleMessage("Exit"),
"expand": MessageLookupByLibrary.simpleMessage("Standard"),
"expirationTime": MessageLookupByLibrary.simpleMessage("Expiration time"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("Expected status"),
"exportFile": MessageLookupByLibrary.simpleMessage("Export file"),
"exportLogs": MessageLookupByLibrary.simpleMessage("Export logs"),
"exportSuccess": MessageLookupByLibrary.simpleMessage("Export Success"),
@@ -343,9 +371,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"externalFetch": MessageLookupByLibrary.simpleMessage("External fetch"),
"externalLink": MessageLookupByLibrary.simpleMessage("External link"),
"externalResources": MessageLookupByLibrary.simpleMessage(
"External resources",
),
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip filter"),
"fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip range"),
"fallback": MessageLookupByLibrary.simpleMessage("Fallback"),
@@ -359,9 +384,6 @@ class MessageLookup extends MessageLookupByLibrary {
"fileIsUpdate": MessageLookupByLibrary.simpleMessage(
"The file has been modified. Do you want to save the changes?",
),
"filterSystemApp": MessageLookupByLibrary.simpleMessage(
"Filter system app",
),
"findProcessMode": MessageLookupByLibrary.simpleMessage("Find process"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
"There is a certain performance loss after opening",
@@ -370,13 +392,8 @@ class MessageLookup extends MessageLookupByLibrary {
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to force restart the core?",
),
"fourColumns": MessageLookupByLibrary.simpleMessage("Four columns"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("FruitSalad"),
"general": MessageLookupByLibrary.simpleMessage("General"),
"generalDesc": MessageLookupByLibrary.simpleMessage(
"Modify general settings",
),
"geoData": MessageLookupByLibrary.simpleMessage("GeoData"),
"geodataLoader": MessageLookupByLibrary.simpleMessage(
"Geo Low Memory Mode",
),
@@ -384,9 +401,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Enabling will use the Geo low memory loader",
),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip code"),
"getOriginRules": MessageLookupByLibrary.simpleMessage(
"Get original rules",
),
"global": MessageLookupByLibrary.simpleMessage("Global"),
"go": MessageLookupByLibrary.simpleMessage("Go"),
"goDownload": MessageLookupByLibrary.simpleMessage("Go to download"),
@@ -396,6 +410,7 @@ class MessageLookup extends MessageLookupByLibrary {
"hasCacheChange": MessageLookupByLibrary.simpleMessage(
"Do you want to cache the changes?",
),
"hideFromList": MessageLookupByLibrary.simpleMessage("Hide from list"),
"host": MessageLookupByLibrary.simpleMessage("Host"),
"hostsDesc": MessageLookupByLibrary.simpleMessage("Add Hosts"),
"hotkeyConflict": MessageLookupByLibrary.simpleMessage("Hotkey conflict"),
@@ -405,22 +420,41 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage(
"Use keyboard to control applications",
),
"hours": MessageLookupByLibrary.simpleMessage("Hours"),
"hoursAgo": m6,
"icon": MessageLookupByLibrary.simpleMessage("Icon"),
"iconConfiguration": MessageLookupByLibrary.simpleMessage(
"Icon configuration",
),
"iconRecords": MessageLookupByLibrary.simpleMessage("Icon records"),
"iconStyle": MessageLookupByLibrary.simpleMessage("Icon style"),
"iconUrl": MessageLookupByLibrary.simpleMessage("Icon URL"),
"ignoreBatteryOptimization": MessageLookupByLibrary.simpleMessage(
"Ignore Battery Optimization",
),
"import": MessageLookupByLibrary.simpleMessage("Import"),
"importFile": MessageLookupByLibrary.simpleMessage("Import from file"),
"importFromURL": MessageLookupByLibrary.simpleMessage("Import from URL"),
"importUrl": MessageLookupByLibrary.simpleMessage("Import from URL"),
"includeAllProxies": MessageLookupByLibrary.simpleMessage(
"Include all proxies",
),
"includeAllProxiesTip": MessageLookupByLibrary.simpleMessage(
"Import all proxies not containing proxy groups, additional proxy groups can be added below",
),
"includeAllProxyProviders": MessageLookupByLibrary.simpleMessage(
"Include all proxy providers",
),
"includeAllProxyProvidersTip": MessageLookupByLibrary.simpleMessage(
"When enabled, it will override the imported proxy providers",
),
"infiniteTime": MessageLookupByLibrary.simpleMessage("Long term effective"),
"init": MessageLookupByLibrary.simpleMessage("Init"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage(
"Please enter the correct hotkey",
),
"inputProxyGroupName": MessageLookupByLibrary.simpleMessage(
"Input proxy group name",
),
"inputRuleContent": MessageLookupByLibrary.simpleMessage(
"Input rule content",
),
"intelligentSelected": MessageLookupByLibrary.simpleMessage(
"Intelligent selection",
),
@@ -430,6 +464,10 @@ class MessageLookup extends MessageLookupByLibrary {
"invalidBackupFile": MessageLookupByLibrary.simpleMessage(
"Invalid backup file",
),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
"When turned on it will be able to receive IPv6 traffic",
@@ -438,7 +476,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Allow IPv6 inbound",
),
"ja": MessageLookupByLibrary.simpleMessage("Japanese"),
"just": MessageLookupByLibrary.simpleMessage("Just"),
"justNow": MessageLookupByLibrary.simpleMessage("Just now"),
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage(
"Tcp keep alive interval",
@@ -455,6 +492,19 @@ class MessageLookup extends MessageLookupByLibrary {
"localBackupDesc": MessageLookupByLibrary.simpleMessage(
"Backup local data to local",
),
"locationPermission": MessageLookupByLibrary.simpleMessage(
"Location Permission",
),
"locationPermissionDeniedMessage": MessageLookupByLibrary.simpleMessage(
"Location permission was denied, so the current Wi-Fi name cannot be obtained. Please open location permission manually in system settings.",
),
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
),
"locationPermissionGuide": m11,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"Location Permission Required",
),
"log": MessageLookupByLibrary.simpleMessage("Log"),
"logLevel": MessageLookupByLibrary.simpleMessage("LogLevel"),
"logcat": MessageLookupByLibrary.simpleMessage("Logcat"),
@@ -469,6 +519,8 @@ class MessageLookup extends MessageLookupByLibrary {
"Used for UWP loopback unlocking",
),
"loose": MessageLookupByLibrary.simpleMessage("Loose"),
"matchSourceIp": MessageLookupByLibrary.simpleMessage("Match source IP"),
"maxFailedTimes": MessageLookupByLibrary.simpleMessage("Max failed times"),
"memoryInfo": MessageLookupByLibrary.simpleMessage("Memory info"),
"messageTest": MessageLookupByLibrary.simpleMessage("Message test"),
"messageTestTip": MessageLookupByLibrary.simpleMessage(
@@ -479,16 +531,13 @@ class MessageLookup extends MessageLookupByLibrary {
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
"Modify the default system exit event",
),
"minutes": MessageLookupByLibrary.simpleMessage("Minutes"),
"minutesAgo": m7,
"minutesAgo": m12,
"mixedPort": MessageLookupByLibrary.simpleMessage("Mixed Port"),
"mode": MessageLookupByLibrary.simpleMessage("Mode"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("Monochrome"),
"months": MessageLookupByLibrary.simpleMessage("Months"),
"monthsAgo": m8,
"monthsAgo": m13,
"more": MessageLookupByLibrary.simpleMessage("More"),
"name": MessageLookupByLibrary.simpleMessage("Name"),
"nameSort": MessageLookupByLibrary.simpleMessage("Sort by name"),
"nameserver": MessageLookupByLibrary.simpleMessage("Nameserver"),
"nameserverDesc": MessageLookupByLibrary.simpleMessage(
"For resolving domain",
@@ -509,27 +558,22 @@ class MessageLookup extends MessageLookupByLibrary {
"networkException": MessageLookupByLibrary.simpleMessage(
"Network exception, please check your connection and try again",
),
"networkRequestException": MessageLookupByLibrary.simpleMessage(
"Network request exception, please try again later.",
),
"networkSpeed": MessageLookupByLibrary.simpleMessage("Network speed"),
"networkType": MessageLookupByLibrary.simpleMessage("Network type"),
"neutralScheme": MessageLookupByLibrary.simpleMessage("Neutral"),
"noData": MessageLookupByLibrary.simpleMessage("No data"),
"noHotKey": MessageLookupByLibrary.simpleMessage("No HotKey"),
"noIcon": MessageLookupByLibrary.simpleMessage("None"),
"noInfo": MessageLookupByLibrary.simpleMessage("No info"),
"noLongerRemind": MessageLookupByLibrary.simpleMessage(
"Don\'t remind again",
),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"),
"noNetwork": MessageLookupByLibrary.simpleMessage("No network"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("No network APP"),
"noProxy": MessageLookupByLibrary.simpleMessage("No proxy"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"Please create a profile or add a valid profile",
),
"noRecords": MessageLookupByLibrary.simpleMessage("No records"),
"noResolve": MessageLookupByLibrary.simpleMessage("No resolve IP"),
"noResolveHostname": MessageLookupByLibrary.simpleMessage(
"No resolve hostname",
),
"none": MessageLookupByLibrary.simpleMessage("none"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"The current proxy group cannot be selected.",
@@ -537,19 +581,20 @@ class MessageLookup extends MessageLookupByLibrary {
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"No profile, Please add a profile",
),
"nullTip": m9,
"numberTip": m10,
"oneColumn": MessageLookupByLibrary.simpleMessage("One column"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage(
"Only third-party apps",
"nullTip": m14,
"numberTip": m15,
"onDemand": MessageLookupByLibrary.simpleMessage("On Demand"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage(
"Configure the program running state for specific scenarios",
),
"onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"),
"onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage(
"Only statistics proxy",
),
"onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage(
"When turned on, only statistics proxy traffic",
),
"optional": MessageLookupByLibrary.simpleMessage("Optional"),
"options": MessageLookupByLibrary.simpleMessage("Options"),
"other": MessageLookupByLibrary.simpleMessage("Other"),
"otherContributors": MessageLookupByLibrary.simpleMessage(
@@ -557,20 +602,11 @@ class MessageLookup extends MessageLookupByLibrary {
),
"outboundMode": MessageLookupByLibrary.simpleMessage("Outbound mode"),
"override": MessageLookupByLibrary.simpleMessage("Override"),
"overrideDesc": MessageLookupByLibrary.simpleMessage(
"Override Proxy related config",
),
"overrideDns": MessageLookupByLibrary.simpleMessage("Override Dns"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Turning it on will override the DNS options in the profile",
),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
"Does not take effect in script mode",
),
"overrideMode": MessageLookupByLibrary.simpleMessage("Override mode"),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
"Override the original rule",
),
"overrideScript": MessageLookupByLibrary.simpleMessage("Override script"),
"overwriteTypeCustom": MessageLookupByLibrary.simpleMessage("Custom"),
"overwriteTypeCustomDesc": MessageLookupByLibrary.simpleMessage(
@@ -588,9 +624,6 @@ class MessageLookup extends MessageLookupByLibrary {
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"Please enter the admin password",
),
"pleaseUploadFile": MessageLookupByLibrary.simpleMessage(
"Please upload file",
),
"pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage(
"Please upload a valid QR code",
),
@@ -598,10 +631,11 @@ class MessageLookup extends MessageLookupByLibrary {
"portConflictTip": MessageLookupByLibrary.simpleMessage(
"Please enter a different port",
),
"portTip": m11,
"portTip": m16,
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
"Prioritize the use of DOH\'s http/3",
),
"prerequisites": MessageLookupByLibrary.simpleMessage("Prerequisites"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage(
"Please press the keyboard.",
),
@@ -622,9 +656,6 @@ class MessageLookup extends MessageLookupByLibrary {
"profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage(
"Please input the profile name",
),
"profileParseErrorDesc": MessageLookupByLibrary.simpleMessage(
"profile parse error",
),
"profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage(
"Please input a valid profile URL",
),
@@ -636,29 +667,51 @@ class MessageLookup extends MessageLookupByLibrary {
"project": MessageLookupByLibrary.simpleMessage("Project"),
"providers": MessageLookupByLibrary.simpleMessage("Providers"),
"proxies": MessageLookupByLibrary.simpleMessage("Proxies"),
"proxiesSetting": MessageLookupByLibrary.simpleMessage("Proxies setting"),
"proxiesEmpty": MessageLookupByLibrary.simpleMessage("Proxies is empty"),
"proxyChains": MessageLookupByLibrary.simpleMessage("Proxy chains"),
"proxyDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"Detected selected proxies are abnormal",
),
"proxyFilter": MessageLookupByLibrary.simpleMessage("Proxy filter"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("Proxy group"),
"proxyGroupDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"Detected current proxy group is abnormal",
),
"proxyGroupEmpty": MessageLookupByLibrary.simpleMessage(
"Proxy group is empty",
),
"proxyGroupNameDuplicate": MessageLookupByLibrary.simpleMessage(
"Proxy group name is duplicate",
),
"proxyGroupNameEmpty": MessageLookupByLibrary.simpleMessage(
"Proxy group name cannot be empty",
),
"proxyNameserver": MessageLookupByLibrary.simpleMessage("Proxy nameserver"),
"proxyNameserverDesc": MessageLookupByLibrary.simpleMessage(
"Domain for resolving proxy nodes",
),
"proxyPort": MessageLookupByLibrary.simpleMessage("ProxyPort"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Set the Clash listening port",
"proxyProviderDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"Detected selected proxy providers are abnormal",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("Proxy providers"),
"proxyProvidersEmpty": MessageLookupByLibrary.simpleMessage(
"Proxy providers is empty",
),
"proxyProvidersNotEmpty": MessageLookupByLibrary.simpleMessage(
"Proxy providers cannot be empty",
),
"proxyType": MessageLookupByLibrary.simpleMessage("Proxy type"),
"pruneCache": MessageLookupByLibrary.simpleMessage("Prune cache"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("Pure black mode"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR code"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage(
"Scan QR code to obtain profile",
),
"quickFill": MessageLookupByLibrary.simpleMessage("Quick fill"),
"rainbowScheme": MessageLookupByLibrary.simpleMessage("Rainbow"),
"redirPort": MessageLookupByLibrary.simpleMessage("Redir Port"),
"redo": MessageLookupByLibrary.simpleMessage("redo"),
"regExp": MessageLookupByLibrary.simpleMessage("RegExp"),
"reload": MessageLookupByLibrary.simpleMessage("Reload"),
"remote": MessageLookupByLibrary.simpleMessage("Remote"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
"Backup local data to WebDAV",
@@ -723,16 +776,112 @@ class MessageLookup extends MessageLookupByLibrary {
"routeMode_config": MessageLookupByLibrary.simpleMessage("Use config"),
"ru": MessageLookupByLibrary.simpleMessage("Russian"),
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
"ruleActionAndDesc": MessageLookupByLibrary.simpleMessage(
"Logical rule AND",
),
"ruleActionDomainDesc": MessageLookupByLibrary.simpleMessage(
"Match full domain",
),
"ruleActionDomainKeywordDesc": MessageLookupByLibrary.simpleMessage(
"Match domain keyword",
),
"ruleActionDomainRegexDesc": MessageLookupByLibrary.simpleMessage(
"Wildcard match, only supports * and ? wildcards",
),
"ruleActionDomainSuffixDesc": MessageLookupByLibrary.simpleMessage(
"Match domain suffix",
),
"ruleActionDscpDesc": MessageLookupByLibrary.simpleMessage(
"Match DSCP mark (tproxy udp inbound only)",
),
"ruleActionDstPortDesc": MessageLookupByLibrary.simpleMessage(
"Match request target port range",
),
"ruleActionGeoipDesc": MessageLookupByLibrary.simpleMessage(
"Match IP\'s country code",
),
"ruleActionGeositeDesc": MessageLookupByLibrary.simpleMessage(
"Match domains within Geosite",
),
"ruleActionInNameDesc": MessageLookupByLibrary.simpleMessage(
"Match inbound name",
),
"ruleActionInPortDesc": MessageLookupByLibrary.simpleMessage(
"Match inbound port",
),
"ruleActionInTypeDesc": MessageLookupByLibrary.simpleMessage(
"Match inbound type",
),
"ruleActionInUserDesc": MessageLookupByLibrary.simpleMessage(
"Match inbound username, supports multiple usernames separated by /",
),
"ruleActionIpAsnDesc": MessageLookupByLibrary.simpleMessage(
"Match IP\'s ASN",
),
"ruleActionIpCidr6Desc": MessageLookupByLibrary.simpleMessage(
"Match IP address range, IP-CIDR6 is just an alias",
),
"ruleActionIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"Match IP address range",
),
"ruleActionIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"Match IP suffix range",
),
"ruleActionMatchDesc": MessageLookupByLibrary.simpleMessage(
"Match all requests, no conditions needed",
),
"ruleActionNetworkDesc": MessageLookupByLibrary.simpleMessage(
"Match TCP or UDP",
),
"ruleActionNotDesc": MessageLookupByLibrary.simpleMessage(
"Logical rule NOT",
),
"ruleActionOrDesc": MessageLookupByLibrary.simpleMessage("Logical rule OR"),
"ruleActionProcessNameDesc": MessageLookupByLibrary.simpleMessage(
"Match using process name, matches package name on Android",
),
"ruleActionProcessNameRegexDesc": MessageLookupByLibrary.simpleMessage(
"Match using process name regex, matches package name on Android",
),
"ruleActionProcessPathDesc": MessageLookupByLibrary.simpleMessage(
"Match using full process path",
),
"ruleActionProcessPathRegexDesc": MessageLookupByLibrary.simpleMessage(
"Match using process path regex",
),
"ruleActionRuleSetDesc": MessageLookupByLibrary.simpleMessage(
"Reference rule set, requires rule-providers configuration",
),
"ruleActionSrcGeoipDesc": MessageLookupByLibrary.simpleMessage(
"Match source IP\'s country code",
),
"ruleActionSrcIpAsnDesc": MessageLookupByLibrary.simpleMessage(
"Match source IP\'s ASN",
),
"ruleActionSrcIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"Match source IP address range",
),
"ruleActionSrcIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"Match source IP suffix range",
),
"ruleActionSrcPortDesc": MessageLookupByLibrary.simpleMessage(
"Match request source port range",
),
"ruleActionSubRuleDesc": MessageLookupByLibrary.simpleMessage(
"Match to sub-rule, pay attention to the use of parentheses",
),
"ruleActionUidDesc": MessageLookupByLibrary.simpleMessage(
"Match Linux USER ID",
),
"ruleEmpty": MessageLookupByLibrary.simpleMessage("Rule is empty"),
"ruleName": MessageLookupByLibrary.simpleMessage("Rule name"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"),
"ruleSet": MessageLookupByLibrary.simpleMessage("Rule set"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("Rule target"),
"save": MessageLookupByLibrary.simpleMessage("Save"),
"saveChanges": MessageLookupByLibrary.simpleMessage(
"Do you want to save the changes?",
),
"saveTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to save?",
),
"script": MessageLookupByLibrary.simpleMessage("Script"),
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
"Script mode, use external extension scripts, provide one-click override configuration capability",
@@ -740,8 +889,21 @@ class MessageLookup extends MessageLookupByLibrary {
"search": MessageLookupByLibrary.simpleMessage("Search"),
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
"selectProxies": MessageLookupByLibrary.simpleMessage("Select proxies"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage(
"Select proxy providers",
),
"selectRuleSet": MessageLookupByLibrary.simpleMessage(
"Please select rule set",
),
"selectSplitStrategy": MessageLookupByLibrary.simpleMessage(
"Please select split strategy",
),
"selectSubRule": MessageLookupByLibrary.simpleMessage(
"Please select sub rule",
),
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
"selectedCountTitle": m12,
"selectedCountTitle": m17,
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
"show": MessageLookupByLibrary.simpleMessage("Show"),
"shrink": MessageLookupByLibrary.simpleMessage("Shrink"),
@@ -757,6 +919,11 @@ class MessageLookup extends MessageLookupByLibrary {
"specialProxy": MessageLookupByLibrary.simpleMessage("Special proxy"),
"specialRules": MessageLookupByLibrary.simpleMessage("special rules"),
"speedStatistics": MessageLookupByLibrary.simpleMessage("Speed statistics"),
"splitStrategy": MessageLookupByLibrary.simpleMessage("Split strategy"),
"splitStrategyNotEmpty": MessageLookupByLibrary.simpleMessage(
"Split strategy cannot be empty",
),
"ssidsEmpty": MessageLookupByLibrary.simpleMessage("SSIDs is empty"),
"stackMode": MessageLookupByLibrary.simpleMessage("Stack mode"),
"standard": MessageLookupByLibrary.simpleMessage("Standard"),
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
@@ -772,11 +939,15 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Style"),
"subRule": MessageLookupByLibrary.simpleMessage("Sub rule"),
"subRuleEmpty": MessageLookupByLibrary.simpleMessage("Sub rule is empty"),
"subRuleNotEmpty": MessageLookupByLibrary.simpleMessage(
"Sub rule cannot be empty",
),
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
"suspended": MessageLookupByLibrary.simpleMessage("Suspended..."),
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
"system": MessageLookupByLibrary.simpleMessage("System"),
"systemApp": MessageLookupByLibrary.simpleMessage("System APP"),
"systemFont": MessageLookupByLibrary.simpleMessage("System font"),
"systemProxy": MessageLookupByLibrary.simpleMessage("System proxy"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
"Attach HTTP proxy to VpnService",
@@ -786,11 +957,14 @@ class MessageLookup extends MessageLookupByLibrary {
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"Effective only in mobile view",
),
"tapToAuthorize": MessageLookupByLibrary.simpleMessage("Tap to authorize"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP concurrent"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
"Enabling it will allow TCP concurrency",
),
"testInterval": MessageLookupByLibrary.simpleMessage("Test interval"),
"testUrl": MessageLookupByLibrary.simpleMessage("Test url"),
"testWhenUsed": MessageLookupByLibrary.simpleMessage("Test when used"),
"textScale": MessageLookupByLibrary.simpleMessage("Text Scaling"),
"theme": MessageLookupByLibrary.simpleMessage("Theme"),
"themeColor": MessageLookupByLibrary.simpleMessage("Theme color"),
@@ -798,9 +972,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Set dark mode,adjust the color",
),
"themeMode": MessageLookupByLibrary.simpleMessage("Theme mode"),
"threeColumns": MessageLookupByLibrary.simpleMessage("Three columns"),
"tight": MessageLookupByLibrary.simpleMessage("Tight"),
"time": MessageLookupByLibrary.simpleMessage("Time"),
"timeout": MessageLookupByLibrary.simpleMessage("Timeout"),
"tip": MessageLookupByLibrary.simpleMessage("tip"),
"toggle": MessageLookupByLibrary.simpleMessage("Toggle"),
"tonalSpotScheme": MessageLookupByLibrary.simpleMessage("TonalSpot"),
@@ -813,10 +987,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"turnOff": MessageLookupByLibrary.simpleMessage("Turn Off"),
"turnOn": MessageLookupByLibrary.simpleMessage("Turn On"),
"twoColumns": MessageLookupByLibrary.simpleMessage("Two columns"),
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"unable to update current profile",
),
"undo": MessageLookupByLibrary.simpleMessage("undo"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("Unified delay"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
@@ -833,7 +1003,7 @@ class MessageLookup extends MessageLookupByLibrary {
"urlDesc": MessageLookupByLibrary.simpleMessage(
"Obtain profile through URL",
),
"urlTip": m13,
"urlTip": m18,
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"),
"value": MessageLookupByLibrary.simpleMessage("Value"),
@@ -842,15 +1012,9 @@ class MessageLookup extends MessageLookupByLibrary {
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"VPN configuration change detected",
),
"vpnDesc": MessageLookupByLibrary.simpleMessage(
"Modify VPN related settings",
),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
"Auto routes all system traffic through VpnService",
),
"vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage(
"Attach HTTP proxy to VpnService",
),
"vpnTip": MessageLookupByLibrary.simpleMessage(
"Changes take effect after restarting the VPN",
),
@@ -858,8 +1022,7 @@ class MessageLookup extends MessageLookupByLibrary {
"WebDAV configuration",
),
"whitelistMode": MessageLookupByLibrary.simpleMessage("Whitelist mode"),
"years": MessageLookupByLibrary.simpleMessage("Years"),
"yearsAgo": m14,
"yearsAgo": m19,
"zh_CN": MessageLookupByLibrary.simpleMessage("Simplified Chinese"),
};
}
+254 -90
View File
@@ -34,21 +34,32 @@ class MessageLookup extends MessageLookupByLibrary {
static String m6(count) => "${count}時間前";
static String m7(count) => "${count}分前";
static String m7(target) => "${target} は無効なポリシーです";
static String m8(count) => "${count}ヶ月前";
static String m8(proxyName) => "${proxyName} は無効なプロキシです";
static String m9(label) => "まだ${label}はありません";
static String m9(providerName) => "${providerName} は無効なプロキシプロバイダーです";
static String m10(label) => "${label}は数字でなければなりません";
static String m10(subRule) => "${subRule} は無効なSUB_RULEです";
static String m11(label) => "${label} は 1024 から 49151 の間でなければなりません";
static String m11(appName) =>
"1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check ${appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.";
static String m12(count) => "${count} 項目が選択されています";
static String m12(count) => "${count}分前";
static String m13(label) => "${label}はURLである必要があります";
static String m13(count) => "${count}ヶ月前";
static String m14(count) => "${count}年前";
static String m14(label) => "まだ${label}はありません";
static String m15(label) => "${label}は数字でなければなりません";
static String m16(label) => "${label} は 1024 から 49151 の間でなければなりません";
static String m17(count) => "${count} 項目が選択されています";
static String m18(label) => "${label}はURLである必要があります";
static String m19(count) => "${count}年前";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -73,19 +84,19 @@ class MessageLookup extends MessageLookupByLibrary {
"action_view": MessageLookupByLibrary.simpleMessage("表示/非表示"),
"add": MessageLookupByLibrary.simpleMessage("追加"),
"addProfile": MessageLookupByLibrary.simpleMessage("プロファイルを追加"),
"addProxies": MessageLookupByLibrary.simpleMessage("プロキシを追加"),
"addProxyGroup": MessageLookupByLibrary.simpleMessage("プロキシグループを追加"),
"addProxyProviders": MessageLookupByLibrary.simpleMessage("プロキシプロバイダーを追加"),
"addRule": MessageLookupByLibrary.simpleMessage("ルールを追加"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage("元のルールに追加"),
"addSsid": MessageLookupByLibrary.simpleMessage("SSIDを追加"),
"addedRules": MessageLookupByLibrary.simpleMessage("追加ルール"),
"additionalParameters": MessageLookupByLibrary.simpleMessage("追加パラメータ"),
"address": MessageLookupByLibrary.simpleMessage("アドレス"),
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAVサーバーアドレス"),
"addressTip": MessageLookupByLibrary.simpleMessage("有効なWebDAVアドレスを入力"),
"adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理者自動起動"),
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage("管理者モードで起動"),
"advancedConfig": MessageLookupByLibrary.simpleMessage("高度な設定"),
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage("多様な設定を提供"),
"ago": MessageLookupByLibrary.simpleMessage("前"),
"agree": MessageLookupByLibrary.simpleMessage("同意"),
"allApps": MessageLookupByLibrary.simpleMessage("全アプリ"),
"allowBypass": MessageLookupByLibrary.simpleMessage("アプリがVPNをバイパスすることを許可"),
"allowBypassDesc": MessageLookupByLibrary.simpleMessage(
"有効化すると一部アプリがVPNをバイパス",
@@ -94,13 +105,13 @@ class MessageLookup extends MessageLookupByLibrary {
"allowLanDesc": MessageLookupByLibrary.simpleMessage("LAN経由でのプロキシアクセスを許可"),
"app": MessageLookupByLibrary.simpleMessage("アプリ"),
"appAccessControl": MessageLookupByLibrary.simpleMessage("アプリアクセス制御"),
"appDesc": MessageLookupByLibrary.simpleMessage("アプリ関連設定の処理"),
"appendSystemDns": MessageLookupByLibrary.simpleMessage("システムDNSを追加"),
"appendSystemDnsTip": MessageLookupByLibrary.simpleMessage(
"設定にシステムDNSを強制的に追加します",
),
"application": MessageLookupByLibrary.simpleMessage("アプリケーション"),
"applicationDesc": MessageLookupByLibrary.simpleMessage("アプリ関連設定を変更"),
"authorized": MessageLookupByLibrary.simpleMessage("許可済み"),
"auto": MessageLookupByLibrary.simpleMessage("自動"),
"autoCheckUpdate": MessageLookupByLibrary.simpleMessage("自動更新チェック"),
"autoCheckUpdateDesc": MessageLookupByLibrary.simpleMessage(
@@ -125,6 +136,14 @@ class MessageLookup extends MessageLookupByLibrary {
"backupSuccess": MessageLookupByLibrary.simpleMessage("バックアップ成功"),
"basicConfig": MessageLookupByLibrary.simpleMessage("基本設定"),
"basicConfigDesc": MessageLookupByLibrary.simpleMessage("基本設定をグローバルに変更"),
"basicInfo": MessageLookupByLibrary.simpleMessage("基本情報"),
"basicStrategy": MessageLookupByLibrary.simpleMessage("基本戦略"),
"batteryOptimizationDesc": MessageLookupByLibrary.simpleMessage(
"To ensure background operation, please disable battery optimization for this app. Tap to go to settings.",
),
"batteryOptimizationStatusTip": MessageLookupByLibrary.simpleMessage(
"システムの影響により、この状態は必ずしも正確とは限りません。",
),
"bind": MessageLookupByLibrary.simpleMessage("バインド"),
"blacklistMode": MessageLookupByLibrary.simpleMessage("ブラックリストモード"),
"bypassDomain": MessageLookupByLibrary.simpleMessage("バイパスドメイン"),
@@ -133,14 +152,9 @@ class MessageLookup extends MessageLookupByLibrary {
"キャッシュが破損しています。クリアしますか?",
),
"cancel": MessageLookupByLibrary.simpleMessage("キャンセル"),
"cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage(
"システムアプリの除外を解除",
),
"cancelSelectAll": MessageLookupByLibrary.simpleMessage("全選択解除"),
"checkError": MessageLookupByLibrary.simpleMessage("確認エラー"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("更新を確認"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("アプリは最新版です"),
"checking": MessageLookupByLibrary.simpleMessage("確認中..."),
"clearData": MessageLookupByLibrary.simpleMessage("データを消去"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("クリップボードにエクスポート"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("クリップボードからインポート"),
@@ -148,24 +162,33 @@ class MessageLookup extends MessageLookupByLibrary {
"colorSchemes": MessageLookupByLibrary.simpleMessage("カラースキーム"),
"columns": MessageLookupByLibrary.simpleMessage("列"),
"compatible": MessageLookupByLibrary.simpleMessage("互換モード"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage(
"有効化すると一部機能を失いますが、Clashの完全サポートを獲得",
"configDataDetected": MessageLookupByLibrary.simpleMessage(
"設定内にデータが検出されました",
),
"confirm": MessageLookupByLibrary.simpleMessage("確認"),
"confirmClearAllData": MessageLookupByLibrary.simpleMessage(
"すべてのデータをクリアしてもよろしいですか?",
),
"confirmDeleteProxyGroup": MessageLookupByLibrary.simpleMessage(
"現在のプロキシグループを削除してもよろしいですか?",
),
"confirmExitWindow": MessageLookupByLibrary.simpleMessage(
"現在のウィンドウを閉じてもよろしいですか?",
),
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage(
"コアを強制的にクラッシュさせてもよろしいですか?",
),
"confirmOverwriteTip": MessageLookupByLibrary.simpleMessage(
"確認後、既存のデータは上書きされます",
),
"connected": MessageLookupByLibrary.simpleMessage("接続済み"),
"connecting": MessageLookupByLibrary.simpleMessage("接続中..."),
"connection": MessageLookupByLibrary.simpleMessage("接続"),
"connections": MessageLookupByLibrary.simpleMessage("接続"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
"connectivity": MessageLookupByLibrary.simpleMessage("接続性:"),
"contactMe": MessageLookupByLibrary.simpleMessage("連絡する"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentNotEmpty": MessageLookupByLibrary.simpleMessage("内容は空にできません"),
"contentScheme": MessageLookupByLibrary.simpleMessage("コンテンツテーマ"),
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage(
"グローバル追加ルールを制御",
@@ -175,10 +198,6 @@ class MessageLookup extends MessageLookupByLibrary {
"copyLink": MessageLookupByLibrary.simpleMessage("リンクをコピー"),
"copySuccess": MessageLookupByLibrary.simpleMessage("コピー成功"),
"core": MessageLookupByLibrary.simpleMessage("コア"),
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"コア設定の変更が検出されました",
),
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
"coreStatus": MessageLookupByLibrary.simpleMessage("コアステータス"),
"country": MessageLookupByLibrary.simpleMessage("国"),
"crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"),
@@ -187,24 +206,26 @@ class MessageLookup extends MessageLookupByLibrary {
"有効にすると、アプリがクラッシュした際に機密情報を含まないクラッシュログを自動的にアップロードします",
),
"create": MessageLookupByLibrary.simpleMessage("作成"),
"createProfile": MessageLookupByLibrary.simpleMessage("Create Profile"),
"creationTime": MessageLookupByLibrary.simpleMessage("作成時間"),
"custom": MessageLookupByLibrary.simpleMessage("カスタム"),
"cut": MessageLookupByLibrary.simpleMessage("切り取り"),
"dark": MessageLookupByLibrary.simpleMessage("ダーク"),
"dashboard": MessageLookupByLibrary.simpleMessage("ダッシュボード"),
"dataChangedSave": MessageLookupByLibrary.simpleMessage(
"データの変更が検出されました。保存しますか?",
),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"本アプリはFirebase Crashlyticsを使用してクラッシュ情報を収集し、アプリの安定性を向上させます。\n収集されるデータにはデバイス情報とクラッシュ詳細が含まれますが、個人の機密データは含まれません。\n設定でこの機能を無効にすることができます。",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("データ収集説明"),
"days": MessageLookupByLibrary.simpleMessage("日"),
"daysAgo": m0,
"defaultNameserver": MessageLookupByLibrary.simpleMessage("デフォルトネームサーバー"),
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage(
"DNSサーバーの解決用",
),
"defaultSort": MessageLookupByLibrary.simpleMessage("デフォルト順"),
"defaultText": MessageLookupByLibrary.simpleMessage("デフォルト"),
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
"delayTest": MessageLookupByLibrary.simpleMessage("遅延テスト"),
"delete": MessageLookupByLibrary.simpleMessage("削除"),
"deleteMultipTip": m1,
@@ -222,13 +243,13 @@ class MessageLookup extends MessageLookupByLibrary {
"デベロッパーモードが有効になりました。",
),
"direct": MessageLookupByLibrary.simpleMessage("ダイレクト"),
"disableUDP": MessageLookupByLibrary.simpleMessage("UDPを無効化"),
"disclaimer": MessageLookupByLibrary.simpleMessage("免責事項"),
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"本ソフトウェアは学習交流や科学研究などの非営利目的でのみ使用されます。商用利用は厳禁です。いかなる商用活動も本ソフトウェアとは無関係です。",
),
"disconnected": MessageLookupByLibrary.simpleMessage("切断済み"),
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("新バージョンを発見"),
"discovery": MessageLookupByLibrary.simpleMessage("新しいバージョンを発見"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("DNS関連設定の更新"),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNSハイジャッキング"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNSモード"),
@@ -237,19 +258,27 @@ class MessageLookup extends MessageLookupByLibrary {
"download": MessageLookupByLibrary.simpleMessage("ダウンロード"),
"edit": MessageLookupByLibrary.simpleMessage("編集"),
"editGlobalRules": MessageLookupByLibrary.simpleMessage("グローバルルールを編集"),
"editProxy": MessageLookupByLibrary.simpleMessage("プロキシを編集"),
"editProxyGroup": MessageLookupByLibrary.simpleMessage("プロキシグループを編集"),
"editRule": MessageLookupByLibrary.simpleMessage("ルールを編集"),
"editSsid": MessageLookupByLibrary.simpleMessage("SSIDを編集"),
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("英語"),
"enableOverride": MessageLookupByLibrary.simpleMessage("上書きを有効化"),
"entries": MessageLookupByLibrary.simpleMessage(" エントリ"),
"exclude": MessageLookupByLibrary.simpleMessage("最近のタスクから非表示"),
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"アプリがバックグラウンド時に最近のタスクから非表示",
),
"excludeProxyFilter": MessageLookupByLibrary.simpleMessage("除外プロキシフィルター"),
"excludeSsids": MessageLookupByLibrary.simpleMessage("Exclude SSIDs"),
"excludeSsidsDesc": MessageLookupByLibrary.simpleMessage(
"When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
),
"excludeType": MessageLookupByLibrary.simpleMessage("除外タイプ"),
"existsTip": m5,
"exit": MessageLookupByLibrary.simpleMessage("終了"),
"expand": MessageLookupByLibrary.simpleMessage("標準"),
"expirationTime": MessageLookupByLibrary.simpleMessage("有効期限"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("期待されるステータス"),
"exportFile": MessageLookupByLibrary.simpleMessage("ファイルをエクスポート"),
"exportLogs": MessageLookupByLibrary.simpleMessage("ログをエクスポート"),
"exportSuccess": MessageLookupByLibrary.simpleMessage("エクスポート成功"),
@@ -260,7 +289,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"externalFetch": MessageLookupByLibrary.simpleMessage("外部取得"),
"externalLink": MessageLookupByLibrary.simpleMessage("外部リンク"),
"externalResources": MessageLookupByLibrary.simpleMessage("外部リソース"),
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeipフィルター"),
"fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip範囲"),
"fallback": MessageLookupByLibrary.simpleMessage("フォールバック"),
@@ -272,7 +300,6 @@ class MessageLookup extends MessageLookupByLibrary {
"fileIsUpdate": MessageLookupByLibrary.simpleMessage(
"ファイルが変更されました。保存しますか?",
),
"filterSystemApp": MessageLookupByLibrary.simpleMessage("システムアプリを除外"),
"findProcessMode": MessageLookupByLibrary.simpleMessage("プロセス検出"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとパフォーマンスが若干低下します",
@@ -281,22 +308,19 @@ class MessageLookup extends MessageLookupByLibrary {
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
"コアを強制再起動してもよろしいですか?",
),
"fourColumns": MessageLookupByLibrary.simpleMessage("4列"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("フルーツサラダ"),
"general": MessageLookupByLibrary.simpleMessage("一般"),
"generalDesc": MessageLookupByLibrary.simpleMessage("一般設定を変更"),
"geoData": MessageLookupByLibrary.simpleMessage("地域データ"),
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低メモリモード"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとGeo低メモリローダーを使用",
),
"geoipCode": MessageLookupByLibrary.simpleMessage("GeoIPコード"),
"getOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを取得"),
"global": MessageLookupByLibrary.simpleMessage("グローバル"),
"go": MessageLookupByLibrary.simpleMessage("移動"),
"goDownload": MessageLookupByLibrary.simpleMessage("ダウンロードへ"),
"goToConfigureScript": MessageLookupByLibrary.simpleMessage("スクリプト設定に移動"),
"hasCacheChange": MessageLookupByLibrary.simpleMessage("変更をキャッシュしますか?"),
"hideFromList": MessageLookupByLibrary.simpleMessage("リストから隠す"),
"host": MessageLookupByLibrary.simpleMessage("ホスト"),
"hostsDesc": MessageLookupByLibrary.simpleMessage("ホストを追加"),
"hotkeyConflict": MessageLookupByLibrary.simpleMessage("ホットキー競合"),
@@ -304,28 +328,46 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage(
"キーボードでアプリを制御",
),
"hours": MessageLookupByLibrary.simpleMessage("時間"),
"hoursAgo": m6,
"icon": MessageLookupByLibrary.simpleMessage("アイコン"),
"iconConfiguration": MessageLookupByLibrary.simpleMessage("アイコン設定"),
"iconRecords": MessageLookupByLibrary.simpleMessage("アイコン履歴"),
"iconStyle": MessageLookupByLibrary.simpleMessage("アイコンスタイル"),
"iconUrl": MessageLookupByLibrary.simpleMessage("アイコンURL"),
"ignoreBatteryOptimization": MessageLookupByLibrary.simpleMessage(
"Ignore Battery Optimization",
),
"import": MessageLookupByLibrary.simpleMessage("インポート"),
"importFile": MessageLookupByLibrary.simpleMessage("ファイルからインポート"),
"importFromURL": MessageLookupByLibrary.simpleMessage("URLからインポート"),
"importUrl": MessageLookupByLibrary.simpleMessage("URLからインポート"),
"includeAllProxies": MessageLookupByLibrary.simpleMessage("すべてのプロキシを含める"),
"includeAllProxiesTip": MessageLookupByLibrary.simpleMessage(
"プロキシグループに含まれないすべてのプロキシをインポートします。下でさらにプロキシグループを追加できます",
),
"includeAllProxyProviders": MessageLookupByLibrary.simpleMessage(
"すべてのプロキシプロバイダーを含める",
),
"includeAllProxyProvidersTip": MessageLookupByLibrary.simpleMessage(
"有効にすると、インポートされたプロキシプロバイダーを上書きします",
),
"infiniteTime": MessageLookupByLibrary.simpleMessage("長期有効"),
"init": MessageLookupByLibrary.simpleMessage("初期化"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("正しいホットキーを入力"),
"inputProxyGroupName": MessageLookupByLibrary.simpleMessage("プロキシグループ名を入力"),
"inputRuleContent": MessageLookupByLibrary.simpleMessage("ルール内容を入力"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("インテリジェント選択"),
"internet": MessageLookupByLibrary.simpleMessage("インターネット"),
"interval": MessageLookupByLibrary.simpleMessage("インターバル"),
"intranetIP": MessageLookupByLibrary.simpleMessage("イントラネットIP"),
"invalidBackupFile": MessageLookupByLibrary.simpleMessage("無効なバックアップファイル"),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("有効化するとIPv6トラフィックを受信可能"),
"ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("IPv6インバウンドを許可"),
"ja": MessageLookupByLibrary.simpleMessage("日本語"),
"just": MessageLookupByLibrary.simpleMessage("たった今"),
"justNow": MessageLookupByLibrary.simpleMessage("たった今"),
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage(
"TCPキープアライブ間隔",
@@ -340,6 +382,19 @@ class MessageLookup extends MessageLookupByLibrary {
"loading": MessageLookupByLibrary.simpleMessage("読み込み中..."),
"local": MessageLookupByLibrary.simpleMessage("ローカル"),
"localBackupDesc": MessageLookupByLibrary.simpleMessage("ローカルにデータをバックアップ"),
"locationPermission": MessageLookupByLibrary.simpleMessage(
"Location Permission",
),
"locationPermissionDeniedMessage": MessageLookupByLibrary.simpleMessage(
"位置情報の権限が拒否されたため、現在の Wi-Fi 名を取得できません。システム設定で位置情報の権限を手動で有効にしてください。",
),
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
),
"locationPermissionGuide": m11,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"Location Permission Required",
),
"log": MessageLookupByLibrary.simpleMessage("ログ"),
"logLevel": MessageLookupByLibrary.simpleMessage("ログレベル"),
"logcat": MessageLookupByLibrary.simpleMessage("ログキャット"),
@@ -350,6 +405,8 @@ class MessageLookup extends MessageLookupByLibrary {
"loopback": MessageLookupByLibrary.simpleMessage("ループバック解除ツール"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage("UWPループバック解除用"),
"loose": MessageLookupByLibrary.simpleMessage("疎"),
"matchSourceIp": MessageLookupByLibrary.simpleMessage("送信元IPをマッチング"),
"maxFailedTimes": MessageLookupByLibrary.simpleMessage("最大失敗回数"),
"memoryInfo": MessageLookupByLibrary.simpleMessage("メモリ情報"),
"messageTest": MessageLookupByLibrary.simpleMessage("メッセージテスト"),
"messageTestTip": MessageLookupByLibrary.simpleMessage("これはメッセージです。"),
@@ -358,16 +415,13 @@ class MessageLookup extends MessageLookupByLibrary {
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
"システムの終了イベントを変更",
),
"minutes": MessageLookupByLibrary.simpleMessage("分"),
"minutesAgo": m7,
"minutesAgo": m12,
"mixedPort": MessageLookupByLibrary.simpleMessage("混合ポート"),
"mode": MessageLookupByLibrary.simpleMessage("モード"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("モノクローム"),
"months": MessageLookupByLibrary.simpleMessage("月"),
"monthsAgo": m8,
"monthsAgo": m13,
"more": MessageLookupByLibrary.simpleMessage("詳細"),
"name": MessageLookupByLibrary.simpleMessage("名前"),
"nameSort": MessageLookupByLibrary.simpleMessage("名前順"),
"nameserver": MessageLookupByLibrary.simpleMessage("ネームサーバー"),
"nameserverDesc": MessageLookupByLibrary.simpleMessage("ドメイン解決用"),
"nameserverPolicy": MessageLookupByLibrary.simpleMessage("ネームサーバーポリシー"),
@@ -380,25 +434,18 @@ class MessageLookup extends MessageLookupByLibrary {
"networkException": MessageLookupByLibrary.simpleMessage(
"ネットワーク例外、接続を確認してもう一度お試しください",
),
"networkRequestException": MessageLookupByLibrary.simpleMessage(
"ネットワーク要求例外、後でもう一度試してください。",
),
"networkSpeed": MessageLookupByLibrary.simpleMessage("ネットワーク速度"),
"networkType": MessageLookupByLibrary.simpleMessage("ネットワーク種別"),
"neutralScheme": MessageLookupByLibrary.simpleMessage("ニュートラル"),
"noData": MessageLookupByLibrary.simpleMessage("データなし"),
"noHotKey": MessageLookupByLibrary.simpleMessage("ホットキーなし"),
"noIcon": MessageLookupByLibrary.simpleMessage("なし"),
"noInfo": MessageLookupByLibrary.simpleMessage("情報なし"),
"noLongerRemind": MessageLookupByLibrary.simpleMessage("今後表示しない"),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("追加情報なし"),
"noNetwork": MessageLookupByLibrary.simpleMessage("ネットワークなし"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("ネットワークなしアプリ"),
"noProxy": MessageLookupByLibrary.simpleMessage("プロキシなし"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"プロファイルを作成するか、有効なプロファイルを追加してください",
),
"noRecords": MessageLookupByLibrary.simpleMessage("履歴なし"),
"noResolve": MessageLookupByLibrary.simpleMessage("IPを解決しない"),
"noResolveHostname": MessageLookupByLibrary.simpleMessage("ホスト名を解決しない"),
"none": MessageLookupByLibrary.simpleMessage("なし"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"現在のプロキシグループは選択できません",
@@ -406,30 +453,28 @@ class MessageLookup extends MessageLookupByLibrary {
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"プロファイルがありません。追加してください",
),
"nullTip": m9,
"numberTip": m10,
"oneColumn": MessageLookupByLibrary.simpleMessage("1列"),
"nullTip": m14,
"numberTip": m15,
"onDemand": MessageLookupByLibrary.simpleMessage("On Demand"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage(
"Configure the program running state for specific scenarios",
),
"onlyIcon": MessageLookupByLibrary.simpleMessage("アイコンのみ"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage("サードパーティアプリのみ"),
"onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("プロキシのみ統計"),
"onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとプロキシトラフィックのみ統計",
),
"optional": MessageLookupByLibrary.simpleMessage("オプション"),
"options": MessageLookupByLibrary.simpleMessage("オプション"),
"other": MessageLookupByLibrary.simpleMessage("その他"),
"otherContributors": MessageLookupByLibrary.simpleMessage("その他の貢献者"),
"outboundMode": MessageLookupByLibrary.simpleMessage("アウトバウンドモード"),
"override": MessageLookupByLibrary.simpleMessage("上書き"),
"overrideDesc": MessageLookupByLibrary.simpleMessage("プロキシ関連設定を上書き"),
"overrideDns": MessageLookupByLibrary.simpleMessage("DNS上書き"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとプロファイルのDNS設定を上書き",
),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
"スクリプトモードでは有効になりません",
),
"overrideMode": MessageLookupByLibrary.simpleMessage("上書きモード"),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを上書き"),
"overrideScript": MessageLookupByLibrary.simpleMessage("上書きスクリプト"),
"overwriteTypeCustom": MessageLookupByLibrary.simpleMessage("カスタム"),
"overwriteTypeCustomDesc": MessageLookupByLibrary.simpleMessage(
@@ -447,16 +492,14 @@ class MessageLookup extends MessageLookupByLibrary {
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"管理者パスワードを入力",
),
"pleaseUploadFile": MessageLookupByLibrary.simpleMessage(
"ファイルをアップロードしてください",
),
"pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage(
"有効なQRコードをアップロードしてください",
),
"port": MessageLookupByLibrary.simpleMessage("ポート"),
"portConflictTip": MessageLookupByLibrary.simpleMessage("別のポートを入力してください"),
"portTip": m11,
"portTip": m16,
"preferH3Desc": MessageLookupByLibrary.simpleMessage("DOHのHTTP/3を優先使用"),
"prerequisites": MessageLookupByLibrary.simpleMessage("Prerequisites"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("キーボードを押してください"),
"preview": MessageLookupByLibrary.simpleMessage("プレビュー"),
"process": MessageLookupByLibrary.simpleMessage("プロセス"),
@@ -471,9 +514,6 @@ class MessageLookup extends MessageLookupByLibrary {
"profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage(
"プロファイル名を入力してください",
),
"profileParseErrorDesc": MessageLookupByLibrary.simpleMessage(
"プロファイル解析エラー",
),
"profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage(
"有効なプロファイルURLを入力してください",
),
@@ -485,25 +525,47 @@ class MessageLookup extends MessageLookupByLibrary {
"project": MessageLookupByLibrary.simpleMessage("プロジェクト"),
"providers": MessageLookupByLibrary.simpleMessage("プロバイダー"),
"proxies": MessageLookupByLibrary.simpleMessage("プロキシ"),
"proxiesSetting": MessageLookupByLibrary.simpleMessage("プロキシ設定"),
"proxiesEmpty": MessageLookupByLibrary.simpleMessage("プロキシが空です"),
"proxyChains": MessageLookupByLibrary.simpleMessage("プロキシチェーン"),
"proxyDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"選択されたプロキシに異常があることを検出しました",
),
"proxyFilter": MessageLookupByLibrary.simpleMessage("プロキシフィルター"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("プロキシグループ"),
"proxyGroupDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"現在のプロキシグループが異常であることを検出しました",
),
"proxyGroupEmpty": MessageLookupByLibrary.simpleMessage("プロキシグループが空です"),
"proxyGroupNameDuplicate": MessageLookupByLibrary.simpleMessage(
"プロキシグループ名が重複しています",
),
"proxyGroupNameEmpty": MessageLookupByLibrary.simpleMessage(
"プロキシグループ名は空にできません",
),
"proxyNameserver": MessageLookupByLibrary.simpleMessage("プロキシネームサーバー"),
"proxyNameserverDesc": MessageLookupByLibrary.simpleMessage(
"プロキシノード解決用ドメイン",
),
"proxyPort": MessageLookupByLibrary.simpleMessage("プロキシポート"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("Clashのリスニングポートを設定"),
"proxyProviderDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"選択されたプロキシプロバイダーに異常があることを検出しました",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("プロキシプロバイダー"),
"proxyProvidersEmpty": MessageLookupByLibrary.simpleMessage(
"プロキシプロバイダーが空です",
),
"proxyProvidersNotEmpty": MessageLookupByLibrary.simpleMessage(
"プロキシプロバイダーは空にできません",
),
"proxyType": MessageLookupByLibrary.simpleMessage("プロキシタイプ"),
"pruneCache": MessageLookupByLibrary.simpleMessage("キャッシュの削除"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("純黒モード"),
"qrcode": MessageLookupByLibrary.simpleMessage("QRコード"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage("QRコードをスキャンしてプロファイルを取得"),
"quickFill": MessageLookupByLibrary.simpleMessage("クイック入力"),
"rainbowScheme": MessageLookupByLibrary.simpleMessage("レインボー"),
"redirPort": MessageLookupByLibrary.simpleMessage("Redirポート"),
"redo": MessageLookupByLibrary.simpleMessage("やり直す"),
"regExp": MessageLookupByLibrary.simpleMessage("正規表現"),
"reload": MessageLookupByLibrary.simpleMessage("リロード"),
"remote": MessageLookupByLibrary.simpleMessage("リモート"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
"WebDAVにデータをバックアップ",
@@ -550,12 +612,104 @@ class MessageLookup extends MessageLookupByLibrary {
"routeMode_config": MessageLookupByLibrary.simpleMessage("設定を使用"),
"ru": MessageLookupByLibrary.simpleMessage("ロシア語"),
"rule": MessageLookupByLibrary.simpleMessage("ルール"),
"ruleActionAndDesc": MessageLookupByLibrary.simpleMessage("論理ルール AND"),
"ruleActionDomainDesc": MessageLookupByLibrary.simpleMessage(
"完全なドメインをマッチング",
),
"ruleActionDomainKeywordDesc": MessageLookupByLibrary.simpleMessage(
"ドメインキーワードをマッチング",
),
"ruleActionDomainRegexDesc": MessageLookupByLibrary.simpleMessage(
"ワイルドカードマッチング(*と?のみサポート)",
),
"ruleActionDomainSuffixDesc": MessageLookupByLibrary.simpleMessage(
"ドメイン接尾辞をマッチング",
),
"ruleActionDscpDesc": MessageLookupByLibrary.simpleMessage(
"DSCPマークをマッチング (tproxy udp inboundのみ)",
),
"ruleActionDstPortDesc": MessageLookupByLibrary.simpleMessage(
"宛先ポート範囲をマッチング",
),
"ruleActionGeoipDesc": MessageLookupByLibrary.simpleMessage(
"IPの国コードをマッチング",
),
"ruleActionGeositeDesc": MessageLookupByLibrary.simpleMessage(
"Match domains within Geosite",
),
"ruleActionInNameDesc": MessageLookupByLibrary.simpleMessage(
"インバウンド名をマッチング",
),
"ruleActionInPortDesc": MessageLookupByLibrary.simpleMessage(
"インバウンドポートをマッチング",
),
"ruleActionInTypeDesc": MessageLookupByLibrary.simpleMessage(
"インバウンドタイプをマッチング",
),
"ruleActionInUserDesc": MessageLookupByLibrary.simpleMessage(
"インバウンドユーザー名をマッチング(/で複数指定可)",
),
"ruleActionIpAsnDesc": MessageLookupByLibrary.simpleMessage("IPのASNをマッチング"),
"ruleActionIpCidr6Desc": MessageLookupByLibrary.simpleMessage(
"IPアドレス範囲をマッチング(IP-CIDR6はエイリアスです)",
),
"ruleActionIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"IPアドレス範囲をマッチング",
),
"ruleActionIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"IP接尾辞範囲をマッチング",
),
"ruleActionMatchDesc": MessageLookupByLibrary.simpleMessage(
"すべてのリクエストにマッチ(条件なし)",
),
"ruleActionNetworkDesc": MessageLookupByLibrary.simpleMessage(
"TCPまたはUDPをマッチング",
),
"ruleActionNotDesc": MessageLookupByLibrary.simpleMessage("論理ルール NOT"),
"ruleActionOrDesc": MessageLookupByLibrary.simpleMessage("論理ルール OR"),
"ruleActionProcessNameDesc": MessageLookupByLibrary.simpleMessage(
"プロセス名でマッチング(Androidではパッケージ名)",
),
"ruleActionProcessNameRegexDesc": MessageLookupByLibrary.simpleMessage(
"プロセス名正規表現でマッチング(Androidではパッケージ名)",
),
"ruleActionProcessPathDesc": MessageLookupByLibrary.simpleMessage(
"フルプロセスパスでマッチング",
),
"ruleActionProcessPathRegexDesc": MessageLookupByLibrary.simpleMessage(
"プロセスパス正規表現でマッチング",
),
"ruleActionRuleSetDesc": MessageLookupByLibrary.simpleMessage(
"ルールセットを参照。rule-providersの設定が必要",
),
"ruleActionSrcGeoipDesc": MessageLookupByLibrary.simpleMessage(
"送信元IPの国コードをマッチング",
),
"ruleActionSrcIpAsnDesc": MessageLookupByLibrary.simpleMessage(
"送信元IPのASNをマッチング",
),
"ruleActionSrcIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"送信元IPアドレス範囲をマッチング",
),
"ruleActionSrcIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"送信元IP接尾辞範囲をマッチング",
),
"ruleActionSrcPortDesc": MessageLookupByLibrary.simpleMessage(
"送信元ポート範囲をマッチング",
),
"ruleActionSubRuleDesc": MessageLookupByLibrary.simpleMessage(
"サブルールにマッチング。括弧の使用に注意",
),
"ruleActionUidDesc": MessageLookupByLibrary.simpleMessage(
"Linux USER IDをマッチング",
),
"ruleEmpty": MessageLookupByLibrary.simpleMessage("ルールが空です"),
"ruleName": MessageLookupByLibrary.simpleMessage("ルール名"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("ルールプロバイダー"),
"ruleSet": MessageLookupByLibrary.simpleMessage("ルールセット"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("ルール対象"),
"save": MessageLookupByLibrary.simpleMessage("保存"),
"saveChanges": MessageLookupByLibrary.simpleMessage("変更を保存しますか?"),
"saveTip": MessageLookupByLibrary.simpleMessage("保存してもよろしいですか?"),
"script": MessageLookupByLibrary.simpleMessage("スクリプト"),
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
"スクリプトモード、外部拡張スクリプトを使用し、ワンクリックで設定を上書きする機能を提供",
@@ -563,8 +717,17 @@ class MessageLookup extends MessageLookupByLibrary {
"search": MessageLookupByLibrary.simpleMessage("検索"),
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
"selectAll": MessageLookupByLibrary.simpleMessage("すべて選択"),
"selectProxies": MessageLookupByLibrary.simpleMessage("プロキシを選択"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage(
"プロキシプロバイダーを選択",
),
"selectRuleSet": MessageLookupByLibrary.simpleMessage("ルールセットを選択してください"),
"selectSplitStrategy": MessageLookupByLibrary.simpleMessage(
"分流戦略を選択してください",
),
"selectSubRule": MessageLookupByLibrary.simpleMessage("サブルールを選択してください"),
"selected": MessageLookupByLibrary.simpleMessage("選択済み"),
"selectedCountTitle": m12,
"selectedCountTitle": m17,
"settings": MessageLookupByLibrary.simpleMessage("設定"),
"show": MessageLookupByLibrary.simpleMessage("表示"),
"shrink": MessageLookupByLibrary.simpleMessage("縮小"),
@@ -578,6 +741,11 @@ class MessageLookup extends MessageLookupByLibrary {
"specialProxy": MessageLookupByLibrary.simpleMessage("特殊プロキシ"),
"specialRules": MessageLookupByLibrary.simpleMessage("特殊ルール"),
"speedStatistics": MessageLookupByLibrary.simpleMessage("速度統計"),
"splitStrategy": MessageLookupByLibrary.simpleMessage("分流戦略"),
"splitStrategyNotEmpty": MessageLookupByLibrary.simpleMessage(
"分流戦略は空にできません",
),
"ssidsEmpty": MessageLookupByLibrary.simpleMessage("SSIDs is empty"),
"stackMode": MessageLookupByLibrary.simpleMessage("スタックモード"),
"standard": MessageLookupByLibrary.simpleMessage("標準"),
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
@@ -591,11 +759,13 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("VPNを停止中..."),
"style": MessageLookupByLibrary.simpleMessage("スタイル"),
"subRule": MessageLookupByLibrary.simpleMessage("サブルール"),
"subRuleEmpty": MessageLookupByLibrary.simpleMessage("サブルールが空です"),
"subRuleNotEmpty": MessageLookupByLibrary.simpleMessage("サブルールは空にできません"),
"submit": MessageLookupByLibrary.simpleMessage("送信"),
"suspended": MessageLookupByLibrary.simpleMessage("一時停止中..."),
"sync": MessageLookupByLibrary.simpleMessage("同期"),
"system": MessageLookupByLibrary.simpleMessage("システム"),
"systemApp": MessageLookupByLibrary.simpleMessage("システムアプリ"),
"systemFont": MessageLookupByLibrary.simpleMessage("システムフォント"),
"systemProxy": MessageLookupByLibrary.simpleMessage("システムプロキシ"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
"HTTPプロキシをVpnServiceに接続",
@@ -603,17 +773,20 @@ class MessageLookup extends MessageLookupByLibrary {
"tab": MessageLookupByLibrary.simpleMessage("タブ"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("タブアニメーション"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage("モバイル表示でのみ有効"),
"tapToAuthorize": MessageLookupByLibrary.simpleMessage("タップして許可"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP並列処理"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("TCP並列処理を許可"),
"testInterval": MessageLookupByLibrary.simpleMessage("テスト間隔"),
"testUrl": MessageLookupByLibrary.simpleMessage("URLテスト"),
"testWhenUsed": MessageLookupByLibrary.simpleMessage("使用時にテスト"),
"textScale": MessageLookupByLibrary.simpleMessage("テキストスケーリング"),
"theme": MessageLookupByLibrary.simpleMessage("テーマ"),
"themeColor": MessageLookupByLibrary.simpleMessage("テーマカラー"),
"themeDesc": MessageLookupByLibrary.simpleMessage("ダークモードの設定、色の調整"),
"themeMode": MessageLookupByLibrary.simpleMessage("テーマモード"),
"threeColumns": MessageLookupByLibrary.simpleMessage("3列"),
"tight": MessageLookupByLibrary.simpleMessage("密"),
"time": MessageLookupByLibrary.simpleMessage("時間"),
"timeout": MessageLookupByLibrary.simpleMessage("タイムアウト"),
"tip": MessageLookupByLibrary.simpleMessage("ヒント"),
"toggle": MessageLookupByLibrary.simpleMessage("トグル"),
"tonalSpotScheme": MessageLookupByLibrary.simpleMessage("トーンスポット"),
@@ -624,10 +797,6 @@ class MessageLookup extends MessageLookupByLibrary {
"tunDesc": MessageLookupByLibrary.simpleMessage("管理者モードでのみ有効"),
"turnOff": MessageLookupByLibrary.simpleMessage("オフ"),
"turnOn": MessageLookupByLibrary.simpleMessage("オン"),
"twoColumns": MessageLookupByLibrary.simpleMessage("2列"),
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"現在のプロファイルを更新できません",
),
"undo": MessageLookupByLibrary.simpleMessage("元に戻す"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("統一遅延"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage(
@@ -640,7 +809,7 @@ class MessageLookup extends MessageLookupByLibrary {
"upload": MessageLookupByLibrary.simpleMessage("アップロード"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("URL経由でプロファイルを取得"),
"urlTip": m13,
"urlTip": m18,
"useHosts": MessageLookupByLibrary.simpleMessage("ホストを使用"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("システムホストを使用"),
"value": MessageLookupByLibrary.simpleMessage("値"),
@@ -649,18 +818,13 @@ class MessageLookup extends MessageLookupByLibrary {
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"VPN設定の変更が検出されました",
),
"vpnDesc": MessageLookupByLibrary.simpleMessage("VPN関連設定の変更"),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
"VpnService経由で全システムトラフィックをルーティング",
),
"vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage(
"HTTPプロキシをVpnServiceに接続",
),
"vpnTip": MessageLookupByLibrary.simpleMessage("変更はVPN再起動後に有効"),
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV設定"),
"whitelistMode": MessageLookupByLibrary.simpleMessage("ホワイトリストモード"),
"years": MessageLookupByLibrary.simpleMessage("年"),
"yearsAgo": m14,
"yearsAgo": m19,
"zh_CN": MessageLookupByLibrary.simpleMessage("簡体字中国語"),
};
}
+302 -127
View File
@@ -37,23 +37,35 @@ class MessageLookup extends MessageLookupByLibrary {
static String m6(count) =>
"${Intl.plural(count, one: '${count} час назад', few: '${count} часа назад', many: '${count} часов назад', other: '${count} часа назад')}";
static String m7(count) =>
static String m7(target) => "${target} является недопустимой политикой";
static String m8(proxyName) => "${proxyName} является недопустимым прокси";
static String m9(providerName) =>
"${providerName} является недопустимым провайдером прокси";
static String m10(subRule) => "${subRule} является недопустимым подправилом";
static String m11(appName) =>
"1. Open System Settings > Privacy & Security\n2. Choose Location Services\n3. Find and check ${appName} in the right list\n\nAfter completing the setup, return to the app and use it normally. Thank you for your cooperation.";
static String m12(count) =>
"${Intl.plural(count, one: '${count} минута назад', few: '${count} минуты назад', many: '${count} минут назад', other: '${count} минуты назад')}";
static String m8(count) =>
static String m13(count) =>
"${Intl.plural(count, one: '${count} месяц назад', few: '${count} месяца назад', many: '${count} месяцев назад', other: '${count} месяца назад')}";
static String m9(label) => "${label} пока отсутствуют";
static String m14(label) => "${label} пока отсутствуют";
static String m10(label) => "${label} должно быть числом";
static String m15(label) => "${label} должно быть числом";
static String m11(label) => "${label} должен быть числом от 1024 до 49151";
static String m16(label) => "${label} должен быть числом от 1024 до 49151";
static String m12(count) => "Выбрано ${count} элементов";
static String m17(count) => "Выбрано ${count} элементов";
static String m13(label) => "${label} должен быть URL";
static String m18(label) => "${label} должен быть URL";
static String m14(count) =>
static String m19(count) =>
"${Intl.plural(count, one: '${count} год назад', few: '${count} года назад', many: '${count} лет назад', other: '${count} года назад')}";
final messages = _notInlinedMessages(_notInlinedMessages);
@@ -81,31 +93,31 @@ class MessageLookup extends MessageLookupByLibrary {
"action_view": MessageLookupByLibrary.simpleMessage("Показать/Скрыть"),
"add": MessageLookupByLibrary.simpleMessage("Добавить"),
"addProfile": MessageLookupByLibrary.simpleMessage("Добавить профиль"),
"addRule": MessageLookupByLibrary.simpleMessage("Добавить правило"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage(
"Добавить к оригинальным правилам",
"addProxies": MessageLookupByLibrary.simpleMessage("Добавить прокси"),
"addProxyGroup": MessageLookupByLibrary.simpleMessage(
"Добавить группу прокси",
),
"addProxyProviders": MessageLookupByLibrary.simpleMessage(
"Добавить провайдеров прокси",
),
"addRule": MessageLookupByLibrary.simpleMessage("Добавить правило"),
"addSsid": MessageLookupByLibrary.simpleMessage("Добавить SSID"),
"addedRules": MessageLookupByLibrary.simpleMessage("Добавленные правила"),
"additionalParameters": MessageLookupByLibrary.simpleMessage(
"Дополнительные параметры",
),
"address": MessageLookupByLibrary.simpleMessage("Адрес"),
"addressHelp": MessageLookupByLibrary.simpleMessage("Адрес сервера WebDAV"),
"addressTip": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите действительный адрес WebDAV",
),
"adminAutoLaunch": MessageLookupByLibrary.simpleMessage(
"Автозапуск с правами администратора",
),
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage(
"Запуск с правами администратора при загрузке системы",
),
"advancedConfig": MessageLookupByLibrary.simpleMessage(
"Расширенная конфигурация",
),
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage(
"Предоставляет разнообразные варианты конфигурации",
),
"ago": MessageLookupByLibrary.simpleMessage(" назад"),
"agree": MessageLookupByLibrary.simpleMessage("Согласен"),
"allApps": MessageLookupByLibrary.simpleMessage("Все приложения"),
"allowBypass": MessageLookupByLibrary.simpleMessage(
"Разрешить приложениям обходить VPN",
),
@@ -120,9 +132,6 @@ class MessageLookup extends MessageLookupByLibrary {
"appAccessControl": MessageLookupByLibrary.simpleMessage(
"Контроль доступа приложений",
),
"appDesc": MessageLookupByLibrary.simpleMessage(
"Обработка настроек, связанных с приложением",
),
"appendSystemDns": MessageLookupByLibrary.simpleMessage(
"Добавить системный DNS",
),
@@ -133,6 +142,7 @@ class MessageLookup extends MessageLookupByLibrary {
"applicationDesc": MessageLookupByLibrary.simpleMessage(
"Изменение настроек, связанных с приложением",
),
"authorized": MessageLookupByLibrary.simpleMessage("Разрешено"),
"auto": MessageLookupByLibrary.simpleMessage("Авто"),
"autoCheckUpdate": MessageLookupByLibrary.simpleMessage(
"Автопроверка обновлений",
@@ -175,6 +185,14 @@ class MessageLookup extends MessageLookupByLibrary {
"basicConfigDesc": MessageLookupByLibrary.simpleMessage(
"Глобальное изменение базовых настроек",
),
"basicInfo": MessageLookupByLibrary.simpleMessage("Основная информация"),
"basicStrategy": MessageLookupByLibrary.simpleMessage("Базовая стратегия"),
"batteryOptimizationDesc": MessageLookupByLibrary.simpleMessage(
"To ensure background operation, please disable battery optimization for this app. Tap to go to settings.",
),
"batteryOptimizationStatusTip": MessageLookupByLibrary.simpleMessage(
"Из-за особенностей системы этот статус не всегда может быть точным.",
),
"bind": MessageLookupByLibrary.simpleMessage("Привязать"),
"blacklistMode": MessageLookupByLibrary.simpleMessage(
"Режим черного списка",
@@ -187,18 +205,13 @@ class MessageLookup extends MessageLookupByLibrary {
"Кэш поврежден. Хотите очистить его?",
),
"cancel": MessageLookupByLibrary.simpleMessage("Отмена"),
"cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage(
"Отменить фильтрацию системных приложений",
),
"cancelSelectAll": MessageLookupByLibrary.simpleMessage(
"Отменить выбор всего",
),
"checkError": MessageLookupByLibrary.simpleMessage("Ошибка проверки"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("Проверить обновления"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage(
"Текущее приложение уже является последней версией",
),
"checking": MessageLookupByLibrary.simpleMessage("Проверка..."),
"clearData": MessageLookupByLibrary.simpleMessage("Очистить данные"),
"clipboardExport": MessageLookupByLibrary.simpleMessage(
"Экспорт в буфер обмена",
@@ -210,16 +223,25 @@ class MessageLookup extends MessageLookupByLibrary {
"colorSchemes": MessageLookupByLibrary.simpleMessage("Цветовые схемы"),
"columns": MessageLookupByLibrary.simpleMessage("Столбцы"),
"compatible": MessageLookupByLibrary.simpleMessage("Режим совместимости"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage(
"Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.",
"configDataDetected": MessageLookupByLibrary.simpleMessage(
"Данные обнаружены в конфигурации",
),
"confirm": MessageLookupByLibrary.simpleMessage("Подтвердить"),
"confirmClearAllData": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите очистить все данные?",
),
"confirmDeleteProxyGroup": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите удалить текущую группу прокси?",
),
"confirmExitWindow": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите выйти из текущего окна?",
),
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите принудительно аварийно завершить работу ядра?",
),
"confirmOverwriteTip": MessageLookupByLibrary.simpleMessage(
"Существующие данные будут перезаписаны после подтверждения",
),
"connected": MessageLookupByLibrary.simpleMessage("Подключено"),
"connecting": MessageLookupByLibrary.simpleMessage("Подключение..."),
"connection": MessageLookupByLibrary.simpleMessage("Соединение"),
@@ -228,8 +250,10 @@ class MessageLookup extends MessageLookupByLibrary {
"Просмотр текущих данных о соединениях",
),
"connectivity": MessageLookupByLibrary.simpleMessage("Связь:"),
"contactMe": MessageLookupByLibrary.simpleMessage("Свяжитесь со мной"),
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
"contentNotEmpty": MessageLookupByLibrary.simpleMessage(
"Содержимое не может быть пустым",
),
"contentScheme": MessageLookupByLibrary.simpleMessage("Контентная тема"),
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage(
"Управление глобальными добавленными правилами",
@@ -241,10 +265,6 @@ class MessageLookup extends MessageLookupByLibrary {
"copyLink": MessageLookupByLibrary.simpleMessage("Копировать ссылку"),
"copySuccess": MessageLookupByLibrary.simpleMessage("Копирование успешно"),
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"Обнаружено изменение конфигурации ядра",
),
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
"coreStatus": MessageLookupByLibrary.simpleMessage("Основной статус"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"),
@@ -253,17 +273,21 @@ class MessageLookup extends MessageLookupByLibrary {
"При включении автоматически загружает журналы сбоев без конфиденциальной информации, когда приложение выходит из строя",
),
"create": MessageLookupByLibrary.simpleMessage("Создать"),
"createProfile": MessageLookupByLibrary.simpleMessage("Create Profile"),
"creationTime": MessageLookupByLibrary.simpleMessage("Время создания"),
"custom": MessageLookupByLibrary.simpleMessage("Пользовательский"),
"cut": MessageLookupByLibrary.simpleMessage("Вырезать"),
"dark": MessageLookupByLibrary.simpleMessage("Темный"),
"dashboard": MessageLookupByLibrary.simpleMessage("Панель управления"),
"dataChangedSave": MessageLookupByLibrary.simpleMessage(
"Обнаружены изменения данных, хотите сохранить?",
),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"Это приложение использует Firebase Crashlytics для сбора информации о сбоях nhằm улучшения стабильности приложения.\nСобираемые данные включают информацию об устройстве и подробности о сбоях, но не содержат персональных конфиденциальных данных.\nВы можете отключить эту функцию в настройках.",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage(
"Уведомление о сборе данных",
),
"days": MessageLookupByLibrary.simpleMessage("Дней"),
"daysAgo": m0,
"defaultNameserver": MessageLookupByLibrary.simpleMessage(
"Сервер имен по умолчанию",
@@ -271,12 +295,8 @@ class MessageLookup extends MessageLookupByLibrary {
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage(
"Для разрешения DNS-сервера",
),
"defaultSort": MessageLookupByLibrary.simpleMessage(
"Сортировка по умолчанию",
),
"defaultText": MessageLookupByLibrary.simpleMessage("По умолчанию"),
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
"delayTest": MessageLookupByLibrary.simpleMessage("Тест задержки"),
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
"deleteMultipTip": m1,
@@ -298,6 +318,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Режим разработчика активирован.",
),
"direct": MessageLookupByLibrary.simpleMessage("Прямой"),
"disableUDP": MessageLookupByLibrary.simpleMessage("Отключить UDP"),
"disclaimer": MessageLookupByLibrary.simpleMessage(
"Отказ от ответственности",
),
@@ -308,9 +329,6 @@ class MessageLookup extends MessageLookupByLibrary {
"discoverNewVersion": MessageLookupByLibrary.simpleMessage(
"Обнаружена новая версия",
),
"discovery": MessageLookupByLibrary.simpleMessage(
"Обнаружена новая версия",
),
"dnsDesc": MessageLookupByLibrary.simpleMessage(
"Обновление настроек, связанных с DNS",
),
@@ -325,12 +343,14 @@ class MessageLookup extends MessageLookupByLibrary {
"editGlobalRules": MessageLookupByLibrary.simpleMessage(
"Редактировать глобальные правила",
),
"editProxy": MessageLookupByLibrary.simpleMessage("Редактировать прокси"),
"editProxyGroup": MessageLookupByLibrary.simpleMessage(
"Редактировать группу прокси",
),
"editRule": MessageLookupByLibrary.simpleMessage("Редактировать правило"),
"editSsid": MessageLookupByLibrary.simpleMessage("Изменить SSID"),
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("Английский"),
"enableOverride": MessageLookupByLibrary.simpleMessage(
"Включить переопределение",
),
"entries": MessageLookupByLibrary.simpleMessage(" записей"),
"exclude": MessageLookupByLibrary.simpleMessage(
"Скрыть из последних задач",
@@ -338,10 +358,18 @@ class MessageLookup extends MessageLookupByLibrary {
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"Когда приложение находится в фоновом режиме, оно скрыто из последних задач",
),
"excludeProxyFilter": MessageLookupByLibrary.simpleMessage(
"Исключить фильтр прокси",
),
"excludeSsids": MessageLookupByLibrary.simpleMessage("Exclude SSIDs"),
"excludeSsidsDesc": MessageLookupByLibrary.simpleMessage(
"When connected to an excluded SSID Wi-Fi, the app running state will be automatically switched.",
),
"excludeType": MessageLookupByLibrary.simpleMessage("Тип исключения"),
"existsTip": m5,
"exit": MessageLookupByLibrary.simpleMessage("Выход"),
"expand": MessageLookupByLibrary.simpleMessage("Стандартный"),
"expirationTime": MessageLookupByLibrary.simpleMessage("Время истечения"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("Ожидаемый статус"),
"exportFile": MessageLookupByLibrary.simpleMessage("Экспорт файла"),
"exportLogs": MessageLookupByLibrary.simpleMessage("Экспорт логов"),
"exportSuccess": MessageLookupByLibrary.simpleMessage("Экспорт успешен"),
@@ -354,9 +382,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"externalFetch": MessageLookupByLibrary.simpleMessage("Внешнее получение"),
"externalLink": MessageLookupByLibrary.simpleMessage("Внешняя ссылка"),
"externalResources": MessageLookupByLibrary.simpleMessage(
"Внешние ресурсы",
),
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Фильтр Fakeip"),
"fakeipRange": MessageLookupByLibrary.simpleMessage("Диапазон Fakeip"),
"fallback": MessageLookupByLibrary.simpleMessage("Резервный"),
@@ -372,9 +397,6 @@ class MessageLookup extends MessageLookupByLibrary {
"fileIsUpdate": MessageLookupByLibrary.simpleMessage(
"Файл был изменен. Хотите сохранить изменения?",
),
"filterSystemApp": MessageLookupByLibrary.simpleMessage(
"Фильтровать системные приложения",
),
"findProcessMode": MessageLookupByLibrary.simpleMessage(
"Режим поиска процесса",
),
@@ -385,13 +407,8 @@ class MessageLookup extends MessageLookupByLibrary {
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите принудительно перезапустить ядро?",
),
"fourColumns": MessageLookupByLibrary.simpleMessage("Четыре столбца"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("Фруктовый микс"),
"general": MessageLookupByLibrary.simpleMessage("Общие"),
"generalDesc": MessageLookupByLibrary.simpleMessage(
"Изменение общих настроек",
),
"geoData": MessageLookupByLibrary.simpleMessage("Геоданные"),
"geodataLoader": MessageLookupByLibrary.simpleMessage(
"Режим низкого потребления памяти для геоданных",
),
@@ -399,9 +416,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Включение будет использовать загрузчик геоданных с низким потреблением памяти",
),
"geoipCode": MessageLookupByLibrary.simpleMessage("Код Geoip"),
"getOriginRules": MessageLookupByLibrary.simpleMessage(
"Получить оригинальные правила",
),
"global": MessageLookupByLibrary.simpleMessage("Глобальный"),
"go": MessageLookupByLibrary.simpleMessage("Перейти"),
"goDownload": MessageLookupByLibrary.simpleMessage("Перейти к загрузке"),
@@ -411,6 +425,7 @@ class MessageLookup extends MessageLookupByLibrary {
"hasCacheChange": MessageLookupByLibrary.simpleMessage(
"Хотите сохранить изменения в кэше?",
),
"hideFromList": MessageLookupByLibrary.simpleMessage("Скрыть из списка"),
"host": MessageLookupByLibrary.simpleMessage("Хост"),
"hostsDesc": MessageLookupByLibrary.simpleMessage("Добавить Hosts"),
"hotkeyConflict": MessageLookupByLibrary.simpleMessage(
@@ -422,17 +437,30 @@ class MessageLookup extends MessageLookupByLibrary {
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage(
"Использование клавиатуры для управления приложением",
),
"hours": MessageLookupByLibrary.simpleMessage("Часов"),
"hoursAgo": m6,
"icon": MessageLookupByLibrary.simpleMessage("Иконка"),
"iconConfiguration": MessageLookupByLibrary.simpleMessage(
"Конфигурация иконки",
),
"iconRecords": MessageLookupByLibrary.simpleMessage("История иконок"),
"iconStyle": MessageLookupByLibrary.simpleMessage("Стиль иконки"),
"iconUrl": MessageLookupByLibrary.simpleMessage("URL иконки"),
"ignoreBatteryOptimization": MessageLookupByLibrary.simpleMessage(
"Ignore Battery Optimization",
),
"import": MessageLookupByLibrary.simpleMessage("Импорт"),
"importFile": MessageLookupByLibrary.simpleMessage("Импорт из файла"),
"importFromURL": MessageLookupByLibrary.simpleMessage("Импорт из URL"),
"importUrl": MessageLookupByLibrary.simpleMessage("Импорт по URL"),
"includeAllProxies": MessageLookupByLibrary.simpleMessage(
"Включить все прокси",
),
"includeAllProxiesTip": MessageLookupByLibrary.simpleMessage(
"Импорт всех прокси, не содержащих группы прокси, дополнительные группы прокси можно добавить ниже",
),
"includeAllProxyProviders": MessageLookupByLibrary.simpleMessage(
"Включить всех провайдеров прокси",
),
"includeAllProxyProvidersTip": MessageLookupByLibrary.simpleMessage(
"При включении это переопределит импортированных провайдеров прокси",
),
"infiniteTime": MessageLookupByLibrary.simpleMessage(
"Долгосрочное действие",
),
@@ -440,6 +468,12 @@ class MessageLookup extends MessageLookupByLibrary {
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите правильную горячую клавишу",
),
"inputProxyGroupName": MessageLookupByLibrary.simpleMessage(
"Введите имя группы прокси",
),
"inputRuleContent": MessageLookupByLibrary.simpleMessage(
"Введите содержимое правила",
),
"intelligentSelected": MessageLookupByLibrary.simpleMessage(
"Интеллектуальный выбор",
),
@@ -449,6 +483,10 @@ class MessageLookup extends MessageLookupByLibrary {
"invalidBackupFile": MessageLookupByLibrary.simpleMessage(
"Неверный файл резервной копии",
),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
"При включении будет возможно получать IPv6 трафик",
@@ -457,7 +495,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Разрешить входящий IPv6",
),
"ja": MessageLookupByLibrary.simpleMessage("Японский"),
"just": MessageLookupByLibrary.simpleMessage("Только что"),
"justNow": MessageLookupByLibrary.simpleMessage("Только что"),
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage(
"Интервал поддержания TCP-соединения",
@@ -474,6 +511,19 @@ class MessageLookup extends MessageLookupByLibrary {
"localBackupDesc": MessageLookupByLibrary.simpleMessage(
"Резервное копирование локальных данных на локальный диск",
),
"locationPermission": MessageLookupByLibrary.simpleMessage(
"Location Permission",
),
"locationPermissionDeniedMessage": MessageLookupByLibrary.simpleMessage(
"Разрешение на геолокацию отклонено, поэтому невозможно получить имя текущей Wi-Fi сети. Включите разрешение на геолокацию вручную в системных настройках.",
),
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"According to system requirements, obtaining the Wi-Fi name requires you to grant location permission.",
),
"locationPermissionGuide": m11,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"Location Permission Required",
),
"log": MessageLookupByLibrary.simpleMessage("Журнал"),
"logLevel": MessageLookupByLibrary.simpleMessage("Уровень логов"),
"logcat": MessageLookupByLibrary.simpleMessage("Logcat"),
@@ -490,6 +540,12 @@ class MessageLookup extends MessageLookupByLibrary {
"Используется для разблокировки Loopback UWP",
),
"loose": MessageLookupByLibrary.simpleMessage("Свободный"),
"matchSourceIp": MessageLookupByLibrary.simpleMessage(
"Сопоставить исходный IP",
),
"maxFailedTimes": MessageLookupByLibrary.simpleMessage(
"Макс. количество неудач",
),
"memoryInfo": MessageLookupByLibrary.simpleMessage("Информация о памяти"),
"messageTest": MessageLookupByLibrary.simpleMessage(
"Тестирование сообщения",
@@ -502,16 +558,13 @@ class MessageLookup extends MessageLookupByLibrary {
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
"Изменить стандартное событие выхода из системы",
),
"minutes": MessageLookupByLibrary.simpleMessage("Минут"),
"minutesAgo": m7,
"minutesAgo": m12,
"mixedPort": MessageLookupByLibrary.simpleMessage("Смешанный порт"),
"mode": MessageLookupByLibrary.simpleMessage("Режим"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("Монохром"),
"months": MessageLookupByLibrary.simpleMessage("Месяцев"),
"monthsAgo": m8,
"monthsAgo": m13,
"more": MessageLookupByLibrary.simpleMessage("Еще"),
"name": MessageLookupByLibrary.simpleMessage("Имя"),
"nameSort": MessageLookupByLibrary.simpleMessage("Сортировка по имени"),
"nameserver": MessageLookupByLibrary.simpleMessage("Сервер имен"),
"nameserverDesc": MessageLookupByLibrary.simpleMessage(
"Для разрешения домена",
@@ -532,29 +585,22 @@ class MessageLookup extends MessageLookupByLibrary {
"networkException": MessageLookupByLibrary.simpleMessage(
"Ошибка сети, проверьте соединение и попробуйте еще раз",
),
"networkRequestException": MessageLookupByLibrary.simpleMessage(
"Исключение сетевого запроса, пожалуйста, попробуйте позже.",
),
"networkSpeed": MessageLookupByLibrary.simpleMessage("Скорость сети"),
"networkType": MessageLookupByLibrary.simpleMessage("Тип сети"),
"neutralScheme": MessageLookupByLibrary.simpleMessage("Нейтральные"),
"noData": MessageLookupByLibrary.simpleMessage("Нет данных"),
"noHotKey": MessageLookupByLibrary.simpleMessage("Нет горячей клавиши"),
"noIcon": MessageLookupByLibrary.simpleMessage("Нет иконки"),
"noInfo": MessageLookupByLibrary.simpleMessage("Нет информации"),
"noLongerRemind": MessageLookupByLibrary.simpleMessage(
"Больше не напоминать",
),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage(
"Нет дополнительной информации",
),
"noNetwork": MessageLookupByLibrary.simpleMessage("Нет сети"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("Приложение без сети"),
"noProxy": MessageLookupByLibrary.simpleMessage("Нет прокси"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, создайте профиль или добавьте действительный профиль",
),
"noRecords": MessageLookupByLibrary.simpleMessage("Нет записей"),
"noResolve": MessageLookupByLibrary.simpleMessage("Не разрешать IP"),
"noResolveHostname": MessageLookupByLibrary.simpleMessage(
"Не разрешать имя хоста",
),
"none": MessageLookupByLibrary.simpleMessage("Нет"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"Текущая группа прокси не может быть выбрана.",
@@ -562,19 +608,20 @@ class MessageLookup extends MessageLookupByLibrary {
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"Нет профиля, пожалуйста, добавьте профиль",
),
"nullTip": m9,
"numberTip": m10,
"oneColumn": MessageLookupByLibrary.simpleMessage("Один столбец"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("Только иконка"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage(
"Только сторонние приложения",
"nullTip": m14,
"numberTip": m15,
"onDemand": MessageLookupByLibrary.simpleMessage("On Demand"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage(
"Configure the program running state for specific scenarios",
),
"onlyIcon": MessageLookupByLibrary.simpleMessage("Только иконка"),
"onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage(
"Только статистика прокси",
),
"onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage(
"При включении будет учитываться только трафик прокси",
),
"optional": MessageLookupByLibrary.simpleMessage("Необязательно"),
"options": MessageLookupByLibrary.simpleMessage("Опции"),
"other": MessageLookupByLibrary.simpleMessage("Другое"),
"otherContributors": MessageLookupByLibrary.simpleMessage(
@@ -584,22 +631,13 @@ class MessageLookup extends MessageLookupByLibrary {
"Режим исходящего трафика",
),
"override": MessageLookupByLibrary.simpleMessage("Переопределить"),
"overrideDesc": MessageLookupByLibrary.simpleMessage(
"Переопределить конфигурацию, связанную с прокси",
),
"overrideDns": MessageLookupByLibrary.simpleMessage("Переопределить DNS"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Включение переопределит настройки DNS в профиле",
),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
"В скриптовом режиме не действует",
),
"overrideMode": MessageLookupByLibrary.simpleMessage(
"Режим переопределения",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
"Переопределить оригинальное правило",
),
"overrideScript": MessageLookupByLibrary.simpleMessage(
"Скрипт переопределения",
),
@@ -621,9 +659,6 @@ class MessageLookup extends MessageLookupByLibrary {
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите пароль администратора",
),
"pleaseUploadFile": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, загрузите файл",
),
"pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, загрузите действительный QR-код",
),
@@ -631,10 +666,11 @@ class MessageLookup extends MessageLookupByLibrary {
"portConflictTip": MessageLookupByLibrary.simpleMessage(
"Введите другой порт",
),
"portTip": m11,
"portTip": m16,
"preferH3Desc": MessageLookupByLibrary.simpleMessage(
"Приоритетное использование HTTP/3 для DOH",
),
"prerequisites": MessageLookupByLibrary.simpleMessage("Prerequisites"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, нажмите клавишу.",
),
@@ -655,9 +691,6 @@ class MessageLookup extends MessageLookupByLibrary {
"profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите имя профиля",
),
"profileParseErrorDesc": MessageLookupByLibrary.simpleMessage(
"ошибка разбора профиля",
),
"profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите действительный URL профиля",
),
@@ -669,9 +702,25 @@ class MessageLookup extends MessageLookupByLibrary {
"project": MessageLookupByLibrary.simpleMessage("Проект"),
"providers": MessageLookupByLibrary.simpleMessage("Провайдеры"),
"proxies": MessageLookupByLibrary.simpleMessage("Прокси"),
"proxiesSetting": MessageLookupByLibrary.simpleMessage("Настройка прокси"),
"proxiesEmpty": MessageLookupByLibrary.simpleMessage("Список прокси пуст"),
"proxyChains": MessageLookupByLibrary.simpleMessage("Цепочки прокси"),
"proxyDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"Обнаружена аномалия выбранных прокси",
),
"proxyFilter": MessageLookupByLibrary.simpleMessage("Фильтр прокси"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("Группа прокси"),
"proxyGroupDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"Обнаружена аномалия текущей группы прокси",
),
"proxyGroupEmpty": MessageLookupByLibrary.simpleMessage(
"Группа прокси пуста",
),
"proxyGroupNameDuplicate": MessageLookupByLibrary.simpleMessage(
"Имя группы прокси дублируется",
),
"proxyGroupNameEmpty": MessageLookupByLibrary.simpleMessage(
"Имя группы прокси не может быть пустым",
),
"proxyNameserver": MessageLookupByLibrary.simpleMessage(
"Прокси-сервер имен",
),
@@ -679,21 +728,27 @@ class MessageLookup extends MessageLookupByLibrary {
"Домен для разрешения прокси-узлов",
),
"proxyPort": MessageLookupByLibrary.simpleMessage("Порт прокси"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Установить порт прослушивания Clash",
"proxyProviderDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"Обнаружена аномалия выбранных провайдеров прокси",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("Провайдеры прокси"),
"proxyProvidersEmpty": MessageLookupByLibrary.simpleMessage(
"Провайдеры прокси пусты",
),
"proxyProvidersNotEmpty": MessageLookupByLibrary.simpleMessage(
"Провайдеры прокси не могут быть пустыми",
),
"proxyType": MessageLookupByLibrary.simpleMessage("Тип прокси"),
"pruneCache": MessageLookupByLibrary.simpleMessage("Очистить кэш"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("Чисто черный режим"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR-код"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage(
"Сканируйте QR-код для получения профиля",
),
"quickFill": MessageLookupByLibrary.simpleMessage("Быстрое заполнение"),
"rainbowScheme": MessageLookupByLibrary.simpleMessage("Радужные"),
"redirPort": MessageLookupByLibrary.simpleMessage("Redir-порт"),
"redo": MessageLookupByLibrary.simpleMessage("Повторить"),
"regExp": MessageLookupByLibrary.simpleMessage("Регулярное выражение"),
"reload": MessageLookupByLibrary.simpleMessage("Перезагрузить"),
"remote": MessageLookupByLibrary.simpleMessage("Удаленный"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage(
"Резервное копирование локальных данных на WebDAV",
@@ -768,14 +823,112 @@ class MessageLookup extends MessageLookupByLibrary {
),
"ru": MessageLookupByLibrary.simpleMessage("Русский"),
"rule": MessageLookupByLibrary.simpleMessage("Правило"),
"ruleActionAndDesc": MessageLookupByLibrary.simpleMessage(
"Логическое правило AND",
),
"ruleActionDomainDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить полный домен",
),
"ruleActionDomainKeywordDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить ключевое слово домена",
),
"ruleActionDomainRegexDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставление по маске, поддерживает только * и ?",
),
"ruleActionDomainSuffixDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить суффикс домена",
),
"ruleActionDscpDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить метку DSCP (только для tproxy udp inbound)",
),
"ruleActionDstPortDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон портов назначения запроса",
),
"ruleActionGeoipDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить код страны IP-адреса",
),
"ruleActionGeositeDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить домены внутри Geosite",
),
"ruleActionInNameDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить входящее имя",
),
"ruleActionInPortDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить входящий порт",
),
"ruleActionInTypeDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить входящий тип",
),
"ruleActionInUserDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить входящее имя пользователя, поддерживает несколько имен через /",
),
"ruleActionIpAsnDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить ASN IP-адреса",
),
"ruleActionIpCidr6Desc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон IP-адресов (IP-CIDR6 — это просто псевдоним)",
),
"ruleActionIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон IP-адресов",
),
"ruleActionIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон суффиксов IP",
),
"ruleActionMatchDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить все запросы, условия не требуются",
),
"ruleActionNetworkDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить TCP или UDP",
),
"ruleActionNotDesc": MessageLookupByLibrary.simpleMessage(
"Логическое правило NOT",
),
"ruleActionOrDesc": MessageLookupByLibrary.simpleMessage(
"Логическое правило OR",
),
"ruleActionProcessNameDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить по имени процесса, на Android соответствует имени пакета",
),
"ruleActionProcessNameRegexDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить по регулярному выражению имени процесса, на Android соответствует имени пакета",
),
"ruleActionProcessPathDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить по полному пути процесса",
),
"ruleActionProcessPathRegexDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить по регулярному выражению пути процесса",
),
"ruleActionRuleSetDesc": MessageLookupByLibrary.simpleMessage(
"Ссылка на набор правил, требуется настройка rule-providers",
),
"ruleActionSrcGeoipDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить код страны исходного IP",
),
"ruleActionSrcIpAsnDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить ASN исходного IP",
),
"ruleActionSrcIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон исходных IP-адресов",
),
"ruleActionSrcIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон исходных суффиксов IP",
),
"ruleActionSrcPortDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить диапазон портов источника запроса",
),
"ruleActionSubRuleDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить с подправилом, обратите внимание на использование скобок",
),
"ruleActionUidDesc": MessageLookupByLibrary.simpleMessage(
"Сопоставить Linux USER ID",
),
"ruleEmpty": MessageLookupByLibrary.simpleMessage("Правило пусто"),
"ruleName": MessageLookupByLibrary.simpleMessage("Название правила"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Провайдеры правил"),
"ruleSet": MessageLookupByLibrary.simpleMessage("Набор правил"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("Цель правила"),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"saveChanges": MessageLookupByLibrary.simpleMessage("Сохранить изменения?"),
"saveTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите сохранить?",
),
"script": MessageLookupByLibrary.simpleMessage("Скрипт"),
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
"Режим скрипта, использование внешних расширяющих скриптов, предоставление возможности переопределения конфигурации одним кликом",
@@ -783,8 +936,21 @@ class MessageLookup extends MessageLookupByLibrary {
"search": MessageLookupByLibrary.simpleMessage("Поиск"),
"seconds": MessageLookupByLibrary.simpleMessage("Секунд"),
"selectAll": MessageLookupByLibrary.simpleMessage("Выбрать все"),
"selectProxies": MessageLookupByLibrary.simpleMessage("Выбрать прокси"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage(
"Выбрать провайдеров прокси",
),
"selectRuleSet": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, выберите набор правил",
),
"selectSplitStrategy": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, выберите стратегию разделения",
),
"selectSubRule": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, выберите подправило",
),
"selected": MessageLookupByLibrary.simpleMessage("Выбрано"),
"selectedCountTitle": m12,
"selectedCountTitle": m17,
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"show": MessageLookupByLibrary.simpleMessage("Показать"),
"shrink": MessageLookupByLibrary.simpleMessage("Сжать"),
@@ -802,6 +968,13 @@ class MessageLookup extends MessageLookupByLibrary {
"speedStatistics": MessageLookupByLibrary.simpleMessage(
"Статистика скорости",
),
"splitStrategy": MessageLookupByLibrary.simpleMessage(
"Стратегия разделения",
),
"splitStrategyNotEmpty": MessageLookupByLibrary.simpleMessage(
"Стратегия разделения не может быть пустой",
),
"ssidsEmpty": MessageLookupByLibrary.simpleMessage("SSIDs is empty"),
"stackMode": MessageLookupByLibrary.simpleMessage("Режим стека"),
"standard": MessageLookupByLibrary.simpleMessage("Стандартный"),
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
@@ -817,11 +990,15 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("Остановка VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Стиль"),
"subRule": MessageLookupByLibrary.simpleMessage("Подправило"),
"subRuleEmpty": MessageLookupByLibrary.simpleMessage("Подправило пусто"),
"subRuleNotEmpty": MessageLookupByLibrary.simpleMessage(
"Подправило не может быть пустым",
),
"submit": MessageLookupByLibrary.simpleMessage("Отправить"),
"suspended": MessageLookupByLibrary.simpleMessage("Приостановлено..."),
"sync": MessageLookupByLibrary.simpleMessage("Синхронизация"),
"system": MessageLookupByLibrary.simpleMessage("Система"),
"systemApp": MessageLookupByLibrary.simpleMessage("Системное приложение"),
"systemFont": MessageLookupByLibrary.simpleMessage("Системный шрифт"),
"systemProxy": MessageLookupByLibrary.simpleMessage("Системный прокси"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
"Прикрепить HTTP-прокси к VpnService",
@@ -831,11 +1008,20 @@ class MessageLookup extends MessageLookupByLibrary {
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage(
"Действительно только в мобильном виде",
),
"tapToAuthorize": MessageLookupByLibrary.simpleMessage(
"Нажмите, чтобы разрешить",
),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP параллелизм"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage(
"Включение позволит использовать параллелизм TCP",
),
"testInterval": MessageLookupByLibrary.simpleMessage(
"Интервал тестирования",
),
"testUrl": MessageLookupByLibrary.simpleMessage("Тест URL"),
"testWhenUsed": MessageLookupByLibrary.simpleMessage(
"Тестировать при использовании",
),
"textScale": MessageLookupByLibrary.simpleMessage("Масштабирование текста"),
"theme": MessageLookupByLibrary.simpleMessage("Тема"),
"themeColor": MessageLookupByLibrary.simpleMessage("Цвет темы"),
@@ -843,9 +1029,9 @@ class MessageLookup extends MessageLookupByLibrary {
"Установить темный режим, настроить цвет",
),
"themeMode": MessageLookupByLibrary.simpleMessage("Режим темы"),
"threeColumns": MessageLookupByLibrary.simpleMessage("Три столбца"),
"tight": MessageLookupByLibrary.simpleMessage("Плотный"),
"time": MessageLookupByLibrary.simpleMessage("Время"),
"timeout": MessageLookupByLibrary.simpleMessage("Таймаут"),
"tip": MessageLookupByLibrary.simpleMessage("подсказка"),
"toggle": MessageLookupByLibrary.simpleMessage("Переключить"),
"tonalSpotScheme": MessageLookupByLibrary.simpleMessage("Тональный акцент"),
@@ -860,10 +1046,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"turnOff": MessageLookupByLibrary.simpleMessage("Выключить"),
"turnOn": MessageLookupByLibrary.simpleMessage("Включить"),
"twoColumns": MessageLookupByLibrary.simpleMessage("Два столбца"),
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"невозможно обновить текущий профиль",
),
"undo": MessageLookupByLibrary.simpleMessage("Отменить"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage(
"Унифицированная задержка",
@@ -882,7 +1064,7 @@ class MessageLookup extends MessageLookupByLibrary {
"urlDesc": MessageLookupByLibrary.simpleMessage(
"Получить профиль через URL",
),
"urlTip": m13,
"urlTip": m18,
"useHosts": MessageLookupByLibrary.simpleMessage("Использовать hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage(
"Использовать системные hosts",
@@ -893,15 +1075,9 @@ class MessageLookup extends MessageLookupByLibrary {
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"Обнаружено изменение конфигурации VPN",
),
"vpnDesc": MessageLookupByLibrary.simpleMessage(
"Изменение настроек, связанных с VPN",
),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
"Автоматически направляет весь системный трафик через VpnService",
),
"vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage(
"Прикрепить HTTP-прокси к VpnService",
),
"vpnTip": MessageLookupByLibrary.simpleMessage(
"Изменения вступят в силу после перезапуска VPN",
),
@@ -911,8 +1087,7 @@ class MessageLookup extends MessageLookupByLibrary {
"whitelistMode": MessageLookupByLibrary.simpleMessage(
"Режим белого списка",
),
"years": MessageLookupByLibrary.simpleMessage("Лет"),
"yearsAgo": m14,
"yearsAgo": m19,
"zh_CN": MessageLookupByLibrary.simpleMessage("Упрощенный китайский"),
};
}
+208 -84
View File
@@ -34,21 +34,32 @@ class MessageLookup extends MessageLookupByLibrary {
static String m6(count) => "${count} 小时前";
static String m7(count) => "${count} 分钟前";
static String m7(target) => "${target} 是一个无效的策略";
static String m8(count) => "${count} 个月前";
static String m8(proxyName) => "${proxyName} 是一个无效的代理";
static String m9(label) => "暂无${label}";
static String m9(providerName) => "${providerName} 是一个无效的代理集";
static String m10(label) => "${label}必须为数字";
static String m10(subRule) => "${subRule} 是一个无效的SUB_RULE";
static String m11(label) => "${label} 必须在 1024 到 49151 之间";
static String m11(appName) =>
"1. 打开 系统设置 > 隐私与安全性\n2. 选择 定位服务\n3. 在右侧列表中找到并勾选 ${appName}\n\n完成设置后,返回应用即可正常使用。感谢您的配合。";
static String m12(count) => "已选择 ${count} 项";
static String m12(count) => "${count} 分钟前";
static String m13(label) => "${label}必须为URL";
static String m13(count) => "${count} 个月前";
static String m14(count) => "${count} 年前";
static String m14(label) => "暂无${label}";
static String m15(label) => "${label}必须为数字";
static String m16(label) => "${label} 必须在 1024 到 49151 之间";
static String m17(count) => "已选择 ${count} 项";
static String m18(label) => "${label}必须为URL";
static String m19(count) => "${count} 年前";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -71,30 +82,30 @@ class MessageLookup extends MessageLookupByLibrary {
"action_view": MessageLookupByLibrary.simpleMessage("显示/隐藏"),
"add": MessageLookupByLibrary.simpleMessage("添加"),
"addProfile": MessageLookupByLibrary.simpleMessage("添加配置"),
"addProxies": MessageLookupByLibrary.simpleMessage("添加代理"),
"addProxyGroup": MessageLookupByLibrary.simpleMessage("添加策略组"),
"addProxyProviders": MessageLookupByLibrary.simpleMessage("添加代理集"),
"addRule": MessageLookupByLibrary.simpleMessage("添加规则"),
"addedOriginRules": MessageLookupByLibrary.simpleMessage("附加到原始规则"),
"addSsid": MessageLookupByLibrary.simpleMessage("添加SSID"),
"addedRules": MessageLookupByLibrary.simpleMessage("附加规则"),
"additionalParameters": MessageLookupByLibrary.simpleMessage("附加参数"),
"address": MessageLookupByLibrary.simpleMessage("地址"),
"addressHelp": MessageLookupByLibrary.simpleMessage("WebDAV服务器地址"),
"addressTip": MessageLookupByLibrary.simpleMessage("请输入有效的WebDAV地址"),
"adminAutoLaunch": MessageLookupByLibrary.simpleMessage("管理员自启动"),
"adminAutoLaunchDesc": MessageLookupByLibrary.simpleMessage("使用管理员模式开机自启动"),
"advancedConfig": MessageLookupByLibrary.simpleMessage("进阶配置"),
"advancedConfigDesc": MessageLookupByLibrary.simpleMessage("提供多样化配置"),
"ago": MessageLookupByLibrary.simpleMessage("前"),
"agree": MessageLookupByLibrary.simpleMessage("同意"),
"allApps": MessageLookupByLibrary.simpleMessage("所有应用"),
"allowBypass": MessageLookupByLibrary.simpleMessage("允许应用绕过VPN"),
"allowBypassDesc": MessageLookupByLibrary.simpleMessage("开启后部分应用可绕过VPN"),
"allowLan": MessageLookupByLibrary.simpleMessage("局域网代理"),
"allowLanDesc": MessageLookupByLibrary.simpleMessage("允许通过局域网访问代理"),
"app": MessageLookupByLibrary.simpleMessage("应用"),
"appAccessControl": MessageLookupByLibrary.simpleMessage("应用访问控制"),
"appDesc": MessageLookupByLibrary.simpleMessage("处理应用相关设置"),
"appendSystemDns": MessageLookupByLibrary.simpleMessage("追加系统DNS"),
"appendSystemDnsTip": MessageLookupByLibrary.simpleMessage("强制为配置附加系统DNS"),
"application": MessageLookupByLibrary.simpleMessage("应用程序"),
"applicationDesc": MessageLookupByLibrary.simpleMessage("修改应用程序相关设置"),
"authorized": MessageLookupByLibrary.simpleMessage("已授权"),
"auto": MessageLookupByLibrary.simpleMessage("自动"),
"autoCheckUpdate": MessageLookupByLibrary.simpleMessage("自动检查更新"),
"autoCheckUpdateDesc": MessageLookupByLibrary.simpleMessage("应用启动时自动检查更新"),
@@ -117,18 +128,23 @@ class MessageLookup extends MessageLookupByLibrary {
"backupSuccess": MessageLookupByLibrary.simpleMessage("备份成功"),
"basicConfig": MessageLookupByLibrary.simpleMessage("基本配置"),
"basicConfigDesc": MessageLookupByLibrary.simpleMessage("全局修改基本配置"),
"basicInfo": MessageLookupByLibrary.simpleMessage("基础信息"),
"basicStrategy": MessageLookupByLibrary.simpleMessage("基础策略"),
"batteryOptimizationDesc": MessageLookupByLibrary.simpleMessage(
"为保证后台运行,请关闭本应用的电池优化。点击前往设置。",
),
"batteryOptimizationStatusTip": MessageLookupByLibrary.simpleMessage(
"受系统影响,不代表一定准确",
),
"bind": MessageLookupByLibrary.simpleMessage("绑定"),
"blacklistMode": MessageLookupByLibrary.simpleMessage("黑名单模式"),
"bypassDomain": MessageLookupByLibrary.simpleMessage("排除域名"),
"bypassDomainDesc": MessageLookupByLibrary.simpleMessage("仅在系统代理启用时生效"),
"cacheCorrupt": MessageLookupByLibrary.simpleMessage("缓存已损坏,是否清空?"),
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
"cancelFilterSystemApp": MessageLookupByLibrary.simpleMessage("取消过滤系统应用"),
"cancelSelectAll": MessageLookupByLibrary.simpleMessage("取消全选"),
"checkError": MessageLookupByLibrary.simpleMessage("检测失败"),
"checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"),
"checking": MessageLookupByLibrary.simpleMessage("检测中..."),
"clearData": MessageLookupByLibrary.simpleMessage("清除数据"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("导出剪贴板"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("剪贴板导入"),
@@ -136,20 +152,23 @@ class MessageLookup extends MessageLookupByLibrary {
"colorSchemes": MessageLookupByLibrary.simpleMessage("配色方案"),
"columns": MessageLookupByLibrary.simpleMessage("列数"),
"compatible": MessageLookupByLibrary.simpleMessage("兼容模式"),
"compatibleDesc": MessageLookupByLibrary.simpleMessage(
"开启将失去部分应用能力,获得全量的Clash的支持",
),
"configDataDetected": MessageLookupByLibrary.simpleMessage("检测到配置中存在数据"),
"confirm": MessageLookupByLibrary.simpleMessage("确定"),
"confirmClearAllData": MessageLookupByLibrary.simpleMessage("确定要清除所有数据?"),
"confirmDeleteProxyGroup": MessageLookupByLibrary.simpleMessage(
"确定要删除当前策略组吗?",
),
"confirmExitWindow": MessageLookupByLibrary.simpleMessage("确定要退出当前窗口吗?"),
"confirmForceCrashCore": MessageLookupByLibrary.simpleMessage("确定要强制崩溃核心?"),
"confirmOverwriteTip": MessageLookupByLibrary.simpleMessage("确定后将会覆盖已有数据"),
"connected": MessageLookupByLibrary.simpleMessage("已连接"),
"connecting": MessageLookupByLibrary.simpleMessage("连接中..."),
"connection": MessageLookupByLibrary.simpleMessage("连接"),
"connections": MessageLookupByLibrary.simpleMessage("连接"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"contactMe": MessageLookupByLibrary.simpleMessage("联系我"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentNotEmpty": MessageLookupByLibrary.simpleMessage("内容不能为空"),
"contentScheme": MessageLookupByLibrary.simpleMessage("内容主题"),
"controlGlobalAddedRules": MessageLookupByLibrary.simpleMessage("控制全局附加规则"),
"copy": MessageLookupByLibrary.simpleMessage("复制"),
@@ -157,10 +176,6 @@ class MessageLookup extends MessageLookupByLibrary {
"copyLink": MessageLookupByLibrary.simpleMessage("复制链接"),
"copySuccess": MessageLookupByLibrary.simpleMessage("复制成功"),
"core": MessageLookupByLibrary.simpleMessage("内核"),
"coreConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"检测到核心配置更改",
),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"coreStatus": MessageLookupByLibrary.simpleMessage("核心状态"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"),
@@ -169,22 +184,22 @@ class MessageLookup extends MessageLookupByLibrary {
"开启后,应用崩溃时自动上传不包含敏感信息的崩溃日志",
),
"create": MessageLookupByLibrary.simpleMessage("创建"),
"createProfile": MessageLookupByLibrary.simpleMessage("创建配置"),
"creationTime": MessageLookupByLibrary.simpleMessage("创建时间"),
"custom": MessageLookupByLibrary.simpleMessage("自定义"),
"cut": MessageLookupByLibrary.simpleMessage("剪切"),
"dark": MessageLookupByLibrary.simpleMessage("深色"),
"dashboard": MessageLookupByLibrary.simpleMessage("仪表盘"),
"dataChangedSave": MessageLookupByLibrary.simpleMessage("检测到数据有更改,是否保存"),
"dataCollectionContent": MessageLookupByLibrary.simpleMessage(
"本应用使用 Firebase Crashlytics 收集崩溃信息以改进应用稳定性。\n收集的数据包括设备信息和崩溃详情,不包含个人敏感数据。\n您可以在设置中关闭此功能。",
),
"dataCollectionTip": MessageLookupByLibrary.simpleMessage("数据收集说明"),
"days": MessageLookupByLibrary.simpleMessage("天"),
"daysAgo": m0,
"defaultNameserver": MessageLookupByLibrary.simpleMessage("默认域名服务器"),
"defaultNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析DNS服务器"),
"defaultSort": MessageLookupByLibrary.simpleMessage("按默认排序"),
"defaultText": MessageLookupByLibrary.simpleMessage("默认"),
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
"delayTest": MessageLookupByLibrary.simpleMessage("延迟测试"),
"delete": MessageLookupByLibrary.simpleMessage("删除"),
"deleteMultipTip": m1,
@@ -200,13 +215,13 @@ class MessageLookup extends MessageLookupByLibrary {
"developerMode": MessageLookupByLibrary.simpleMessage("开发者模式"),
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage("开发者模式已启用。"),
"direct": MessageLookupByLibrary.simpleMessage("直连"),
"disableUDP": MessageLookupByLibrary.simpleMessage("禁用UDP"),
"disclaimer": MessageLookupByLibrary.simpleMessage("免责声明"),
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
"本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。",
),
"disconnected": MessageLookupByLibrary.simpleMessage("已断开"),
"discoverNewVersion": MessageLookupByLibrary.simpleMessage("发现新版本"),
"discovery": MessageLookupByLibrary.simpleMessage("发现新版本"),
"dnsDesc": MessageLookupByLibrary.simpleMessage("更新DNS相关设置"),
"dnsHijacking": MessageLookupByLibrary.simpleMessage("DNS劫持"),
"dnsMode": MessageLookupByLibrary.simpleMessage("DNS模式"),
@@ -215,17 +230,25 @@ class MessageLookup extends MessageLookupByLibrary {
"download": MessageLookupByLibrary.simpleMessage("下载"),
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
"editGlobalRules": MessageLookupByLibrary.simpleMessage("编辑全局规则"),
"editProxy": MessageLookupByLibrary.simpleMessage("编辑代理"),
"editProxyGroup": MessageLookupByLibrary.simpleMessage("编辑策略组"),
"editRule": MessageLookupByLibrary.simpleMessage("编辑规则"),
"editSsid": MessageLookupByLibrary.simpleMessage("编辑SSID"),
"emptyTip": m4,
"en": MessageLookupByLibrary.simpleMessage("英语"),
"enableOverride": MessageLookupByLibrary.simpleMessage("启用覆写"),
"entries": MessageLookupByLibrary.simpleMessage("个条目"),
"exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"),
"excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"),
"excludeProxyFilter": MessageLookupByLibrary.simpleMessage("排除节点过滤器"),
"excludeSsids": MessageLookupByLibrary.simpleMessage("排除SSIDs"),
"excludeSsidsDesc": MessageLookupByLibrary.simpleMessage(
"连接到被排除SSID的WIFI时,将会自动切换应用运行状态",
),
"excludeType": MessageLookupByLibrary.simpleMessage("排除类型"),
"existsTip": m5,
"exit": MessageLookupByLibrary.simpleMessage("退出"),
"expand": MessageLookupByLibrary.simpleMessage("标准"),
"expirationTime": MessageLookupByLibrary.simpleMessage("到期时间"),
"expectedStatus": MessageLookupByLibrary.simpleMessage("预期状态"),
"exportFile": MessageLookupByLibrary.simpleMessage("导出文件"),
"exportLogs": MessageLookupByLibrary.simpleMessage("导出日志"),
"exportSuccess": MessageLookupByLibrary.simpleMessage("导出成功"),
@@ -236,7 +259,6 @@ class MessageLookup extends MessageLookupByLibrary {
),
"externalFetch": MessageLookupByLibrary.simpleMessage("外部获取"),
"externalLink": MessageLookupByLibrary.simpleMessage("外部链接"),
"externalResources": MessageLookupByLibrary.simpleMessage("外部资源"),
"fakeipFilter": MessageLookupByLibrary.simpleMessage("Fakeip过滤"),
"fakeipRange": MessageLookupByLibrary.simpleMessage("Fakeip范围"),
"fallback": MessageLookupByLibrary.simpleMessage("Fallback"),
@@ -246,52 +268,62 @@ class MessageLookup extends MessageLookupByLibrary {
"file": MessageLookupByLibrary.simpleMessage("文件"),
"fileDesc": MessageLookupByLibrary.simpleMessage("直接上传配置文件"),
"fileIsUpdate": MessageLookupByLibrary.simpleMessage("文件有修改,是否保存修改"),
"filterSystemApp": MessageLookupByLibrary.simpleMessage("过滤系统应用"),
"findProcessMode": MessageLookupByLibrary.simpleMessage("查找进程"),
"findProcessModeDesc": MessageLookupByLibrary.simpleMessage("开启后会有一定性能损耗"),
"fontFamily": MessageLookupByLibrary.simpleMessage("字体"),
"forceRestartCoreTip": MessageLookupByLibrary.simpleMessage("您确定要强制重启核心吗?"),
"fourColumns": MessageLookupByLibrary.simpleMessage("四列"),
"fruitSaladScheme": MessageLookupByLibrary.simpleMessage("果缤纷"),
"general": MessageLookupByLibrary.simpleMessage("常规"),
"generalDesc": MessageLookupByLibrary.simpleMessage("修改通用设置"),
"geoData": MessageLookupByLibrary.simpleMessage("地理数据"),
"geodataLoader": MessageLookupByLibrary.simpleMessage("Geo低内存模式"),
"geodataLoaderDesc": MessageLookupByLibrary.simpleMessage("开启将使用Geo低内存加载器"),
"geoipCode": MessageLookupByLibrary.simpleMessage("Geoip代码"),
"getOriginRules": MessageLookupByLibrary.simpleMessage("获取原始规则"),
"global": MessageLookupByLibrary.simpleMessage("全局"),
"go": MessageLookupByLibrary.simpleMessage("前往"),
"goDownload": MessageLookupByLibrary.simpleMessage("前往下载"),
"goToConfigureScript": MessageLookupByLibrary.simpleMessage("前往配置脚本"),
"hasCacheChange": MessageLookupByLibrary.simpleMessage("是否缓存修改"),
"hideFromList": MessageLookupByLibrary.simpleMessage("从列表中隐藏"),
"host": MessageLookupByLibrary.simpleMessage("主机"),
"hostsDesc": MessageLookupByLibrary.simpleMessage("追加Hosts"),
"hotkeyConflict": MessageLookupByLibrary.simpleMessage("快捷键冲突"),
"hotkeyManagement": MessageLookupByLibrary.simpleMessage("快捷键管理"),
"hotkeyManagementDesc": MessageLookupByLibrary.simpleMessage("使用键盘控制应用程序"),
"hours": MessageLookupByLibrary.simpleMessage("小时"),
"hoursAgo": m6,
"icon": MessageLookupByLibrary.simpleMessage("图片"),
"iconConfiguration": MessageLookupByLibrary.simpleMessage("图片配置"),
"iconRecords": MessageLookupByLibrary.simpleMessage("图标记录"),
"iconStyle": MessageLookupByLibrary.simpleMessage("图标样式"),
"iconUrl": MessageLookupByLibrary.simpleMessage("图标链接"),
"ignoreBatteryOptimization": MessageLookupByLibrary.simpleMessage("忽略电池优化"),
"import": MessageLookupByLibrary.simpleMessage("导入"),
"importFile": MessageLookupByLibrary.simpleMessage("通过文件导入"),
"importFromURL": MessageLookupByLibrary.simpleMessage("从URL导入"),
"importUrl": MessageLookupByLibrary.simpleMessage("通过URL导入"),
"includeAllProxies": MessageLookupByLibrary.simpleMessage("包含所有代理"),
"includeAllProxiesTip": MessageLookupByLibrary.simpleMessage(
"引入不包含策略组的所有代理,可在下方额外添加策略组",
),
"includeAllProxyProviders": MessageLookupByLibrary.simpleMessage("包含所有代理集"),
"includeAllProxyProvidersTip": MessageLookupByLibrary.simpleMessage(
"开启后将覆盖引入的代理集",
),
"infiniteTime": MessageLookupByLibrary.simpleMessage("长期有效"),
"init": MessageLookupByLibrary.simpleMessage("初始化"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"),
"inputProxyGroupName": MessageLookupByLibrary.simpleMessage("输入策略组名称"),
"inputRuleContent": MessageLookupByLibrary.simpleMessage("输入规则内容"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"),
"internet": MessageLookupByLibrary.simpleMessage("互联网"),
"interval": MessageLookupByLibrary.simpleMessage("间隔"),
"intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"),
"invalidBackupFile": MessageLookupByLibrary.simpleMessage("无效备份文件"),
"invalidPolicy": m7,
"invalidProxy": m8,
"invalidProxyProvider": m9,
"invalidSubRule": m10,
"ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"),
"ipv6InboundDesc": MessageLookupByLibrary.simpleMessage("允许IPv6入站"),
"ja": MessageLookupByLibrary.simpleMessage("日语"),
"just": MessageLookupByLibrary.simpleMessage("刚刚"),
"justNow": MessageLookupByLibrary.simpleMessage("刚刚"),
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
"key": MessageLookupByLibrary.simpleMessage("键"),
@@ -304,6 +336,17 @@ class MessageLookup extends MessageLookupByLibrary {
"loading": MessageLookupByLibrary.simpleMessage("加载中..."),
"local": MessageLookupByLibrary.simpleMessage("本地"),
"localBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到本地"),
"locationPermission": MessageLookupByLibrary.simpleMessage("位置权限"),
"locationPermissionDeniedMessage": MessageLookupByLibrary.simpleMessage(
"位置权限已被拒绝,无法获取当前 Wi-Fi 名称。请前往系统设置手动开启位置权限。",
),
"locationPermissionDesc": MessageLookupByLibrary.simpleMessage(
"根据系统要求,获取Wi-Fi名称需要您授予位置权限。",
),
"locationPermissionGuide": m11,
"locationPermissionRequired": MessageLookupByLibrary.simpleMessage(
"需要位置权限",
),
"log": MessageLookupByLibrary.simpleMessage("日志"),
"logLevel": MessageLookupByLibrary.simpleMessage("日志等级"),
"logcat": MessageLookupByLibrary.simpleMessage("日志捕获"),
@@ -314,22 +357,21 @@ class MessageLookup extends MessageLookupByLibrary {
"loopback": MessageLookupByLibrary.simpleMessage("回环解锁工具"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"),
"loose": MessageLookupByLibrary.simpleMessage("宽松"),
"matchSourceIp": MessageLookupByLibrary.simpleMessage("匹配来源IP"),
"maxFailedTimes": MessageLookupByLibrary.simpleMessage("最大失败次数"),
"memoryInfo": MessageLookupByLibrary.simpleMessage("内存信息"),
"messageTest": MessageLookupByLibrary.simpleMessage("消息测试"),
"messageTestTip": MessageLookupByLibrary.simpleMessage("这是一条消息。"),
"min": MessageLookupByLibrary.simpleMessage("最小"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"),
"minutes": MessageLookupByLibrary.simpleMessage("分钟"),
"minutesAgo": m7,
"minutesAgo": m12,
"mixedPort": MessageLookupByLibrary.simpleMessage("混合端口"),
"mode": MessageLookupByLibrary.simpleMessage("模式"),
"monochromeScheme": MessageLookupByLibrary.simpleMessage("单色"),
"months": MessageLookupByLibrary.simpleMessage("月"),
"monthsAgo": m8,
"monthsAgo": m13,
"more": MessageLookupByLibrary.simpleMessage("更多"),
"name": MessageLookupByLibrary.simpleMessage("名称"),
"nameSort": MessageLookupByLibrary.simpleMessage("按名称排序"),
"nameserver": MessageLookupByLibrary.simpleMessage("域名服务器"),
"nameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析域名"),
"nameserverPolicy": MessageLookupByLibrary.simpleMessage("域名服务器策略"),
@@ -338,46 +380,39 @@ class MessageLookup extends MessageLookupByLibrary {
"networkDesc": MessageLookupByLibrary.simpleMessage("修改网络相关设置"),
"networkDetection": MessageLookupByLibrary.simpleMessage("网络检测"),
"networkException": MessageLookupByLibrary.simpleMessage("网络异常,请检查连接后重试"),
"networkRequestException": MessageLookupByLibrary.simpleMessage(
"网络请求异常,请稍后再试。",
),
"networkSpeed": MessageLookupByLibrary.simpleMessage("网络速度"),
"networkType": MessageLookupByLibrary.simpleMessage("网络类型"),
"neutralScheme": MessageLookupByLibrary.simpleMessage("中性"),
"noData": MessageLookupByLibrary.simpleMessage("暂无数据"),
"noHotKey": MessageLookupByLibrary.simpleMessage("暂无快捷键"),
"noIcon": MessageLookupByLibrary.simpleMessage("无图标"),
"noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"),
"noLongerRemind": MessageLookupByLibrary.simpleMessage("不再提示"),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"),
"noNetwork": MessageLookupByLibrary.simpleMessage("无网络"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("无网络应用"),
"noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"),
"noRecords": MessageLookupByLibrary.simpleMessage("暂无记录"),
"noResolve": MessageLookupByLibrary.simpleMessage("不解析IP"),
"noResolveHostname": MessageLookupByLibrary.simpleMessage("不解析主机名"),
"none": MessageLookupByLibrary.simpleMessage("无"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"),
"nullProfileDesc": MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"),
"nullTip": m9,
"numberTip": m10,
"oneColumn": MessageLookupByLibrary.simpleMessage("一列"),
"nullTip": m14,
"numberTip": m15,
"onDemand": MessageLookupByLibrary.simpleMessage("按需运行"),
"onDemandDesc": MessageLookupByLibrary.simpleMessage("配置程序特定场景运行状态"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage("仅第三方应用"),
"onlyStatisticsProxy": MessageLookupByLibrary.simpleMessage("仅统计代理"),
"onlyStatisticsProxyDesc": MessageLookupByLibrary.simpleMessage(
"开启后,将只统计代理流量",
),
"optional": MessageLookupByLibrary.simpleMessage("可选"),
"options": MessageLookupByLibrary.simpleMessage("选项"),
"other": MessageLookupByLibrary.simpleMessage("其他"),
"otherContributors": MessageLookupByLibrary.simpleMessage("其他贡献者"),
"outboundMode": MessageLookupByLibrary.simpleMessage("出站模式"),
"override": MessageLookupByLibrary.simpleMessage("覆写"),
"overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"),
"overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage("在脚本模式下不生效"),
"overrideMode": MessageLookupByLibrary.simpleMessage("覆写模式"),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("覆盖原始规则"),
"overrideScript": MessageLookupByLibrary.simpleMessage("覆写脚本"),
"overwriteTypeCustom": MessageLookupByLibrary.simpleMessage("自定义"),
"overwriteTypeCustomDesc": MessageLookupByLibrary.simpleMessage(
@@ -391,14 +426,14 @@ class MessageLookup extends MessageLookupByLibrary {
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"请输入管理员密码",
),
"pleaseUploadFile": MessageLookupByLibrary.simpleMessage("请上传文件"),
"pleaseUploadValidQrcode": MessageLookupByLibrary.simpleMessage(
"请上传有效的二维码",
),
"port": MessageLookupByLibrary.simpleMessage("端口"),
"portConflictTip": MessageLookupByLibrary.simpleMessage("请输入不同的端口"),
"portTip": m11,
"portTip": m16,
"preferH3Desc": MessageLookupByLibrary.simpleMessage("优先使用DOH的http/3"),
"prerequisites": MessageLookupByLibrary.simpleMessage("前置条件"),
"pressKeyboard": MessageLookupByLibrary.simpleMessage("请按下按键"),
"preview": MessageLookupByLibrary.simpleMessage("预览"),
"process": MessageLookupByLibrary.simpleMessage("进程"),
@@ -413,7 +448,6 @@ class MessageLookup extends MessageLookupByLibrary {
"profileNameNullValidationDesc": MessageLookupByLibrary.simpleMessage(
"请输入配置名称",
),
"profileParseErrorDesc": MessageLookupByLibrary.simpleMessage("配置文件解析错误"),
"profileUrlInvalidValidationDesc": MessageLookupByLibrary.simpleMessage(
"请输入有效配置URL",
),
@@ -425,23 +459,37 @@ class MessageLookup extends MessageLookupByLibrary {
"project": MessageLookupByLibrary.simpleMessage("项目"),
"providers": MessageLookupByLibrary.simpleMessage("提供者"),
"proxies": MessageLookupByLibrary.simpleMessage("代理"),
"proxiesSetting": MessageLookupByLibrary.simpleMessage("代理设置"),
"proxiesEmpty": MessageLookupByLibrary.simpleMessage("代理为空"),
"proxyChains": MessageLookupByLibrary.simpleMessage("代理链"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("代理组"),
"proxyDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"检测到选中的代理存在异常",
),
"proxyFilter": MessageLookupByLibrary.simpleMessage("节点过滤器"),
"proxyGroup": MessageLookupByLibrary.simpleMessage("策略组"),
"proxyGroupDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"检测到当前策略组异常",
),
"proxyGroupEmpty": MessageLookupByLibrary.simpleMessage("策略组为空"),
"proxyGroupNameDuplicate": MessageLookupByLibrary.simpleMessage("策略组名称重复"),
"proxyGroupNameEmpty": MessageLookupByLibrary.simpleMessage("策略组名称不能为空"),
"proxyNameserver": MessageLookupByLibrary.simpleMessage("代理域名服务器"),
"proxyNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析代理节点的域名"),
"proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"),
"proxyProviders": MessageLookupByLibrary.simpleMessage("代理提供者"),
"proxyProviderDetectedAbnormal": MessageLookupByLibrary.simpleMessage(
"检测到选中的代理集存在异常",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("代理集"),
"proxyProvidersEmpty": MessageLookupByLibrary.simpleMessage("代理集为空"),
"proxyProvidersNotEmpty": MessageLookupByLibrary.simpleMessage("代理集不能为空"),
"proxyType": MessageLookupByLibrary.simpleMessage("代理类型"),
"pruneCache": MessageLookupByLibrary.simpleMessage("修剪缓存"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"),
"qrcode": MessageLookupByLibrary.simpleMessage("二维码"),
"qrcodeDesc": MessageLookupByLibrary.simpleMessage("扫描二维码获取配置文件"),
"quickFill": MessageLookupByLibrary.simpleMessage("一键填入"),
"rainbowScheme": MessageLookupByLibrary.simpleMessage("彩虹"),
"redirPort": MessageLookupByLibrary.simpleMessage("Redir端口"),
"redo": MessageLookupByLibrary.simpleMessage("重做"),
"regExp": MessageLookupByLibrary.simpleMessage("正则"),
"reload": MessageLookupByLibrary.simpleMessage("重载"),
"remote": MessageLookupByLibrary.simpleMessage("远程"),
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"),
"remoteDestination": MessageLookupByLibrary.simpleMessage("远程目标"),
@@ -482,12 +530,84 @@ class MessageLookup extends MessageLookupByLibrary {
"routeMode_config": MessageLookupByLibrary.simpleMessage("使用配置"),
"ru": MessageLookupByLibrary.simpleMessage("俄语"),
"rule": MessageLookupByLibrary.simpleMessage("规则"),
"ruleActionAndDesc": MessageLookupByLibrary.simpleMessage("逻辑规则 AND"),
"ruleActionDomainDesc": MessageLookupByLibrary.simpleMessage("匹配完整域名"),
"ruleActionDomainKeywordDesc": MessageLookupByLibrary.simpleMessage(
"匹配域名关键字",
),
"ruleActionDomainRegexDesc": MessageLookupByLibrary.simpleMessage(
"通配符匹配,仅支持*和?通配符",
),
"ruleActionDomainSuffixDesc": MessageLookupByLibrary.simpleMessage(
"匹配域名后缀",
),
"ruleActionDscpDesc": MessageLookupByLibrary.simpleMessage(
"匹配DSCP标记 (仅限 tproxy udp 入站)",
),
"ruleActionDstPortDesc": MessageLookupByLibrary.simpleMessage("匹配请求目标端口范围"),
"ruleActionGeoipDesc": MessageLookupByLibrary.simpleMessage("匹配 IP 所属国家代码"),
"ruleActionGeositeDesc": MessageLookupByLibrary.simpleMessage(
"匹配 Geosite 内的域名",
),
"ruleActionInNameDesc": MessageLookupByLibrary.simpleMessage("匹配入站名称"),
"ruleActionInPortDesc": MessageLookupByLibrary.simpleMessage("匹配入站端口"),
"ruleActionInTypeDesc": MessageLookupByLibrary.simpleMessage("匹配入站类型"),
"ruleActionInUserDesc": MessageLookupByLibrary.simpleMessage(
"匹配入站用户名,支持使用 / 分隔多个用户名",
),
"ruleActionIpAsnDesc": MessageLookupByLibrary.simpleMessage("匹配 IP 所属 ASN"),
"ruleActionIpCidr6Desc": MessageLookupByLibrary.simpleMessage(
"匹配 IP 地址范围, IP-CIDR6 只是一个别名",
),
"ruleActionIpCidrDesc": MessageLookupByLibrary.simpleMessage("匹配 IP 地址范围"),
"ruleActionIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"匹配 IP 后缀范围",
),
"ruleActionMatchDesc": MessageLookupByLibrary.simpleMessage("匹配所有请求,无需条件"),
"ruleActionNetworkDesc": MessageLookupByLibrary.simpleMessage("匹配TCP或者UDP"),
"ruleActionNotDesc": MessageLookupByLibrary.simpleMessage("逻辑规则 NOT"),
"ruleActionOrDesc": MessageLookupByLibrary.simpleMessage("逻辑规则 OR"),
"ruleActionProcessNameDesc": MessageLookupByLibrary.simpleMessage(
"使用进程匹配,在Android平台可以匹配包名",
),
"ruleActionProcessNameRegexDesc": MessageLookupByLibrary.simpleMessage(
"使用进程名称正则表达式匹配,在Android平台可以匹配包名",
),
"ruleActionProcessPathDesc": MessageLookupByLibrary.simpleMessage(
"使用完整进程路径匹配",
),
"ruleActionProcessPathRegexDesc": MessageLookupByLibrary.simpleMessage(
"使用进程路径正则表达式匹配",
),
"ruleActionRuleSetDesc": MessageLookupByLibrary.simpleMessage(
"引用规则集合,需配置rule-providers",
),
"ruleActionSrcGeoipDesc": MessageLookupByLibrary.simpleMessage(
"匹配来源 IP 所属国家代码",
),
"ruleActionSrcIpAsnDesc": MessageLookupByLibrary.simpleMessage(
"匹配来源 IP 所属 ASN",
),
"ruleActionSrcIpCidrDesc": MessageLookupByLibrary.simpleMessage(
"匹配来源 IP 地址范围",
),
"ruleActionSrcIpSuffixDesc": MessageLookupByLibrary.simpleMessage(
"匹配来源 IP 后缀范围",
),
"ruleActionSrcPortDesc": MessageLookupByLibrary.simpleMessage("匹配请求来源端口范围"),
"ruleActionSubRuleDesc": MessageLookupByLibrary.simpleMessage(
"匹配至子规则,需要注意括号的使用",
),
"ruleActionUidDesc": MessageLookupByLibrary.simpleMessage(
"匹配 Linux USER ID",
),
"ruleEmpty": MessageLookupByLibrary.simpleMessage("规则为空"),
"ruleName": MessageLookupByLibrary.simpleMessage("规则名称"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则集"),
"ruleSet": MessageLookupByLibrary.simpleMessage("规则集"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("规则目标"),
"save": MessageLookupByLibrary.simpleMessage("保存"),
"saveChanges": MessageLookupByLibrary.simpleMessage("是否保存更改?"),
"saveTip": MessageLookupByLibrary.simpleMessage("确定要保存吗?"),
"script": MessageLookupByLibrary.simpleMessage("脚本"),
"scriptModeDesc": MessageLookupByLibrary.simpleMessage(
"脚本模式,使用外部扩展脚本,提供一键覆写配置的能力",
@@ -495,8 +615,13 @@ class MessageLookup extends MessageLookupByLibrary {
"search": MessageLookupByLibrary.simpleMessage("搜索"),
"seconds": MessageLookupByLibrary.simpleMessage("秒"),
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
"selectProxies": MessageLookupByLibrary.simpleMessage("选择代理"),
"selectProxyProviders": MessageLookupByLibrary.simpleMessage("选择代理集"),
"selectRuleSet": MessageLookupByLibrary.simpleMessage("请选择规则集"),
"selectSplitStrategy": MessageLookupByLibrary.simpleMessage("请选择分流策略"),
"selectSubRule": MessageLookupByLibrary.simpleMessage("请选择子规则"),
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
"selectedCountTitle": m12,
"selectedCountTitle": m17,
"settings": MessageLookupByLibrary.simpleMessage("设置"),
"show": MessageLookupByLibrary.simpleMessage("显示"),
"shrink": MessageLookupByLibrary.simpleMessage("紧凑"),
@@ -510,6 +635,9 @@ class MessageLookup extends MessageLookupByLibrary {
"specialProxy": MessageLookupByLibrary.simpleMessage("特殊代理"),
"specialRules": MessageLookupByLibrary.simpleMessage("特殊规则"),
"speedStatistics": MessageLookupByLibrary.simpleMessage("网速统计"),
"splitStrategy": MessageLookupByLibrary.simpleMessage("分流策略"),
"splitStrategyNotEmpty": MessageLookupByLibrary.simpleMessage("分流策略不能为空"),
"ssidsEmpty": MessageLookupByLibrary.simpleMessage("SSIDs为空"),
"stackMode": MessageLookupByLibrary.simpleMessage("栈模式"),
"standard": MessageLookupByLibrary.simpleMessage("标准"),
"standardModeDesc": MessageLookupByLibrary.simpleMessage(
@@ -523,27 +651,32 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
"style": MessageLookupByLibrary.simpleMessage("风格"),
"subRule": MessageLookupByLibrary.simpleMessage("子规则"),
"subRuleEmpty": MessageLookupByLibrary.simpleMessage("子规则为空"),
"subRuleNotEmpty": MessageLookupByLibrary.simpleMessage("子规则不能为空"),
"submit": MessageLookupByLibrary.simpleMessage("提交"),
"suspended": MessageLookupByLibrary.simpleMessage("挂起中..."),
"sync": MessageLookupByLibrary.simpleMessage("同步"),
"system": MessageLookupByLibrary.simpleMessage("系统"),
"systemApp": MessageLookupByLibrary.simpleMessage("系统应用"),
"systemFont": MessageLookupByLibrary.simpleMessage("系统字体"),
"systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"),
"tab": MessageLookupByLibrary.simpleMessage("标签页"),
"tabAnimation": MessageLookupByLibrary.simpleMessage("选项卡动画"),
"tabAnimationDesc": MessageLookupByLibrary.simpleMessage("仅在移动视图中有效"),
"tapToAuthorize": MessageLookupByLibrary.simpleMessage("点击授权"),
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
"testInterval": MessageLookupByLibrary.simpleMessage("测试间隔"),
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
"testWhenUsed": MessageLookupByLibrary.simpleMessage("使用时测试"),
"textScale": MessageLookupByLibrary.simpleMessage("文本缩放"),
"theme": MessageLookupByLibrary.simpleMessage("主题"),
"themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"),
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),
"themeMode": MessageLookupByLibrary.simpleMessage("主题模式"),
"threeColumns": MessageLookupByLibrary.simpleMessage("三列"),
"tight": MessageLookupByLibrary.simpleMessage("紧凑"),
"time": MessageLookupByLibrary.simpleMessage("时间"),
"timeout": MessageLookupByLibrary.simpleMessage("超时"),
"tip": MessageLookupByLibrary.simpleMessage("提示"),
"toggle": MessageLookupByLibrary.simpleMessage("切换"),
"tonalSpotScheme": MessageLookupByLibrary.simpleMessage("调性点缀"),
@@ -554,10 +687,6 @@ class MessageLookup extends MessageLookupByLibrary {
"tunDesc": MessageLookupByLibrary.simpleMessage("仅在管理员模式生效"),
"turnOff": MessageLookupByLibrary.simpleMessage("关闭"),
"turnOn": MessageLookupByLibrary.simpleMessage("开启"),
"twoColumns": MessageLookupByLibrary.simpleMessage("两列"),
"unableToUpdateCurrentProfileDesc": MessageLookupByLibrary.simpleMessage(
"无法更新当前配置文件",
),
"undo": MessageLookupByLibrary.simpleMessage("撤销"),
"unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"),
@@ -568,7 +697,7 @@ class MessageLookup extends MessageLookupByLibrary {
"upload": MessageLookupByLibrary.simpleMessage("上传"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"),
"urlTip": m13,
"urlTip": m18,
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
"value": MessageLookupByLibrary.simpleMessage("值"),
@@ -577,18 +706,13 @@ class MessageLookup extends MessageLookupByLibrary {
"vpnConfigChangeDetected": MessageLookupByLibrary.simpleMessage(
"检测到VPN相关配置改动",
),
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),
"vpnEnableDesc": MessageLookupByLibrary.simpleMessage(
"通过VpnService自动路由系统所有流量",
),
"vpnSystemProxyDesc": MessageLookupByLibrary.simpleMessage(
"为VpnService附加HTTP代理",
),
"vpnTip": MessageLookupByLibrary.simpleMessage("重启VPN后改变生效"),
"webDAVConfiguration": MessageLookupByLibrary.simpleMessage("WebDAV配置"),
"whitelistMode": MessageLookupByLibrary.simpleMessage("白名单模式"),
"years": MessageLookupByLibrary.simpleMessage("年"),
"yearsAgo": m14,
"yearsAgo": m19,
"zh_CN": MessageLookupByLibrary.simpleMessage("中文简体"),
};
}
+1070 -440
View File
File diff suppressed because it is too large Load Diff
+4
View File
@@ -5,6 +5,7 @@ import 'package:fl_clash/pages/error.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:rust_api/rust_api.dart';
import 'application.dart';
import 'common/common.dart';
@@ -12,6 +13,9 @@ import 'common/common.dart';
Future<void> main() async {
try {
WidgetsFlutterBinding.ensureInitialized();
if (system.isDesktop) {
await RustLib.init();
}
final version = await system.version;
final container = await globalState.init(version);
HttpOverrides.global = FlClashHttpOverrides();
+1 -1
View File
@@ -32,7 +32,7 @@ class _AndroidContainerState extends ConsumerState<AndroidManager>
if (prev != next) {
debouncer.call(FunctionTag.saveSharedFile, () async {
preferences.saveShareState(next);
}, duration: Duration(seconds: 1));
}, duration: const Duration(seconds: 1));
if (prev?.needSyncSharedState != next.needSyncSharedState) {
service?.syncState(next.needSyncSharedState);
}
+47 -25
View File
@@ -1,7 +1,7 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/controller.dart';
import 'package:fl_clash/core/controller.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/manager/window_manager.dart';
import 'package:fl_clash/providers/providers.dart';
@@ -33,27 +33,43 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
});
ref.listenManual(configProvider, (prev, next) {
if (prev != next) {
appController.savePreferencesDebounce();
globalState.container
.read(storeActionProvider.notifier)
.savePreferencesDebounce();
}
});
ref.listenManual(needUpdateGroupsProvider, (prev, next) {
if (prev != next) {
appController.updateGroupsDebounce();
globalState.container
.read(proxiesActionProvider.notifier)
.updateGroupsDebounce();
}
});
if (window == null) {
return;
ref.listenManual(suspendProvider, (prev, next) {
final isStart = ref.read(isStartProvider);
if (prev != next && isStart) {
debouncer.call(FunctionTag.suspend, () async {
if (next == true) {
await coreController.stopListener();
} else {
await coreController.startListener();
}
ref.read(checkIpNumProvider.notifier).add();
});
}
});
if (system.isMacOS) {
ref.listenManual(autoSetSystemDnsStateProvider, (prev, next) async {
if (prev == next) {
return;
}
if (next.a == true && next.b == true) {
macOS?.updateDns(false);
} else {
macOS?.updateDns(true);
}
});
}
ref.listenManual(autoSetSystemDnsStateProvider, (prev, next) async {
if (prev == next) {
return;
}
if (next.a == true && next.b == true) {
macOS?.updateDns(false);
} else {
macOS?.updateDns(true);
}
});
}
@override
@@ -66,11 +82,13 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {
commonPrint.log('$state');
if (state == AppLifecycleState.resumed) {
permissions.check();
render?.resume();
WidgetsBinding.instance.addPostFrameCallback((_) {
appController.tryCheckIp();
final ref = globalState.container;
ref.read(setupActionProvider.notifier).tryCheckIp();
if (system.isAndroid) {
appController.tryStartCore();
ref.read(coreActionProvider.notifier).tryStartCore();
}
});
}
@@ -78,7 +96,7 @@ class _AppStateManagerState extends ConsumerState<AppStateManager>
@override
void didChangePlatformBrightness() {
appController.updateBrightness();
globalState.container.read(themeActionProvider.notifier).updateBrightness();
}
@override
@@ -164,6 +182,12 @@ class AppSidebarContainer extends ConsumerWidget {
});
}
void _handleToPage(PageLabel pageLabel) {
globalState.container
.read(currentPageLabelProvider.notifier)
.toPage(pageLabel);
}
@override
Widget build(BuildContext context, WidgetRef ref) {
final navigationState = ref.watch(navigationStateProvider);
@@ -182,11 +206,11 @@ class AppSidebarContainer extends ConsumerWidget {
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
if (system.isMacOS) SizedBox(height: 22),
SizedBox(height: 10),
if (system.isMacOS) const SizedBox(height: 22),
const SizedBox(height: 10),
if (!system.isMacOS) ...[
ClipRect(child: AppIcon()),
SizedBox(height: 12),
const ClipRect(child: AppIcon()),
const SizedBox(height: 12),
],
Expanded(
child: ScrollConfiguration(
@@ -216,9 +240,7 @@ class AppSidebarContainer extends ConsumerWidget {
)
.toList(),
onDestinationSelected: (index) {
appController.toPage(
navigationItems[index].label,
);
_handleToPage(navigationItems[index].label);
},
extended: false,
selectedIndex: currentIndex,
+14 -1
View File
@@ -1,7 +1,12 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:wifi_ssid/wifi_ssid.dart';
class ConnectivityManager extends StatefulWidget {
final Function(List<ConnectivityResult> results)? onConnectivityChanged;
@@ -23,7 +28,15 @@ class _ConnectivityManagerState extends State<ConnectivityManager> {
@override
void initState() {
super.initState();
subscription = Connectivity().onConnectivityChanged.listen((results) async {
subscription = Connectivity().onConnectivityChanged.listen((results) {
if (results.contains(ConnectivityResult.wifi)) {
WifiSsidManager.instance.getSsid().then((ssid) {
globalState.container.read(currentSSIDProvider.notifier).value = ssid;
commonPrint.log('Wi-fi SSID: $ssid ', logLevel: LogLevel.info);
});
} else {
globalState.container.read(currentSSIDProvider.notifier).value = null;
}
if (widget.onConnectivityChanged != null) {
widget.onConnectivityChanged!(results);
}

Some files were not shown because too many files have changed in this diff Show More