From 7e7f1f89f363c31bbb7aa5af3f34a8b5c32a7a19 Mon Sep 17 00:00:00 2001
From: chen08209
Date: Wed, 27 May 2026 15:16:51 +0800
Subject: [PATCH] Fix macos performance issue
Support custom global-ua
Update core
Optimize some details
---
.agents/agent-config.md | 28 +
.agents/architecture.md | 156 ++++
.agents/commands.md | 106 +++
.agents/project.md | 28 +
.agents/rules.md | 47 ++
.agents/skills.md | 17 +
.agents/skills/core-platform/SKILL.md | 33 +
.agents/skills/localization/SKILL.md | 45 ++
.agents/skills/provider-tests/SKILL.md | 37 +
.agents/skills/ui-work/SKILL.md | 32 +
.claude/commands/i18n.md | 93 ---
.codegraph/.gitignore | 5 +
.gemini/settings.json | 12 +
.github/homebrew_cask_template.rb | 39 +
.github/release_template.md | 2 +-
.github/workflows/build.yaml | 77 +-
.gitmodules | 10 -
.mcp.json | 12 +
.pre-commit-config.yaml | 20 +
.zed/settings.json | 13 +
AGENTS.md | 246 +-----
CLAUDE.md | 7 +-
Makefile | 33 +
README.md | 9 +-
README_zh_CN.md | 7 +
android/app/build.gradle.kts | 5 +-
android/build.gradle.kts | 15 +
android/common/build.gradle.kts | 3 +-
android/common/consumer-rules.pro | 0
android/core/build.gradle.kts | 1 -
android/gradle.properties | 4 +
android/gradle/libs.versions.toml | 11 +-
.../gradle/wrapper/gradle-wrapper.properties | 2 +-
android/service/build.gradle.kts | 4 +-
.../service/models/NotificationParams.kt | 31 +-
.../follow/clash/service/models/VpnOptions.kt | 90 ++-
android/settings.gradle.kts | 8 +-
arb/intl_en.arb | 15 +-
arb/intl_ja.arb | 15 +-
arb/intl_ru.arb | 15 +-
arb/intl_zh_CN.arb | 15 +-
assets/images/icon/status_2.ico | Bin 2014 -> 2143 bytes
assets/images/icon/status_2.png | Bin 8455 -> 10683 bytes
assets/images/icon/status_3.ico | Bin 2353 -> 2579 bytes
assets/images/icon/status_3.png | Bin 10748 -> 12676 bytes
assets_source/images/icon/status_1.svg | 40 +
assets_source/images/icon/status_2.svg | 36 +
assets_source/images/icon/status_3.svg | 40 +
core/Clash.Meta | 2 +-
core/action.go | 16 +-
core/common.go | 23 +-
core/constant.go | 18 +-
core/go.mod | 32 +-
core/go.sum | 79 +-
core/hub.go | 46 +-
lib/common/common.dart | 2 +-
lib/common/fixed.dart | 16 +-
lib/common/input_limits.dart | 38 +
lib/common/iterable.dart | 7 +
lib/common/keyboard.dart | 168 ++--
lib/common/link.dart | 23 +-
lib/common/package.dart | 8 +-
lib/common/picker.dart | 20 +-
lib/common/pipe.dart | 234 ------
lib/common/protocol.dart | 50 +-
lib/common/request.dart | 60 +-
lib/common/system.dart | 1 +
lib/common/task.dart | 10 +-
lib/common/text.dart | 9 +-
lib/core/controller.dart | 4 +-
lib/core/event.dart | 16 +
lib/core/interface.dart | 6 +-
lib/core/transport.dart | 84 +-
lib/enum/enum.dart | 36 +-
lib/features/overwrite/rule.dart | 5 +-
lib/l10n/intl/messages_en.dart | 89 ++-
lib/l10n/intl/messages_ja.dart | 87 +-
lib/l10n/intl/messages_ru.dart | 89 ++-
lib/l10n/intl/messages_zh_CN.dart | 87 +-
lib/l10n/l10n.dart | 115 +++
lib/main.dart | 4 +-
lib/manager/core_manager.dart | 23 +-
lib/manager/hotkey_manager.dart | 6 +-
lib/manager/theme_manager.dart | 55 +-
lib/manager/window_manager.dart | 9 +-
lib/models/clash_config.dart | 76 +-
lib/models/common.dart | 3 +-
lib/models/config.dart | 1 +
lib/models/core.dart | 2 +
.../generated/clash_config.freezed.dart | 354 +--------
lib/models/generated/clash_config.g.dart | 30 +-
lib/models/generated/config.freezed.dart | 43 +-
lib/models/generated/config.g.dart | 2 +
lib/models/generated/core.freezed.dart | 46 +-
lib/models/generated/core.g.dart | 5 +
lib/pages/editor.dart | 10 +-
lib/pages/home.dart | 48 +-
lib/pages/scan.dart | 5 +-
lib/plugins/app.dart | 8 +-
lib/providers/action.dart | 35 +-
lib/providers/database.dart | 44 +-
lib/providers/generated/action.g.dart | 58 +-
lib/providers/generated/app.g.dart | 2 +-
lib/providers/generated/database.g.dart | 14 +-
lib/providers/generated/state.g.dart | 4 +-
lib/providers/state.dart | 2 +
lib/state.dart | 13 +-
lib/views/application_setting.dart | 5 +-
lib/views/backup_and_restore.dart | 8 +-
lib/views/config/dns.dart | 13 +
lib/views/config/general.dart | 254 +++++-
lib/views/config/network.dart | 2 +
lib/views/config/on_demand.dart | 10 +-
lib/views/config/rules.dart | 7 +-
lib/views/config/scripts.dart | 1 +
lib/views/dashboard/dashboard.dart | 5 +-
lib/views/dashboard/widgets/widgets.dart | 2 +-
lib/views/logs.dart | 4 +-
lib/views/profiles/add.dart | 2 +
lib/views/profiles/edit.dart | 9 +-
.../profiles/overwrite/custom/groups.dart | 15 +-
lib/views/profiles/overwrite/custom/icon.dart | 3 +
.../profiles/overwrite/custom/proxies.dart | 12 +-
.../overwrite/custom/proxy_providers.dart | 9 +-
.../profiles/overwrite/custom/rules.dart | 5 +-
.../profiles/overwrite/custom/widgets.dart | 4 +-
lib/views/profiles/overwrite/standard.dart | 2 +-
lib/views/profiles/preview.dart | 6 +-
lib/views/profiles/profiles.dart | 8 +-
lib/views/proxies/providers.dart | 6 +-
lib/views/resources.dart | 383 +++++----
lib/views/theme.dart | 135 +---
lib/views/tools.dart | 6 +-
lib/widgets/activate_box.dart | 11 +-
lib/widgets/animate_grid.dart | 103 ++-
lib/widgets/bar_chart.dart | 51 +-
lib/widgets/chip.dart | 10 +-
lib/widgets/container.dart | 16 +-
lib/widgets/dialog.dart | 20 +-
lib/widgets/disabled_mask.dart | 11 +-
lib/widgets/dismissible.dart | 2 +-
lib/widgets/donut_chart.dart | 13 +-
lib/widgets/float_layout.dart | 13 +-
lib/widgets/input.dart | 47 +-
lib/widgets/keep_scope.dart | 6 +-
lib/widgets/list.dart | 24 +-
lib/widgets/notification.dart | 16 +-
lib/widgets/palette.dart | 746 +++++++++---------
lib/widgets/scaffold.dart | 1 +
lib/widgets/sheet.dart | 12 +-
lib/widgets/theme.dart | 108 +++
lib/widgets/wave.dart | 47 +-
macos/Podfile.lock | 24 -
opencode.jsonc | 14 +
plugins/flutter_distributor | 1 -
plugins/rust_api/android/build.gradle | 15 +-
plugins/setup/android/build.gradle | 8 +-
.../build_tool/lib/src/rust_builder.dart | 7 +-
.../buildkit/build_tool/lib/src/util.dart | 3 +-
.../buildkit/build_tool/test/util_test.dart | 3 +-
plugins/setup/buildkit/run_build_tool.cmd | 2 +
plugins/setup/buildkit/run_build_tool.sh | 2 +
plugins/tray_manager | 1 -
plugins/wifi_ssid/android/build.gradle | 12 +-
plugins/wifi_ssid/macos/wifi_ssid.podspec | 20 -
.../wifi_ssid/macos/wifi_ssid/Package.swift | 30 +
.../Sources/wifi_ssid}/WifiSsidPlugin.swift | 4 +-
plugins/window_ext/lib/window_ext.dart | 2 +-
plugins/window_ext/macos/window_ext.podspec | 23 -
.../window_ext/macos/window_ext/Package.swift | 26 +
.../Sources/window_ext}/WindowExtPlugin.swift | 12 +-
pubspec.lock | 225 +++---
pubspec.yaml | 42 +-
setup.dart | 37 +-
test/common/converter_test.dart | 50 ++
test/common/datetime_test.dart | 29 +
test/common/delay_test.dart | 28 +-
test/common/iterable_test.dart | 22 +
test/common/picker_test.dart | 29 +
test/common/protocol_test.dart | 19 +
test/core/controller_test.dart | 5 +-
test/enum/enum_test.dart | 114 +++
test/models/common_test.dart | 150 ++++
test/models/config_test.dart | 111 +++
test/models/profile_test.dart | 107 +++
test/providers/action_test.dart | 25 +
test/providers/app_test.dart | 112 ++-
test/providers/config_test.dart | 100 ++-
test/widgets/input_test.dart | 111 +++
test/widgets/state_test.dart | 95 +++
tool/bump_version.sh | 60 ++
tool/generate_status_icons.dart | 115 +++
192 files changed, 4904 insertions(+), 2910 deletions(-)
create mode 100644 .agents/agent-config.md
create mode 100644 .agents/architecture.md
create mode 100644 .agents/commands.md
create mode 100644 .agents/project.md
create mode 100644 .agents/rules.md
create mode 100644 .agents/skills.md
create mode 100644 .agents/skills/core-platform/SKILL.md
create mode 100644 .agents/skills/localization/SKILL.md
create mode 100644 .agents/skills/provider-tests/SKILL.md
create mode 100644 .agents/skills/ui-work/SKILL.md
delete mode 100644 .claude/commands/i18n.md
create mode 100644 .codegraph/.gitignore
create mode 100644 .gemini/settings.json
create mode 100644 .github/homebrew_cask_template.rb
create mode 100644 .mcp.json
create mode 100644 .pre-commit-config.yaml
create mode 100644 .zed/settings.json
create mode 100644 Makefile
create mode 100644 android/common/consumer-rules.pro
create mode 100644 assets_source/images/icon/status_1.svg
create mode 100644 assets_source/images/icon/status_2.svg
create mode 100644 assets_source/images/icon/status_3.svg
create mode 100644 lib/common/input_limits.dart
delete mode 100644 lib/common/pipe.dart
create mode 100644 opencode.jsonc
delete mode 160000 plugins/flutter_distributor
delete mode 160000 plugins/tray_manager
delete mode 100644 plugins/wifi_ssid/macos/wifi_ssid.podspec
create mode 100644 plugins/wifi_ssid/macos/wifi_ssid/Package.swift
rename plugins/wifi_ssid/macos/{Classes => wifi_ssid/Sources/wifi_ssid}/WifiSsidPlugin.swift (100%)
delete mode 100644 plugins/window_ext/macos/window_ext.podspec
create mode 100644 plugins/window_ext/macos/window_ext/Package.swift
rename plugins/window_ext/macos/{Classes => window_ext/Sources/window_ext}/WindowExtPlugin.swift (87%)
mode change 100755 => 100644 pubspec.yaml
create mode 100644 test/common/converter_test.dart
create mode 100644 test/common/datetime_test.dart
create mode 100644 test/common/picker_test.dart
create mode 100644 test/common/protocol_test.dart
create mode 100644 test/enum/enum_test.dart
create mode 100644 test/models/common_test.dart
create mode 100644 test/models/profile_test.dart
create mode 100644 test/widgets/input_test.dart
create mode 100644 test/widgets/state_test.dart
create mode 100755 tool/bump_version.sh
create mode 100644 tool/generate_status_icons.dart
diff --git a/.agents/agent-config.md b/.agents/agent-config.md
new file mode 100644
index 00000000..bcd0b158
--- /dev/null
+++ b/.agents/agent-config.md
@@ -0,0 +1,28 @@
+# Agent Configuration Model
+
+Use the smallest durable surface that matches the job.
+
+## Surfaces
+
+- `AGENTS.md`: auto-loaded repository entry point. Keep it small and reserve it for always-on rules, routing, and high-priority expectations.
+- `.agents/*.md`: human- and agent-readable reference docs linked from `AGENTS.md`. Use these for detailed project context, commands, architecture, and conventions.
+- `.agents/skills/*/SKILL.md`: repo-scoped Codex skills. Use these for repeatable workflows that should be discoverable by name and description.
+- `.codex/config.toml`: trusted project Codex settings such as MCP, hooks, sandbox, approval, or model defaults.
+- `.codex/rules/*.rules`: command permission rules for running commands outside the sandbox. Do not mix these with coding conventions in `.agents/rules.md`.
+- `.codex/hooks.json` or `.codex/config.toml` hooks: lifecycle automation such as prompt checks, command validation, or final verification gates.
+
+## Placement Rules
+
+- Put stable team conventions in `AGENTS.md` only when every task must see them without opening another file.
+- Put detailed explanations in `.agents/*.md` and link them from `AGENTS.md`.
+- Put reusable task workflows in `.agents/skills//SKILL.md`.
+- Put mechanical enforcement in linters, tests, hooks, or `.codex/rules`; do not rely on prose when tooling can enforce the rule.
+- Keep user-specific preferences out of the repository. They belong in user-level Codex configuration or user-level skills.
+
+## Skill Authoring Rules
+
+- Use lowercase hyphenated names.
+- Start descriptions with `Use when...`.
+- Describe trigger conditions in the description, not the workflow.
+- Keep `SKILL.md` lean; link to `.agents/*.md` for large reference material.
+- Add scripts only when deterministic behavior is needed repeatedly.
diff --git a/.agents/architecture.md b/.agents/architecture.md
new file mode 100644
index 00000000..dc541683
--- /dev/null
+++ b/.agents/architecture.md
@@ -0,0 +1,156 @@
+# Architecture
+
+## Core Integration
+
+The Go proxy core in `core/` operates in two modes.
+
+Android lib mode:
+
+- Go core is compiled as a C shared library, `libclash.so`, through `go build -buildmode=c-shared` with CGO.
+- Flutter calls it via FFI through the `service` plugin.
+- Dart-side implementation: `lib/core/lib.dart` (`CoreLib`).
+
+Desktop core mode:
+
+- Go core runs as a separate process with `CGO_ENABLED=0`.
+- Flutter communicates via JSON over socket, using a Unix socket on macOS/Linux and TCP on Windows.
+- Dart-side implementation: `lib/core/service.dart` (`CoreService`).
+
+`lib/core/controller.dart` (`CoreController`) selects the implementation based on platform. `lib/core/interface.dart` defines the shared `CoreHandlerInterface`.
+
+Key Go core files:
+
+- `core/hub.go`: handler functions.
+- `core/action.go`: dispatch.
+- `core/lib.go`: CGO exports.
+- `core/server.go`: socket server.
+
+## State Management
+
+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` in `lib/state.dart` is a singleton holding app lifecycle, timers, theme, and start/stop state. Providers are generated into `lib/providers/generated/`.
+
+## Database
+
+The app uses Drift/SQLite in `lib/database/`. Current schema version is 2.
+
+Tables:
+
+- `Profiles`
+- `Scripts`
+- `Rules`
+- `ProfileRuleLinks` (`profile_rule_mapping`)
+- `ProxyGroups`
+- `IconRecords` (`icon_records`)
+
+Rule scenes distinguish global added rules, profile added rules, profile custom rules, and disabled links. Rule and proxy-group ordering use fractional indexing.
+
+Generated Drift output lives in `lib/database/generated/database.g.dart`. After schema changes, run code generation and add or update focused database tests under `test/database/` when converter or migration behavior changes.
+
+## Manager Stack
+
+Managers are nested `InheritedWidget`/`StatefulWidget` components in `lib/application.dart`:
+
+```text
+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 and Actions
+
+`lib/core/controller.dart` (`CoreController`) is a singleton facade over `CoreHandlerInterface`. Public methods delegate to the platform-specific interface, either Android FFI or desktop socket. It has an `@visibleForTesting` constructor and `resetInstance()` for test injection.
+
+Business logic lives in Riverpod notifier classes in `lib/providers/action.dart`:
+
+- `CommonAction`: update check and common UI operations.
+- `SetupAction`: config setup and 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 and proxy selection.
+- `ProfilesAction`: profile CRUD, auto-update, import.
+
+## Platform Managers
+
+Desktop:
+
+- `WindowManager`
+- `TrayManager`
+- `HotKeyManager`
+- `ProxyManager`
+
+Mobile:
+
+- `AndroidManager`
+- `TileManager`
+- `VpnManager`
+
+Shared:
+
+- `ConnectivityManager`
+- `CoreManager`
+- `AppStateManager`
+- `StatusManager`
+- `ThemeManager`
+
+## Build System
+
+`setup.dart` 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`, embedded at compile time for Windows.
+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 passes `--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 and the Rust helper. The app pings the helper and verifies the token matches.
+- Debug: The Rust helper skips token verification when built in debug mode, so `flutter run` works without the SHA256 flow.
+
+`plugins/setup/` is an FFI plugin that exists only as a build harness. It carries no Dart API, only platform build hooks that trigger Go compilation. Windows builds also compile a Rust helper in `services/helper/` through `RustBuilder`.
+
+Build configuration defaults live in `build_tool/lib/src/options.dart` and can be overridden via `build_config.yaml`.
+
+Architecture detection is automatic. The `--description` flag passed to `flutter_distributor` adds arch suffixes to artifact names, such as `FlClash-0.8.93-macos-arm64.dmg`.
+
+## Local Plugins
+
+- `setup`: build harness FFI plugin.
+- `proxy`: system proxy configuration.
+- `rust_api`: Flutter Rust Bridge FFI plugin.
+- `tray_manager`: system tray fork/customization.
+- `wifi_ssid`: Wi-Fi SSID detection.
+- `window_ext`: window extensions.
+- `flutter_distributor`: app packaging/distribution.
+
+## Rust Helper Service
+
+`services/helper/` is a Windows-only privileged helper for starting the core as admin and managing TUN. It is built with:
+
+```bash
+cargo build --release --features windows-service
+```
+
+It uses token-based auth with the Flutter app.
diff --git a/.agents/commands.md b/.agents/commands.md
new file mode 100644
index 00000000..6de8c960
--- /dev/null
+++ b/.agents/commands.md
@@ -0,0 +1,106 @@
+# Commands
+
+## Building
+
+Update submodules first. The ClashMeta Go core lives in `core/Clash.Meta/`.
+
+```bash
+git submodule update --init --recursive
+```
+
+Full package build, including Go core, Flutter, and packaging, runs through `setup.dart`:
+
+```bash
+dart setup.dart macos
+dart setup.dart linux
+dart setup.dart windows
+dart setup.dart android
+```
+
+Build only the Go core and skip Flutter packaging:
+
+```bash
+make core-macos
+make core-linux
+make core-windows
+make core-android
+```
+
+Pass `ARCH` or `TARGET_PLATFORM` through `make` when needed, for example:
+
+```bash
+make core-macos ARCH=arm64
+make core-android TARGET_PLATFORM=android-arm64
+```
+
+The Makefile wraps `plugins/setup/buildkit/run_build_tool.sh`; prefer the `make` entry points unless debugging the build tool itself.
+
+## Flutter Development
+
+The project is pinned with FVM.
+
+```bash
+fvm flutter pub get
+fvm flutter run
+fvm flutter test
+```
+
+Plain Flutter also works when the global SDK matches project constraints:
+
+```bash
+flutter pub get
+flutter run
+flutter test
+```
+
+Use `flutter test`, not `dart test`, because models pull in Flutter types.
+
+## Code Generation
+
+Run code generation after modifying models, providers, or database schema:
+
+```bash
+dart run build_runner build --delete-conflicting-outputs
+dart run build_runner watch
+```
+
+Code generation covers:
+
+- Riverpod providers through `riverpod_generator`.
+- Models through `freezed` and `json_serializable`.
+- Database tables through `drift_dev`.
+
+Generated output paths, configured in `build.yaml`:
+
+- `lib/models/generated/*.g.dart`, `*.freezed.dart`.
+- `lib/providers/generated/*.g.dart`.
+- `lib/database/generated/*.g.dart`.
+
+## Testing
+
+Tests use `package:test/test.dart` for pure Dart logic and `flutter_test` for provider and widget tests. `mocktail` is the mocking framework.
+
+```bash
+flutter test test/models/
+flutter test test/core/
+flutter test test/providers/
+flutter test test/common/
+flutter test test/database/
+flutter test test/widgets/
+flutter test test/setup_test.dart
+flutter test plugins/proxy/test/proxy_test.dart
+```
+
+Root `flutter test` only discovers the root package's `test/` directory by default. Include bundled plugin Dart tests by passing paths explicitly, or run `flutter test` from that plugin package directory. Native plugin tests under platform folders are not run by `flutter test`.
+
+## Verify
+
+CI runs these in order:
+
+```bash
+flutter pub get
+flutter analyze --no-fatal-infos
+flutter test --reporter expanded
+```
+
+Run `flutter analyze` locally before committing when practical.
diff --git a/.agents/project.md b/.agents/project.md
new file mode 100644
index 00000000..9ca9529f
--- /dev/null
+++ b/.agents/project.md
@@ -0,0 +1,28 @@
+# Project Context
+
+FlClash is a multi-platform proxy client based on ClashMeta (mihomo), built with Flutter. It supports Android, Windows, macOS, and Linux, using a Material You design with Surfboard-like UI.
+
+## Version Notes
+
+- `.fvmrc` pins Flutter 3.35.7 for local development.
+- CI uses Flutter 3.41.9. These may diverge; trust CI as the source of truth for release builds.
+- Dart SDK constraint: `>=3.8.0 <4.0.0`.
+
+## Build Dependencies
+
+Linux:
+
+```bash
+sudo apt-get install libayatana-appindicator3-dev libkeybinder-3.0-dev
+```
+
+Windows:
+
+- GCC and Inno Setup.
+- `ANDROID_NDK` env var for Android builds.
+
+macOS:
+
+```bash
+npm install -g appdmg
+```
diff --git a/.agents/rules.md b/.agents/rules.md
new file mode 100644
index 00000000..fef46309
--- /dev/null
+++ b/.agents/rules.md
@@ -0,0 +1,47 @@
+# Rules
+
+These are repository coding and testing conventions. Codex command permission rules belong in `.codex/rules/*.rules`; see `.agents/agent-config.md` before adding those.
+
+## Dart and Flutter Style
+
+`analysis_options.yaml` enforces these non-default rules:
+
+- `prefer_single_quotes: true`: always use single quotes.
+- `require_trailing_commas: true`: use trailing commas in multi-line argument lists.
+- `sort_child_properties_last: true`: `child:` must be the last named parameter.
+- `avoid_print: true`: do not use `print()` calls.
+- `prefer_const_constructors: true` and `prefer_const_declarations: true`.
+- `prefer_final_locals: true` and `prefer_final_in_for_each: true`.
+- `always_declare_return_types: true`.
+
+Generated directories are excluded from analysis:
+
+- `build/**`
+- `lib/l10n/intl/**`
+- `lib/**/generated/**`
+- `plugins/**`
+
+## Testing Rules
+
+Use `CoreController.test(mock)` to inject a mocked `CoreHandlerInterface`. Call `CoreController.resetInstance()` in `tearDown` to clean up the singleton between tests.
+
+Register fallback values for freezed params used with `any()` matchers.
+
+Use `ProviderContainer` directly for simple Riverpod provider tests. The generated Riverpod `update()` method takes a callback:
+
+```dart
+notifier.update((state) => newValue);
+```
+
+When testing freezed models with nested objects, always round-trip through `jsonEncode` and `jsonDecode`. Direct `fromJson(toJson())` fails for nested freezed types because `toJson()` stores child objects directly instead of maps.
+
+## Generated Code
+
+Do not manually edit generated files under:
+
+- `lib/models/generated/`
+- `lib/providers/generated/`
+- `lib/database/generated/`
+- `lib/l10n/intl/`
+
+After schema, model, or provider changes, run build generation and include focused tests when behavior changes.
diff --git a/.agents/skills.md b/.agents/skills.md
new file mode 100644
index 00000000..09ad53e6
--- /dev/null
+++ b/.agents/skills.md
@@ -0,0 +1,17 @@
+# Agent Skills
+
+Repo-scoped Codex skills live under `.agents/skills/*/SKILL.md`. Codex can discover these by skill `name` and `description`, then load the full instructions only when a task matches.
+
+## Available Repo Skills
+
+- `localization`: hardcoded UI text scans, ARB updates, locale generation, and localization verification.
+- `provider-tests`: Riverpod provider, notifier, and state-management tests.
+- `ui-work`: Flutter UI, widgets, Material You styling, navigation surfaces, and user-facing interactions.
+- `core-platform`: core integration, platform managers, Go core communication, desktop/mobile behavior, and Windows helper flow.
+
+## Authoring Notes
+
+- Add new repeatable workflows as `.agents/skills//SKILL.md`.
+- Keep skill descriptions trigger-focused and start them with `Use when...`.
+- Keep long reference material in `.agents/*.md`; skills should link to it instead of duplicating it.
+- Put command permission rules in `.codex/rules/*.rules`, not in this file.
diff --git a/.agents/skills/core-platform/SKILL.md b/.agents/skills/core-platform/SKILL.md
new file mode 100644
index 00000000..02c23612
--- /dev/null
+++ b/.agents/skills/core-platform/SKILL.md
@@ -0,0 +1,33 @@
+---
+name: core-platform
+description: Use when changing FlClash core integration, platform managers, Go core communication, desktop/mobile platform behavior, or Windows helper flow.
+---
+
+# Core And Platform
+
+## When To Use
+
+Use this for changes touching `lib/core/`, `lib/manager/`, `core/`, `services/helper/`, build hooks, system proxy, tray, VPN, TUN, or platform-specific desktop/mobile behavior.
+
+## Workflow
+
+1. Identify which boundary owns the behavior:
+ - Android lib mode: `lib/core/lib.dart`.
+ - Desktop process/socket mode: `lib/core/service.dart`.
+ - Shared facade: `lib/core/controller.dart` and `lib/core/interface.dart`.
+ - Platform lifecycle: `lib/manager/`.
+2. Route feature code through `CoreController` and `CoreHandlerInterface`; avoid direct calls to platform implementations outside their boundary.
+3. Keep desktop and mobile paths explicit.
+4. For action-layer behavior, inspect `lib/providers/action.dart` and relevant generated providers.
+5. Add or update shared Dart tests for logic that can be isolated.
+6. Manually verify native behavior when automated coverage is not practical.
+
+## Reference Files
+
+Read `.agents/architecture.md` for the current core modes, manager stack, build hooks, local plugins, and Windows helper notes.
+
+## Pitfalls
+
+- Debug Windows helper auth differs from release token verification.
+- `plugins/setup/` is a build harness, not a Dart API plugin.
+- Build hooks can trigger Go or Rust compilation indirectly through Flutter platform builds.
diff --git a/.agents/skills/localization/SKILL.md b/.agents/skills/localization/SKILL.md
new file mode 100644
index 00000000..95a3e404
--- /dev/null
+++ b/.agents/skills/localization/SKILL.md
@@ -0,0 +1,45 @@
+---
+name: localization
+description: Use when changing FlClash UI text, scanning for hardcoded Chinese strings, updating ARB localization, or fixing generated locale output in this repository.
+---
+
+# Localization
+
+## When To Use
+
+Use this for localization work inside `/Users/Shared/follow/FlClash`, especially hardcoded UI text in `lib/`, ARB updates, missing translations, or generated `lib/l10n/` output that does not match source ARB values.
+
+Do not use this for README translation sync or manual edits to generated localization Dart.
+
+## Workflow
+
+1. Confirm `pubspec.yaml` still uses `flutter_intl`, source ARBs under `arb/`, and generated output under `lib/l10n/`.
+2. Scan user-facing Dart text before opening many files:
+
+ ```bash
+ rg -n "[\\p{Han}]" lib -g '!lib/l10n/intl/**' -g '!lib/**/generated/**'
+ ```
+
+3. Inspect the smallest relevant call sites and nearby ARB keys.
+4. Add or update every source ARB:
+ - `arb/intl_en.arb`
+ - `arb/intl_zh_CN.arb`
+ - `arb/intl_ja.arb`
+ - `arb/intl_ru.arb`
+5. Replace inline strings with existing project accessors:
+ - Widgets with `BuildContext`: `context.appLocalizations.key` from `common.dart`.
+ - Controllers/providers/non-widget code: `currentAppLocalizations.key` from `app_localizations.dart`.
+6. Regenerate:
+
+ ```bash
+ dart run intl_utils:generate
+ ```
+
+7. Verify changed Dart files with `flutter analyze` when practical.
+8. Re-run the Han-text scan for targeted strings.
+
+## Pitfalls
+
+- If ja/ru still show English, fix ja/ru source ARB values and regenerate. Do not edit generated Dart.
+- Ignore `lib/l10n/intl/**` and `lib/**/generated/**` during text scans.
+- If generator or analyzer hits local cache permission friction, rerun serially before treating it as a code issue.
diff --git a/.agents/skills/provider-tests/SKILL.md b/.agents/skills/provider-tests/SKILL.md
new file mode 100644
index 00000000..6b6cfb41
--- /dev/null
+++ b/.agents/skills/provider-tests/SKILL.md
@@ -0,0 +1,37 @@
+---
+name: provider-tests
+description: Use when adding or updating FlClash Riverpod provider tests, notifier tests, or state-management tests in this repository.
+---
+
+# Provider Tests
+
+## When To Use
+
+Use this for tests under `test/providers/` or any change that validates Riverpod providers, generated notifiers, app state defaults, or provider interactions.
+
+For broader test expansion, pair this with `.agents/rules.md` and `.agents/commands.md`.
+
+## Workflow
+
+1. Read the provider under test and its generated public API before writing assertions.
+2. Use `ProviderContainer` directly when no widget tree is needed.
+3. Dispose containers in teardown or with `addTearDown(container.dispose)`.
+4. Prefer generated notifier APIs over implementation details. Generated `update()` takes a callback:
+
+ ```dart
+ notifier.update((state) => newValue);
+ ```
+
+5. Mock external dependencies with `mocktail`; register fallback values for freezed params used with `any()`.
+6. Keep tests focused on behavior: defaults, state transitions, persistence boundaries, and side effects.
+7. Run the narrowest relevant test first:
+
+ ```bash
+ flutter test test/providers/
+ ```
+
+## Pitfalls
+
+- Do not use `dart test`; FlClash models and provider tests may depend on Flutter types.
+- Re-check source defaults before asserting them; provider defaults can drift.
+- If async provider timing matters, wait on provider futures or state changes instead of fixed sleeps.
diff --git a/.agents/skills/ui-work/SKILL.md b/.agents/skills/ui-work/SKILL.md
new file mode 100644
index 00000000..c2cab0a2
--- /dev/null
+++ b/.agents/skills/ui-work/SKILL.md
@@ -0,0 +1,32 @@
+---
+name: ui-work
+description: Use when changing FlClash Flutter UI, widgets, screens, Material You styling, navigation surfaces, or user-facing interactions.
+---
+
+# UI Work
+
+## When To Use
+
+Use this for user-facing Flutter UI changes in `lib/`, including widgets, screens, navigation surfaces, settings rows, dialogs, and interaction behavior.
+
+## Workflow
+
+1. Locate existing nearby widgets and reuse their patterns before adding new abstractions.
+2. Follow current Material You and Surfboard-like visual conventions.
+3. Use existing providers, notifiers, and helpers where possible.
+4. Keep `child:` last in widget constructors.
+5. Prefer `const` constructors and final locals.
+6. Localize user-facing text through ARB; use `localization` when text changes are non-trivial.
+7. Add focused widget tests when behavior changes, especially for rendering states, taps, scrolling, and empty/error states.
+8. Run targeted verification:
+
+ ```bash
+ flutter analyze
+ flutter test test/widgets/
+ ```
+
+## Pitfalls
+
+- Do not introduce a new visual system for one screen.
+- Do not manually edit generated localization or provider files.
+- Avoid broad layout rewrites unless the requested change requires them.
diff --git a/.claude/commands/i18n.md b/.claude/commands/i18n.md
deleted file mode 100644
index d85b8148..00000000
--- a/.claude/commands/i18n.md
+++ /dev/null
@@ -1,93 +0,0 @@
-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`
diff --git a/.codegraph/.gitignore b/.codegraph/.gitignore
new file mode 100644
index 00000000..d20c0fe4
--- /dev/null
+++ b/.codegraph/.gitignore
@@ -0,0 +1,5 @@
+# CodeGraph data files — local to each machine, not for committing.
+# Ignore everything in .codegraph/ except this file itself, so transient
+# files (the database, daemon.pid, sockets, logs) never show up in git.
+*
+!.gitignore
diff --git a/.gemini/settings.json b/.gemini/settings.json
new file mode 100644
index 00000000..87ca7dea
--- /dev/null
+++ b/.gemini/settings.json
@@ -0,0 +1,12 @@
+{
+ "mcpServers": {
+ "codegraph": {
+ "type": "stdio",
+ "command": "codegraph",
+ "args": [
+ "serve",
+ "--mcp"
+ ]
+ }
+ }
+}
diff --git a/.github/homebrew_cask_template.rb b/.github/homebrew_cask_template.rb
new file mode 100644
index 00000000..3a9a493a
--- /dev/null
+++ b/.github/homebrew_cask_template.rb
@@ -0,0 +1,39 @@
+cask "flclash" do
+ version "VERSION"
+
+ on_macos do
+ arch arm: "arm64", intel: "amd64"
+
+ sha256 arm: "ARM_SHA256",
+ intel: "AMD_SHA256"
+
+ url "https://github.com/chen08209/FlClash/releases/download/v#{version}/FlClash-#{version}-macos-#{arch}.dmg"
+ end
+
+ name "FlClash"
+ desc "Multi-platform proxy client based on ClashMeta"
+ homepage "https://github.com/chen08209/FlClash"
+
+ livecheck do
+ url :url
+ strategy :github_latest
+ end
+
+ depends_on :macos
+
+ app "FlClash.app"
+
+ postflight do
+ system_command "xattr",
+ args: ["-rd", "com.apple.quarantine", "#{appdir}/FlClash.app"]
+ end
+
+ uninstall quit: "com.follow.clash"
+
+ zap trash: [
+ "~/Library/Application Support/com.follow.clash",
+ "~/Library/Caches/com.follow.clash",
+ "~/Library/Preferences/com.follow.clash.plist",
+ "~/Library/Saved Application State/com.follow.clash.savedState",
+ ]
+end
diff --git a/.github/release_template.md b/.github/release_template.md
index d1300848..c52b363d 100644
--- a/.github/release_template.md
+++ b/.github/release_template.md
@@ -42,7 +42,7 @@
-
+
|
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index abb91b98..a33fba3d 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -6,6 +6,7 @@ on:
- 'v*'
env:
IS_STABLE: ${{ !contains(github.ref, '-') }}
+ FLUTTER_VERSION: '3.44.4'
jobs:
test:
@@ -21,7 +22,7 @@ jobs:
uses: subosito/flutter-action@v2
with:
channel: stable
- flutter-version: 3.41.9
+ flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
- name: Install dependencies
@@ -63,6 +64,7 @@ jobs:
steps:
- name: Setup rust
if: startsWith(matrix.os, 'windows-11-arm')
+ shell: pwsh
run: |
Invoke-WebRequest -Uri "https://win.rustup.rs/aarch64" -OutFile rustup-init.exe
.\rustup-init.exe -y --default-toolchain stable
@@ -79,14 +81,16 @@ jobs:
run: |
echo "${{ secrets.KEYSTORE }}" | base64 --decode > android/app/keystore.jks
echo "${{ secrets.SERVICE_JSON }}" | base64 --decode > android/app/google-services.json
- echo "keyAlias=${{ secrets.KEY_ALIAS }}" >> android/local.properties
- echo "storePassword=${{ secrets.STORE_PASSWORD }}" >> android/local.properties
- echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> android/local.properties
+ {
+ echo "keyAlias=${{ secrets.KEY_ALIAS }}"
+ echo "storePassword=${{ secrets.STORE_PASSWORD }}"
+ echo "keyPassword=${{ secrets.KEY_PASSWORD }}"
+ } >> android/local.properties
- name: Setup Go
uses: actions/setup-go@v5
with:
- go-version: '1.24.0'
+ go-version: '1.26.4'
cache-dependency-path: |
core/go.sum
@@ -95,20 +99,18 @@ jobs:
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.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.44.0-0.1.pre
+ flutter-version: ${{ env.FLUTTER_VERSION }}
cache: true
+ - name: Setup Xcode
+ if: startsWith(matrix.platform,'macos')
+ run: |
+ sudo xcode-select -s /Applications/Xcode_26.3.app || sudo xcode-select -s /Applications/Xcode_26.2.app
+ xcodebuild -version
+
- name: Setup NDK
if: startsWith(matrix.platform,'android')
uses: nttld/setup-ndk@v1
@@ -133,17 +135,18 @@ jobs:
overwrite: true
changelog:
+ if: ${{ !contains(github.ref, '-') }}
+ permissions:
+ contents: write
runs-on: ubuntu-latest
needs: [ build ]
steps:
- name: Checkout
uses: actions/checkout@v4
- if: ${{ env.IS_STABLE == 'true' }}
with:
fetch-depth: 0
ref: refs/heads/main
- name: Generate
- if: ${{ env.IS_STABLE == 'true' }}
run: |
last_ver=$(grep -m1 '^## ' CHANGELOG.md 2>/dev/null | sed 's/^## //')
@@ -167,7 +170,6 @@ jobs:
mv "$temp" CHANGELOG.md
- name: Commit
- if: ${{ env.IS_STABLE == 'true' }}
run: |
git add CHANGELOG.md
if ! git diff --cached --quiet; then
@@ -175,8 +177,7 @@ jobs:
git config --local user.email "chen08209@gmail.com"
git config --local user.name "chen08209"
git commit -m "Update changelog"
- git push
- if [ $? -eq 0 ]; then
+ if git push; then
echo "Push succeeded"
else
echo "Push failed"
@@ -188,7 +189,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
upload:
- permissions: write-all
+ permissions:
+ contents: write
needs: [ build ]
runs-on: ubuntu-latest
services:
@@ -251,7 +253,7 @@ jobs:
if: ${{ env.IS_STABLE == 'true' }}
run: |
cd ./dist
- sha256sum * > SHA256SUMS
+ sha256sum ./* > SHA256SUMS
- name: Release
if: ${{ env.IS_STABLE == 'true' }}
@@ -260,6 +262,35 @@ jobs:
files: ./dist/*
body_path: './release.md'
+ - name: Create Homebrew Cask Source Dir
+ if: ${{ env.IS_STABLE == 'true' }}
+ run: |
+ version=$(echo "${{ github.ref_name }}" | sed 's/^v//')
+ arm_sha=$(sha256sum "./dist/FlClash-${version}-macos-arm64.dmg" | awk '{print $1}')
+ amd_sha=$(sha256sum "./dist/FlClash-${version}-macos-amd64.dmg" | awk '{print $1}')
+
+ mkdir -p ./brew/Casks
+ sed \
+ -e "s|VERSION|$version|g" \
+ -e "s|ARM_SHA256|$arm_sha|g" \
+ -e "s|AMD_SHA256|$amd_sha|g" \
+ ./.github/homebrew_cask_template.rb > ./brew/Casks/flclash.rb
+
+ - name: Push to Homebrew tap
+ if: ${{ env.IS_STABLE == 'true' }}
+ uses: cpina/github-action-push-to-another-repository@v1.7.2
+ env:
+ SSH_DEPLOY_KEY: ${{ secrets.HOMEBREW_SSH_DEPLOY_KEY }}
+ with:
+ source-directory: ./brew/
+ destination-github-username: chen08209
+ destination-repository-name: homebrew-tap
+ user-name: 'chen08029'
+ user-email: 'chen08209@gmail.com'
+ target-branch: main
+ commit-message: Update Homebrew cask from ${{ github.ref_name }}
+ target-directory: /
+
- name: Create Fdroid Source Dir
if: ${{ env.IS_STABLE == 'true' }}
run: |
@@ -276,8 +307,8 @@ jobs:
source-directory: ./tmp/
destination-github-username: chen08209
destination-repository-name: FlClash-fdroid-repo
- user-name: 'github-actions[bot]'
- user-email: 'github-actions[bot]@users.noreply.github.com'
+ user-name: 'chen08029'
+ user-email: 'chen08209@gmail.com'
target-branch: main
commit-message: Update from ${{ github.ref_name }}
target-directory: /tmp/
diff --git a/.gitmodules b/.gitmodules
index 70bf7cd8..f373ba78 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -2,13 +2,3 @@
path = core/Clash.Meta
url = git@github.com:chen08209/Clash.Meta.git
branch = FlClash
-[submodule "plugins/flutter_distributor"]
- path = plugins/flutter_distributor
- url = git@github.com:chen08209/flutter_distributor.git
- branch = FlClash
-[submodule "plugins/tray_manager"]
- path = plugins/tray_manager
- url = git@github.com:chen08209/tray_manager.git
- branch = main
-
-
diff --git a/.mcp.json b/.mcp.json
new file mode 100644
index 00000000..87ca7dea
--- /dev/null
+++ b/.mcp.json
@@ -0,0 +1,12 @@
+{
+ "mcpServers": {
+ "codegraph": {
+ "type": "stdio",
+ "command": "codegraph",
+ "args": [
+ "serve",
+ "--mcp"
+ ]
+ }
+ }
+}
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 00000000..1d977681
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,20 @@
+repos:
+ - repo: local
+ hooks:
+ - id: dart-format
+ name: dart format
+ entry: dart format
+ language: system
+ files: \.dart$
+ - id: flutter-analyze
+ name: flutter analyze
+ entry: flutter analyze --no-fatal-infos
+ language: system
+ pass_filenames: false
+ files: \.dart$
+ - id: flutter-test
+ name: flutter test
+ entry: flutter test --reporter expanded
+ language: system
+ pass_filenames: false
+ files: \.dart$
diff --git a/.zed/settings.json b/.zed/settings.json
new file mode 100644
index 00000000..1bd257a7
--- /dev/null
+++ b/.zed/settings.json
@@ -0,0 +1,13 @@
+{
+ "lsp": {
+ "gopls": {
+ "binary": {
+ "env": {
+ "CGO_ENABLED": "0",
+ // "GOOS": "android",
+ // "GOARCH": "arm64",
+ },
+ },
+ },
+ },
+}
diff --git a/AGENTS.md b/AGENTS.md
index fba9e5d9..9a555441 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,233 +1,35 @@
# AGENTS.md
-This file provides guidance for AI coding agents working with code in this repository.
+This file is the entry point for AI coding agents working in this repository. Keep it small: detailed guidance lives under
+`.agents/`, and discoverable repo skills live under `.agents/skills/*/SKILL.md`.
-## Project Overview
+## Start Here
-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.
+Read these files before making changes:
-## Common Development Commands
+- [.agents/project.md](.agents/project.md): project overview, versions, and build dependencies.
+- [.agents/commands.md](.agents/commands.md): build, development, code generation, and test commands.
+- [.agents/rules.md](.agents/rules.md): lint, testing, generated-code, and workflow rules.
-### Building
+Read these only when the task touches their area:
-```bash
-# Update submodules first (ClashMeta Go core lives in core/Clash.Meta/)
-git submodule update --init --recursive
+- [.agents/architecture.md](.agents/architecture.md): core integration, providers, database, managers, build system, and
+ local plugins.
+- [.agents/agent-config.md](.agents/agent-config.md): how to choose between `AGENTS.md`, `.agents`, skills, Codex config,
+ command rules, and hooks.
+- [.agents/skills.md](.agents/skills.md): index of repo-scoped skills in `.agents/skills/`.
-# 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
+## Highest Priority Rules
-# 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
-```
+- Use `flutter test`, not `dart test`, because models pull in Flutter types.
+- Run code generation after modifying models, providers, or database schema.
+- Do not manually edit generated files.
+- Follow `analysis_options.yaml`, especially single quotes, trailing commas, `child:` last, no `print()`, const/final
+ preferences, and declared return types.
+- For CI parity, verify with `flutter pub get`, `flutter analyze --no-fatal-infos`, and
+ `flutter test --reporter expanded` when practical.
-### Flutter Development
+## Repo Skills
-```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`)
+Use repo skills from `.agents/skills/` when a task matches their descriptions. Current skills cover localization,
+provider tests, UI work, and core/platform changes.
diff --git a/CLAUDE.md b/CLAUDE.md
index 71993a20..8de06f90 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -1,5 +1,4 @@
-# CLAUDE.md - Claude Code instructions
+# CLAUDE.md
-Please read AGENTS.md for all project guidelines and conventions.
-
-@AGENTS.md
\ No newline at end of file
+This repository uses [AGENTS.md](AGENTS.md) as the canonical agent entry point.
+Read that file first, then follow the `.agents/` references it routes to.
diff --git a/Makefile b/Makefile
new file mode 100644
index 00000000..c1bef1db
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,33 @@
+SHELL := /bin/bash
+
+PLATFORM ?= macos
+BUILDKIT := plugins/setup/buildkit/run_build_tool.sh
+ARCH_ARG := $(if $(ARCH),--arch $(ARCH),)
+TARGET_PLATFORM_ARG := $(if $(TARGET_PLATFORM),--target-platform $(TARGET_PLATFORM),)
+
+.PHONY: help submodules core core-macos core-linux core-windows core-android
+
+help:
+ @echo 'make core # build macOS core by default'
+ @echo 'make core PLATFORM=linux ARCH=amd64'
+ @echo 'make core-macos ARCH=arm64'
+ @echo 'make core-android ARCH=arm64'
+ @echo 'make core-android TARGET_PLATFORM=android-arm64'
+
+submodules:
+ git submodule update --init --recursive
+
+core:
+ bash $(BUILDKIT) $(PLATFORM) $(ARCH_ARG) $(TARGET_PLATFORM_ARG)
+
+core-macos:
+ $(MAKE) core PLATFORM=macos
+
+core-linux:
+ $(MAKE) core PLATFORM=linux
+
+core-windows:
+ $(MAKE) core PLATFORM=windows
+
+core-android:
+ $(MAKE) core PLATFORM=android
diff --git a/README.md b/README.md
index 2e148be1..26b6b975 100644
--- a/README.md
+++ b/README.md
@@ -61,6 +61,13 @@ Support the following actions
+### Homebrew
+
+```bash
+brew tap chen08209/tap
+brew install --cask flclash
+```
+
## Build
1. Update submodules
@@ -129,4 +136,4 @@ The easiest way to support developers is to click on the star (⭐) at the top o
-
\ No newline at end of file
+
diff --git a/README_zh_CN.md b/README_zh_CN.md
index df192ebb..eb517a60 100644
--- a/README_zh_CN.md
+++ b/README_zh_CN.md
@@ -61,6 +61,13 @@ on Mobile:
+### Homebrew
+
+```bash
+brew tap chen08209/tap
+brew install --cask flclash
+```
+
## Build
1. 更新 submodules
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 4cef3e8a..b64ccea8 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -3,14 +3,13 @@ import java.util.Properties
plugins {
id("com.android.application")
- id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
-val localPropertiesFile = rootProject.file("local.properties")
val localProperties = Properties().apply {
+ val localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.inputStream().use { load(it) }
}
@@ -106,4 +105,4 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ndk)
implementation(libs.firebase.analytics)
-}
\ No newline at end of file
+}
diff --git a/android/build.gradle.kts b/android/build.gradle.kts
index dbee657b..18fecbd9 100644
--- a/android/build.gradle.kts
+++ b/android/build.gradle.kts
@@ -15,10 +15,25 @@ subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
+
subprojects {
project.evaluationDependsOn(":app")
}
+gradle.projectsEvaluated {
+ allprojects {
+ tasks.withType().configureEach {
+ sourceCompatibility = JavaVersion.VERSION_17.toString()
+ targetCompatibility = JavaVersion.VERSION_17.toString()
+ }
+ tasks.withType().configureEach {
+ compilerOptions {
+ jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17)
+ }
+ }
+ }
+}
+
tasks.register("clean") {
delete(rootProject.layout.buildDirectory)
}
diff --git a/android/common/build.gradle.kts b/android/common/build.gradle.kts
index b46b3a60..35e2c7c6 100644
--- a/android/common/build.gradle.kts
+++ b/android/common/build.gradle.kts
@@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.library")
- id("org.jetbrains.kotlin.android")
}
android {
@@ -42,4 +41,4 @@ dependencies {
implementation(platform(libs.firebase.bom))
implementation(libs.firebase.crashlytics.ndk)
implementation(libs.firebase.analytics)
-}
\ No newline at end of file
+}
diff --git a/android/common/consumer-rules.pro b/android/common/consumer-rules.pro
new file mode 100644
index 00000000..e69de29b
diff --git a/android/core/build.gradle.kts b/android/core/build.gradle.kts
index 593a503c..915d5c96 100644
--- a/android/core/build.gradle.kts
+++ b/android/core/build.gradle.kts
@@ -2,7 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.library")
- id("org.jetbrains.kotlin.android")
}
android {
diff --git a/android/gradle.properties b/android/gradle.properties
index 598d13fe..f2122b21 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -1,3 +1,7 @@
org.gradle.jvmargs=-Xmx4G
android.useAndroidX=true
android.enableJetifier=true
+# This builtInKotlin flag was added automatically by Flutter migrator
+android.builtInKotlin=false
+# This newDsl flag was added automatically by Flutter migrator
+android.newDsl=false
diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml
index f425f9a8..ed7237ad 100644
--- a/android/gradle/libs.versions.toml
+++ b/android/gradle/libs.versions.toml
@@ -1,6 +1,6 @@
[versions]
#agp = "8.10.1"
-firebaseBom = "34.2.0"
+firebaseBom = "34.15.0"
minSdk = "23"
targetSdk = "36"
compileSdk = "36"
@@ -9,13 +9,12 @@ coreKtx = "1.17.0"
annotationJvm = "1.9.1"
coreSplashscreen = "1.0.1"
gson = "2.13.1"
-kotlin = "2.2.10"
+kotlin = "2.2.20"
smaliDexlib2 = "3.0.9"
-firebaseCrashlyticsKtx = "20.0.1"
-firebaseCommonKtx = "22.0.0"
+firebaseCrashlyticsKtx = "20.0.6"
+firebaseCommonKtx = "22.1.0"
[libraries]
-build-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "coreKtx" }
annotation-jvm = { module = "androidx.annotation:annotation-jvm", version.ref = "annotationJvm" }
core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
@@ -25,4 +24,4 @@ firebase-crashlytics-ndk = { module = "com.google.firebase:firebase-crashlytics-
gson = { module = "com.google.code.gson:gson", version.ref = "gson" }
smali-dexlib2 = { module = "com.android.tools.smali:smali-dexlib2", version.ref = "smaliDexlib2" }
firebase-crashlytics-ktx = { group = "com.google.firebase", name = "firebase-crashlytics-ktx", version.ref = "firebaseCrashlyticsKtx" }
-firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" }
\ No newline at end of file
+firebase-common-ktx = { group = "com.google.firebase", name = "firebase-common-ktx", version.ref = "firebaseCommonKtx" }
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 6064aebd..c1501a7e 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,5 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.1-all.zip
diff --git a/android/service/build.gradle.kts b/android/service/build.gradle.kts
index 8d9637b8..ac715317 100644
--- a/android/service/build.gradle.kts
+++ b/android/service/build.gradle.kts
@@ -2,8 +2,6 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
id("com.android.library")
- id("org.jetbrains.kotlin.android")
- id("kotlin-parcelize")
}
android {
@@ -45,4 +43,4 @@ dependencies {
implementation(project(":common"))
implementation(libs.gson)
implementation(libs.androidx.core)
-}
\ No newline at end of file
+}
diff --git a/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt b/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt
index 7196005d..dc18eeb4 100644
--- a/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt
+++ b/android/service/src/main/java/com/follow/clash/service/models/NotificationParams.kt
@@ -1,11 +1,36 @@
package com.follow.clash.service.models
+import android.os.Parcel
import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-@Parcelize
data class NotificationParams(
val title: String = "FlClash",
val stopText: String = "STOP",
val onlyStatisticsProxy: Boolean = false,
-) : Parcelable
\ No newline at end of file
+) : Parcelable {
+ constructor(parcel: Parcel) : this(
+ title = parcel.readString() ?: "FlClash",
+ stopText = parcel.readString() ?: "STOP",
+ onlyStatisticsProxy = parcel.readByte() != 0.toByte(),
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeString(title)
+ parcel.writeString(stopText)
+ parcel.writeByte(if (onlyStatisticsProxy) 1.toByte() else 0.toByte())
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): NotificationParams {
+ return NotificationParams(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+}
diff --git a/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt b/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt
index 0e2b4acf..27264aa0 100644
--- a/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt
+++ b/android/service/src/main/java/com/follow/clash/service/models/VpnOptions.kt
@@ -1,19 +1,45 @@
package com.follow.clash.service.models
+import android.os.Parcel
import android.os.Parcelable
import com.follow.clash.common.AccessControlMode
-import kotlinx.parcelize.Parcelize
import java.net.InetAddress
-@Parcelize
data class AccessControlProps(
val enable: Boolean,
val mode: AccessControlMode,
val acceptList: List,
val rejectList: List,
-) : Parcelable
+) : Parcelable {
+ constructor(parcel: Parcel) : this(
+ enable = parcel.readByte() != 0.toByte(),
+ mode = AccessControlMode.valueOf(parcel.readString() ?: AccessControlMode.ACCEPT_SELECTED.name),
+ acceptList = parcel.createStringArrayList() ?: emptyList(),
+ rejectList = parcel.createStringArrayList() ?: emptyList(),
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeByte(if (enable) 1.toByte() else 0.toByte())
+ parcel.writeString(mode.name)
+ parcel.writeStringList(acceptList)
+ parcel.writeStringList(rejectList)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ override fun createFromParcel(parcel: Parcel): AccessControlProps {
+ return AccessControlProps(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+}
-@Parcelize
data class VpnOptions(
val enable: Boolean,
val port: Int,
@@ -25,7 +51,59 @@ data class VpnOptions(
val bypassDomain: List,
val stack: String,
val routeAddress: List,
-) : Parcelable
+) : Parcelable {
+ constructor(parcel: Parcel) : this(
+ enable = parcel.readByte() != 0.toByte(),
+ port = parcel.readInt(),
+ ipv6 = parcel.readByte() != 0.toByte(),
+ dnsHijacking = parcel.readByte() != 0.toByte(),
+ accessControlProps = readAccessControlProps(parcel),
+ allowBypass = parcel.readByte() != 0.toByte(),
+ systemProxy = parcel.readByte() != 0.toByte(),
+ bypassDomain = parcel.createStringArrayList() ?: emptyList(),
+ stack = parcel.readString() ?: "",
+ routeAddress = parcel.createStringArrayList() ?: emptyList(),
+ )
+
+ override fun writeToParcel(parcel: Parcel, flags: Int) {
+ parcel.writeByte(if (enable) 1.toByte() else 0.toByte())
+ parcel.writeInt(port)
+ parcel.writeByte(if (ipv6) 1.toByte() else 0.toByte())
+ parcel.writeByte(if (dnsHijacking) 1.toByte() else 0.toByte())
+ parcel.writeParcelable(accessControlProps, flags)
+ parcel.writeByte(if (allowBypass) 1.toByte() else 0.toByte())
+ parcel.writeByte(if (systemProxy) 1.toByte() else 0.toByte())
+ parcel.writeStringList(bypassDomain)
+ parcel.writeString(stack)
+ parcel.writeStringList(routeAddress)
+ }
+
+ override fun describeContents(): Int {
+ return 0
+ }
+
+ companion object CREATOR : Parcelable.Creator {
+ @Suppress("DEPRECATION")
+ private fun readAccessControlProps(parcel: Parcel): AccessControlProps {
+ return parcel.readParcelable(
+ AccessControlProps::class.java.classLoader,
+ ) ?: AccessControlProps(
+ enable = false,
+ mode = AccessControlMode.ACCEPT_SELECTED,
+ acceptList = emptyList(),
+ rejectList = emptyList(),
+ )
+ }
+
+ override fun createFromParcel(parcel: Parcel): VpnOptions {
+ return VpnOptions(parcel)
+ }
+
+ override fun newArray(size: Int): Array {
+ return arrayOfNulls(size)
+ }
+ }
+}
data class CIDR(val address: InetAddress, val prefixLength: Int)
@@ -80,4 +158,4 @@ fun String.toCIDR(): CIDR {
}
return CIDR(address, prefixLength)
-}
\ No newline at end of file
+}
diff --git a/android/settings.gradle.kts b/android/settings.gradle.kts
index dd51c864..5564c35e 100644
--- a/android/settings.gradle.kts
+++ b/android/settings.gradle.kts
@@ -19,10 +19,10 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
- id("com.android.application") version "8.12.2" apply false
- id("org.jetbrains.kotlin.android") version "2.2.10" apply false
- id("com.google.gms.google-services") version ("4.3.15") apply false
- id("com.google.firebase.crashlytics") version ("2.8.1") apply false
+ id("com.android.application") version "9.0.1" apply false
+ id("org.jetbrains.kotlin.android") version "2.4.0" apply false
+ id("com.google.gms.google-services") version ("4.5.0") apply false
+ id("com.google.firebase.crashlytics") version ("3.0.7") apply false
}
diff --git a/arb/intl_en.arb b/arb/intl_en.arb
index 82ef0f95..6951fb3a 100644
--- a/arb/intl_en.arb
+++ b/arb/intl_en.arb
@@ -133,6 +133,7 @@
"app": "App",
"general": "General",
"systemProxyDesc": "Attach HTTP proxy to VpnService",
+ "userAgent": "User-Agent",
"unifiedDelay": "Unified delay",
"unifiedDelayDesc": "Remove extra delays such as handshaking",
"tcpConcurrent": "TCP concurrent",
@@ -537,5 +538,17 @@
"editSsid": "Edit SSID",
"authorized": "Authorized",
"tapToAuthorize": "Tap to authorize",
- "suspended": "Suspended..."
+ "suspended": "Suspended...",
+ "geoOptions": "Geo Options",
+ "geoAutoUpdate": "Auto Update",
+ "geoAutoUpdateInterval": "Auto Update Interval",
+ "geoAutoUpdateIntervalTip": "Auto update interval must be greater than 0",
+ "hours": "hours",
+ "hoursCount": "{count} hours",
+ "geoResources": "Geo Resources",
+ "geoUpdating": "Updating {name}...",
+ "geoSkipped": "{name} skipped",
+ "geoUpdated": "{name} updated",
+ "secondsCount": "{count} seconds",
+ "entriesCount": "{count} entries"
}
diff --git a/arb/intl_ja.arb b/arb/intl_ja.arb
index 4f8aa0cb..a310126c 100644
--- a/arb/intl_ja.arb
+++ b/arb/intl_ja.arb
@@ -133,6 +133,7 @@
"app": "アプリ",
"general": "一般",
"systemProxyDesc": "HTTPプロキシをVpnServiceに接続",
+ "userAgent": "ユーザーエージェント",
"unifiedDelay": "統一遅延",
"unifiedDelayDesc": "ハンドシェイクなどの余分な遅延を削除",
"tcpConcurrent": "TCP並列処理",
@@ -537,5 +538,17 @@
"editSsid": "SSIDを編集",
"authorized": "許可済み",
"tapToAuthorize": "タップして許可",
- "suspended": "一時停止中..."
+ "suspended": "一時停止中...",
+ "geoOptions": "Geoオプション",
+ "geoAutoUpdate": "自動更新",
+ "geoAutoUpdateInterval": "自動更新間隔",
+ "geoAutoUpdateIntervalTip": "自動更新間隔は0より大きくなければなりません",
+ "hours": "時間",
+ "hoursCount": "{count} 時間",
+ "geoResources": "Geoリソース",
+ "geoUpdating": "{name}を更新中...",
+ "geoSkipped": "{name} スキップ済み",
+ "geoUpdated": "{name} 更新済み",
+ "secondsCount": "{count} 秒",
+ "entriesCount": "{count} エントリ"
}
diff --git a/arb/intl_ru.arb b/arb/intl_ru.arb
index e43f9078..4cdfdf30 100644
--- a/arb/intl_ru.arb
+++ b/arb/intl_ru.arb
@@ -133,6 +133,7 @@
"app": "Приложение",
"general": "Общие",
"systemProxyDesc": "Прикрепить HTTP-прокси к VpnService",
+ "userAgent": "User-Agent",
"unifiedDelay": "Унифицированная задержка",
"unifiedDelayDesc": "Убрать дополнительные задержки, такие как рукопожатие",
"tcpConcurrent": "TCP параллелизм",
@@ -537,5 +538,17 @@
"editSsid": "Изменить SSID",
"authorized": "Разрешено",
"tapToAuthorize": "Нажмите, чтобы разрешить",
- "suspended": "Приостановлено..."
+ "suspended": "Приостановлено...",
+ "geoOptions": "Настройки Geo",
+ "geoAutoUpdate": "Автообновление",
+ "geoAutoUpdateInterval": "Интервал автообновления",
+ "geoAutoUpdateIntervalTip": "Интервал автообновления должен быть больше 0",
+ "hours": "часов",
+ "hoursCount": "{count} часов",
+ "geoResources": "Ресурсы Geo",
+ "geoUpdating": "Обновление {name}...",
+ "geoSkipped": "{name} пропущено",
+ "geoUpdated": "{name} обновлено",
+ "secondsCount": "{count} секунд",
+ "entriesCount": "{count} записей"
}
diff --git a/arb/intl_zh_CN.arb b/arb/intl_zh_CN.arb
index e6df9df8..28435c10 100644
--- a/arb/intl_zh_CN.arb
+++ b/arb/intl_zh_CN.arb
@@ -133,6 +133,7 @@
"app": "应用",
"general": "常规",
"systemProxyDesc": "设置系统代理",
+ "userAgent": "用户代理",
"unifiedDelay": "统一延迟",
"unifiedDelayDesc": "去除握手等额外延迟",
"tcpConcurrent": "TCP并发",
@@ -537,5 +538,17 @@
"editSsid": "编辑SSID",
"authorized": "已授权",
"tapToAuthorize": "点击授权",
- "suspended": "挂起中..."
+ "suspended": "挂起中...",
+ "geoOptions": "Geo 选项",
+ "geoAutoUpdate": "自动更新",
+ "geoAutoUpdateInterval": "自动更新间隔",
+ "geoAutoUpdateIntervalTip": "自动更新间隔必须大于0",
+ "hours": "小时",
+ "hoursCount": "{count} 小时",
+ "geoResources": "Geo 资源",
+ "geoUpdating": "正在更新 {name}...",
+ "geoSkipped": "{name} 已跳过",
+ "geoUpdated": "{name} 已更新",
+ "secondsCount": "{count} 秒",
+ "entriesCount": "{count} 个条目"
}
diff --git a/assets/images/icon/status_2.ico b/assets/images/icon/status_2.ico
index 517c818482bffcc280a89108ebee7d0a494e6d08..c13a30654d95855b7d44a251c467632a5af6f2ee 100644
GIT binary patch
delta 2108
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
h;p1g{jwF9l_P@et&fLbai!goE~t`
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
diff --git a/assets/images/icon/status_3.ico b/assets/images/icon/status_3.ico
index 5626c15e4d854af32ca1292c4af10b4abcfab5eb..5989d61132063a004c6189d7ed8fc3f01921a36e 100644
GIT binary patch
delta 2547
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
diff --git a/assets/images/icon/status_3.png b/assets/images/icon/status_3.png
index 183f9148333cf664d3fccb629660266f86f08135..f4e7ae0cf107875229338c6f8b1a2be7c78cc02c 100644
GIT binary patch
literal 12676
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
z