|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +FlClash is a multi-platform proxy client based on ClashMeta (mihomo), built with Flutter. Supports Android, Windows, |
| 8 | +macOS, and Linux. Material You design with Surfboard-like UI. |
| 9 | + |
| 10 | +## Common Development Commands |
| 11 | + |
| 12 | +### Building |
| 13 | + |
| 14 | +```bash |
| 15 | +# Update submodules first (ClashMeta Go core lives in core/Clash.Meta/) |
| 16 | +git submodule update --init --recursive |
| 17 | + |
| 18 | +# Build core + Flutter app via setup.dart |
| 19 | +dart ./setup.dart android --arch arm64 |
| 20 | +dart ./setup.dart android --arch arm --arch-name armeabi-v7a |
| 21 | +dart ./setup.dart android --arch amd64 --arch-name x86_64 |
| 22 | +dart ./setup.dart macos --arch arm64 |
| 23 | +dart ./setup.dart macos --arch amd64 |
| 24 | +dart ./setup.dart linux --arch amd64 |
| 25 | +dart ./setup.dart windows --arch amd64 |
| 26 | + |
| 27 | +# Build only the Go core (skip Flutter packaging) |
| 28 | +dart ./setup.dart android --arch arm64 --out core |
| 29 | +dart ./setup.dart macos --arch arm64 --out core |
| 30 | +``` |
| 31 | + |
| 32 | +### Flutter Development |
| 33 | + |
| 34 | +```bash |
| 35 | +flutter pub get |
| 36 | +flutter run # Run on connected device/desktop |
| 37 | +flutter test # Run all tests (use flutter test, not dart test — models pull in Flutter types) |
| 38 | +``` |
| 39 | + |
| 40 | +### Code Generation |
| 41 | + |
| 42 | +Required after modifying models, providers, or database schema: |
| 43 | + |
| 44 | +```bash |
| 45 | +dart run build_runner build --delete-conflicting-outputs |
| 46 | +dart run build_runner watch # Continuous regeneration |
| 47 | +``` |
| 48 | + |
| 49 | +Code generation covers: Riverpod providers (`riverpod_generator`), models (`freezed`, `json_serializable`), and database |
| 50 | +tables (`drift_dev`). |
| 51 | + |
| 52 | +### Testing |
| 53 | + |
| 54 | +Tests use `package:test/test.dart` for pure Dart logic (common utils, models) and `flutter_test` for provider/widget tests. |
| 55 | +`mocktail` is the mocking framework. |
| 56 | + |
| 57 | +```bash |
| 58 | +flutter test test/models/ # Model serialization & extension round-trip tests |
| 59 | +flutter test test/core/ # CoreController tests (mocked CoreHandlerInterface) |
| 60 | +flutter test test/providers/ # Riverpod provider tests (config & app state notifiers) |
| 61 | +flutter test test/common/ # Utility function tests (utils, string, iterable, fixed, etc.) |
| 62 | +flutter test test/database/ # Database type converter tests |
| 63 | +``` |
| 64 | + |
| 65 | +**Mocking `CoreHandlerInterface`:** Use `CoreController.test(mock)` to inject a mock interface. Call |
| 66 | +`CoreController.resetInstance()` in `tearDown` to clean up the singleton between tests. Remember to |
| 67 | +`registerFallbackValue()` for freezed params used with `any()` matchers. |
| 68 | + |
| 69 | +**Provider tests:** Use `ProviderContainer` directly (no widget tree needed for simple notifiers). The Riverpod |
| 70 | +generated `update()` method takes a callback: `notifier.update((state) => newValue)`. |
| 71 | + |
| 72 | +**Model round-trip tests:** Always go through `jsonEncode`/`jsonDecode` when testing freezed models with |
| 73 | +nested objects — `toJson()` stores child objects directly (not as maps), so direct `fromJson(toJson())` |
| 74 | +fails for nested freezed types. |
| 75 | + |
| 76 | +### Build Dependencies |
| 77 | + |
| 78 | +**Linux:** `sudo apt-get install libayatana-appindicator3-dev libkeybinder-3.0-dev` |
| 79 | + |
| 80 | +**Windows:** GCC and Inno Setup. `ANDROID_NDK` env var for Android builds. |
| 81 | + |
| 82 | +**macOS:** `npm install -g appdmg` for DMG creation. |
| 83 | + |
| 84 | +## Architecture |
| 85 | + |
| 86 | +### Core Integration (Go ClashMeta <-> Flutter) |
| 87 | + |
| 88 | +This is the most important architectural concept. The Go proxy core (`core/`) operates in two modes: |
| 89 | + |
| 90 | +- **Android (lib mode):** Go core compiled as C shared library (`libclash.so`) via `go build -buildmode=c-shared` with |
| 91 | + CGO. Flutter calls it via FFI through the `service` plugin. Dart-side: `lib/core/lib.dart` (`CoreLib` class). |
| 92 | + |
| 93 | +- **Desktop (core mode):** Go core runs as a separate process with `CGO_ENABLED=0`. Flutter communicates via |
| 94 | + JSON-over-socket (Unix socket on macOS/Linux, TCP on Windows). Dart-side: `lib/core/service.dart` (`CoreService` |
| 95 | + class). |
| 96 | + |
| 97 | +`lib/core/controller.dart` (`CoreController`) selects the implementation based on platform. `lib/core/interface.dart` |
| 98 | +defines the shared `CoreHandlerInterface`. |
| 99 | + |
| 100 | +Go core key files: `core/hub.go` (handler functions), `core/action.go` (dispatch), `core/lib.go` (CGO exports), |
| 101 | +`core/server.go` (socket server). |
| 102 | + |
| 103 | +### State Management (Riverpod) |
| 104 | + |
| 105 | +Provider files in `lib/providers/`: |
| 106 | + |
| 107 | +- `app.dart` - Runtime/UI state (logs, traffic, delays, loading, navigation) |
| 108 | +- `config.dart` - Persistent config providers (app settings, theme, VPN, proxy style) |
| 109 | +- `state.dart` - Derived/computed providers (navigation, proxy, tray, color scheme) |
| 110 | +- `action.dart` - Business logic notifiers (setup, backup, core lifecycle, proxy selection) |
| 111 | +- `database.dart` - Drift database provider wrappers |
| 112 | + |
| 113 | +`globalState` (`lib/state.dart`) is a singleton holding app lifecycle, timers, theme, and the start/stop state. |
| 114 | +Providers are generated into `lib/providers/generated/`. |
| 115 | + |
| 116 | +### Database (Drift/SQLite) |
| 117 | + |
| 118 | +Type-safe SQLite via Drift in `lib/database/`. Tables: `Profiles`, `Scripts`, `ProxyGroups`, `GlobalRules`, |
| 119 | +`ProfileAddedRules`, `ProfileCustomRules`, `ProfileDisabledRuleIds`, `Icons`, `Links`. Uses fractional indexing for |
| 120 | +ordering. |
| 121 | + |
| 122 | +### Manager Stack (Widget Tree) |
| 123 | + |
| 124 | +Managers are nested InheritedWidgets/StatefulWidgets in `lib/application.dart`: |
| 125 | + |
| 126 | +``` |
| 127 | +AppEnvManager > StatusManager > ThemeManager |
| 128 | + > [Desktop: WindowManager > TrayManager > HotKeyManager > ProxyManager] |
| 129 | + > ConnectivityManager > CoreManager > AppStateManager |
| 130 | + > [Mobile: AndroidManager > VpnManager | Desktop: WindowHeaderContainer] |
| 131 | +``` |
| 132 | + |
| 133 | +Each manager in `lib/manager/` handles a specific platform concern. Desktop-only managers are conditionally inserted. |
| 134 | + |
| 135 | +### Core Controller + Actions |
| 136 | + |
| 137 | +`lib/core/controller.dart` (`CoreController`) is a singleton facade over `CoreHandlerInterface`. All 25+ public methods |
| 138 | +delegate to the platform-specific interface (Android FFI or desktop socket). Has `@visibleForTesting` constructor and |
| 139 | +`resetInstance()` for test injection. |
| 140 | + |
| 141 | +Business logic lives in Riverpod notifier classes in `lib/providers/action.dart` (~960 lines, should be split): |
| 142 | + |
| 143 | +- `CommonAction` — update check, common UI operations |
| 144 | +- `SetupAction` — config setup, TUN management |
| 145 | +- `BackupAction` — backup/restore with WebDAV sync |
| 146 | +- `CoreAction` — core lifecycle (init, connect, restart, shutdown) |
| 147 | +- `SystemAction` — system integration (tray, exit, brightness) |
| 148 | +- `StoreAction` — profile storage operations |
| 149 | +- `ThemeAction` — theme state updates |
| 150 | +- `ProxiesAction` — group management, proxy selection |
| 151 | +- `ProfilesAction` — profile CRUD, auto-update, import |
| 152 | + |
| 153 | +### Platform Managers (`lib/manager/`) |
| 154 | + |
| 155 | +Desktop: `WindowManager`, `TrayManager`, `HotKeyManager`, `ProxyManager` |
| 156 | +Mobile: `AndroidManager`, `TileManager`, `VpnManager` |
| 157 | +Shared: `ConnectivityManager`, `CoreManager`, `AppStateManager`, `StatusManager`, `ThemeManager` |
| 158 | + |
| 159 | +### Local Plugins (`plugins/`) |
| 160 | + |
| 161 | +- `proxy` - System proxy configuration |
| 162 | +- `rust_api` - Flutter Rust Bridge FFI plugin (named pipe / local socket communication) |
| 163 | +- `tray_manager` - System tray (forked/custom) |
| 164 | +- `wifi_ssid` - Wi-Fi SSID detection |
| 165 | +- `window_ext` - Window extensions |
| 166 | +- `flutter_distributor` - App packaging/distribution |
| 167 | + |
| 168 | +### Rust Helper Service (`services/helper/`) |
| 169 | + |
| 170 | +Windows-only privileged helper for starting the core as admin and managing TUN. Built with |
| 171 | +`cargo build --release --features windows-service`. Token-based auth with Flutter app. |
| 172 | + |
| 173 | +### Localization |
| 174 | + |
| 175 | +ARB files in `arb/`. Generated via `flutter_intl` into `lib/l10n/`. Use `AppLocalizations.of(context)!` for strings. |
| 176 | + |
| 177 | +**Supported locales:** `en`, `zh_CN`, `ja`, `ru` |
| 178 | + |
| 179 | +**Access patterns:** |
| 180 | + |
| 181 | +- In widgets with BuildContext: `context.appLocalizations.key` (import `common.dart`) |
| 182 | +- In controllers/providers/non-widget code: `currentAppLocalizations.key` (import `app_localizations.dart`) |
0 commit comments