Compare commits

..

1 Commits

Author SHA1 Message Date
chen08209
0ba6ef1b9c Support override script
Support proxies search

Add some scenes auto close connections

Update core

Optimize more details
2025-05-28 14:43:46 +08:00
122 changed files with 5000 additions and 3464 deletions

View File

@@ -68,6 +68,7 @@ jobs:
with:
channel: ${{ (startsWith(matrix.os, 'windows-11-arm') || startsWith(matrix.os, 'ubuntu-24.04-arm')) && 'master' || 'stable' }}
cache: true
flutter-version: 3.29.3
- name: Get Flutter Dependency
run: flutter pub get

View File

@@ -1,100 +0,0 @@
plugins {
id "com.android.application"
id "kotlin-android"
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
def defStoreFile = file("keystore.jks")
def defStorePassword = localProperties.getProperty('storePassword')
def defKeyAlias = localProperties.getProperty('keyAlias')
def defKeyPassword = localProperties.getProperty('keyPassword')
def isRelease = defStoreFile.exists() && defStorePassword != null && defKeyAlias != null && defKeyPassword != null
android {
namespace "com.follow.clash"
compileSdk 35
ndkVersion = "28.0.13004108"
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '17'
}
sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}
signingConfigs {
if (isRelease) {
release {
storeFile defStoreFile
storePassword defStorePassword
keyAlias defKeyAlias
keyPassword defKeyPassword
}
}
}
defaultConfig {
applicationId "com.follow.clash"
minSdkVersion 21
targetSdkVersion 35
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}
buildTypes {
debug {
minifyEnabled false
applicationIdSuffix '.debug'
}
release {
minifyEnabled true
debuggable false
if (isRelease) {
signingConfig signingConfigs.release
} else {
signingConfig signingConfigs.debug
}
proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
}
}
}
flutter {
source '../..'
}
dependencies {
implementation project(":core")
implementation 'androidx.core:core-splashscreen:1.0.1'
implementation 'com.google.code.gson:gson:2.10.1'
implementation("com.android.tools.smali:smali-dexlib2:3.0.9") {
exclude group: "com.google.guava", module: "guava"
}
}

View File

@@ -0,0 +1,93 @@
import java.util.Properties
plugins {
id("com.android.application")
id("kotlin-android")
id("dev.flutter.flutter-gradle-plugin")
}
val localPropertiesFile = rootProject.file("local.properties")
val localProperties = Properties().apply {
if (localPropertiesFile.exists()) {
localPropertiesFile.inputStream().use { load(it) }
}
}
val mStoreFile: File = file("keystore.jks")
val mStorePassword: String? = localProperties.getProperty("storePassword")
val mKeyAlias: String? = localProperties.getProperty("keyAlias")
val mKeyPassword: String? = localProperties.getProperty("keyPassword")
val isRelease = mStoreFile.exists()
&& mStorePassword != null
&& mKeyAlias != null
&& mKeyPassword != null
android {
namespace = "com.follow.clash"
compileSdk = 35
ndkVersion = "28.0.13004108"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
applicationId = "com.follow.clash"
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
}
signingConfigs {
if (isRelease) {
create("release") {
storeFile = mStoreFile
storePassword = mStorePassword
keyAlias = mKeyAlias
keyPassword = mKeyPassword
}
}
}
buildTypes {
debug {
isMinifyEnabled = false
applicationIdSuffix = ".debug"
}
release {
isMinifyEnabled = true
isDebuggable = false
signingConfig = if (isRelease) {
signingConfigs.getByName("release")
} else {
signingConfigs.getByName("debug")
}
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
flutter {
source = "../.."
}
dependencies {
implementation(project(":core"))
implementation("androidx.core:core-splashscreen:1.0.1")
implementation("com.google.code.gson:gson:2.10.1")
implementation("com.android.tools.smali:smali-dexlib2:3.0.9") {
exclude(group = "com.google.guava", module = "guava")
}
}

View File

@@ -1,33 +0,0 @@
buildscript {
ext.kotlin_version = "${kotlin_version}"
repositories {
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:$agp_version"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
project.evaluationDependsOn(':core')
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

22
android/build.gradle.kts Normal file
View File

@@ -0,0 +1,22 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}

View File

@@ -1,27 +0,0 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
settings.ext.flutterSdkPath = flutterSdkPath()
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "$agp_version" apply false
id "org.jetbrains.kotlin.android" version "$kotlin_version" apply false
}
include ":app"
include ':core'

View File

@@ -0,0 +1,29 @@
pluginManagement {
val flutterSdkPath = run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.9.2" apply false
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
}
include(":app")
include(":core")

View File

@@ -13,7 +13,6 @@
"resourcesDesc": "External resource related info",
"trafficUsage": "Traffic usage",
"coreInfo": "Core info",
"nullCoreInfoDesc": "Unable to obtain core info",
"networkSpeed": "Network speed",
"outboundMode": "Outbound mode",
"networkDetection": "Network detection",
@@ -22,7 +21,6 @@
"noProxy": "No proxy",
"noProxyDesc": "Please create a profile or add a valid profile",
"nullProfileDesc": "No profile, Please add a profile",
"nullLogsDesc": "No logs",
"settings": "Settings",
"language": "Language",
"defaultText": "Default",
@@ -149,8 +147,6 @@
"addressHelp": "WebDAV server address",
"addressTip": "Please enter a valid WebDAV address",
"password": "Password",
"passwordTip": "Password cannot be empty",
"accountTip": "Account cannot be empty",
"checkUpdate": "Check for updates",
"discoverNewVersion": "Discover the new version",
"checkUpdateError": "The current application is already the latest version",
@@ -185,8 +181,6 @@
"expirationTime": "Expiration time",
"connections": "Connections",
"connectionsDesc": "View current connections data",
"nullRequestsDesc": "No requests",
"nullConnectionsDesc": "No connections",
"intranetIP": "Intranet IP",
"view": "View",
"cut": "Cut",
@@ -219,7 +213,6 @@
"autoCloseConnectionsDesc": "Auto close connections after change node",
"onlyStatisticsProxy": "Only statistics proxy",
"onlyStatisticsProxyDesc": "When turned on, only statistics proxy traffic",
"deleteProfileTip": "Sure you want to delete the current profile?",
"pureBlackMode": "Pure black mode",
"keepAliveIntervalDesc": "Tcp keep alive interval",
"entries": " entries",
@@ -250,7 +243,6 @@
"dnsDesc": "Update DNS related settings",
"key": "Key",
"value": "Value",
"notEmpty": "Cannot be empty",
"hostsDesc": "Add Hosts",
"vpnTip": "Changes take effect after restarting the VPN",
"vpnEnableDesc": "Auto routes all system traffic through VpnService",
@@ -337,15 +329,12 @@
"fileIsUpdate": "The file has been modified. Do you want to save the changes?",
"profileHasUpdate": "The profile has been modified. Do you want to disable auto update?",
"hasCacheChange": "Do you want to cache the changes?",
"nullProxies": "No proxies",
"copySuccess": "Copy success",
"copyLink": "Copy link",
"exportFile": "Export file",
"cacheCorrupt": "The cache is corrupt. Do you want to clear it?",
"detectionTip": "Relying on third-party api is for reference only",
"listen": "Listen",
"keyExists": "The current key already exists",
"valueExists": "The current value already exists",
"undo": "undo",
"redo": "redo",
"none": "none",
@@ -353,28 +342,21 @@
"basicConfigDesc": "Modify the basic configuration globally",
"selectedCountTitle": "{count} items have been selected",
"addRule": "Add rule",
"ruleProviderEmptyTip": "Rule provider cannot be empty",
"ruleName": "Rule name",
"content": "Content",
"contentEmptyTip": "Content cannot be empty",
"subRule": "Sub rule",
"subRuleEmptyTip": "Sub rule content cannot be empty",
"ruleTarget": "Rule target",
"ruleTargetEmptyTip": "Rule target cannot be empty",
"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",
"deleteRuleTip": "Are you sure you want to delete the selected rule?",
"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?",
"deleteColorTip": "Are you sure you want to delete the current color?",
"colorExists": "Current color already exists",
"colorSchemes": "Color schemes",
"palette": "Palette",
"tonalSpotScheme": "TonalSpot",
@@ -400,5 +382,20 @@
"recoveryStrategy": "Recovery strategy",
"recoveryStrategy_override": "Override",
"recoveryStrategy_compatible": "Compatible",
"logsTest": "Logs test"
"logsTest": "Logs test",
"emptyTip": "{label} cannot be empty",
"urlTip": "{label} must be a url",
"proxyPortTip": "proxy port must be between 1024 and 49151",
"numberTip": "{label} must be a number",
"interval": "Interval",
"existsTip": "Current {label} already exists",
"deleteTip": "Are you sure you want to delete the current {label}?",
"deleteMultipTip": "Are you sure you want to delete the selected {label}?",
"nullTip": "No {label} at the moment",
"script": "Script",
"color": "Color",
"rename": "Rename",
"unnamed": "Unnamed",
"pleaseEnterScriptName": "Please enter a script name",
"overrideInvalidTip": "Does not take effect in script mode"
}

View File

@@ -13,7 +13,6 @@
"resourcesDesc": "外部リソース関連情報",
"trafficUsage": "トラフィック使用量",
"coreInfo": "コア情報",
"nullCoreInfoDesc": "コア情報を取得できません",
"networkSpeed": "ネットワーク速度",
"outboundMode": "アウトバウンドモード",
"networkDetection": "ネットワーク検出",
@@ -22,7 +21,6 @@
"noProxy": "プロキシなし",
"noProxyDesc": "プロファイルを作成するか、有効なプロファイルを追加してください",
"nullProfileDesc": "プロファイルがありません。追加してください",
"nullLogsDesc": "ログがありません",
"settings": "設定",
"language": "言語",
"defaultText": "デフォルト",
@@ -149,8 +147,6 @@
"addressHelp": "WebDAVサーバーアドレス",
"addressTip": "有効なWebDAVアドレスを入力",
"password": "パスワード",
"passwordTip": "パスワードは必須です",
"accountTip": "アカウントは必須です",
"checkUpdate": "更新を確認",
"discoverNewVersion": "新バージョンを発見",
"checkUpdateError": "アプリは最新版です",
@@ -185,8 +181,6 @@
"expirationTime": "有効期限",
"connections": "接続",
"connectionsDesc": "現在の接続データを表示",
"nullRequestsDesc": "リクエストなし",
"nullConnectionsDesc": "接続なし",
"intranetIP": "イントラネットIP",
"view": "表示",
"cut": "切り取り",
@@ -219,7 +213,6 @@
"autoCloseConnectionsDesc": "ノード変更後に接続を自動閉じる",
"onlyStatisticsProxy": "プロキシのみ統計",
"onlyStatisticsProxyDesc": "有効化するとプロキシトラフィックのみ統計",
"deleteProfileTip": "現在のプロファイルを削除しますか?",
"pureBlackMode": "純黒モード",
"keepAliveIntervalDesc": "TCPキープアライブ間隔",
"entries": " エントリ",
@@ -250,7 +243,6 @@
"dnsDesc": "DNS関連設定の更新",
"key": "キー",
"value": "値",
"notEmpty": "空欄不可",
"hostsDesc": "ホストを追加",
"vpnTip": "変更はVPN再起動後に有効",
"vpnEnableDesc": "VpnService経由で全システムトラフィックをルーティング",
@@ -337,15 +329,12 @@
"fileIsUpdate": "ファイルが変更されました。保存しますか?",
"profileHasUpdate": "プロファイルが変更されました。自動更新を無効化しますか?",
"hasCacheChange": "変更をキャッシュしますか?",
"nullProxies": "プロキシなし",
"copySuccess": "コピー成功",
"copyLink": "リンクをコピー",
"exportFile": "ファイルをエクスポート",
"cacheCorrupt": "キャッシュが破損しています。クリアしますか?",
"detectionTip": "サードパーティAPIに依存参考値",
"listen": "リスン",
"keyExists": "現在のキーは既に存在します",
"valueExists": "現在の値は既に存在します",
"undo": "元に戻す",
"redo": "やり直す",
"none": "なし",
@@ -353,28 +342,21 @@
"basicConfigDesc": "基本設定をグローバルに変更",
"selectedCountTitle": "{count} 項目が選択されています",
"addRule": "ルールを追加",
"ruleProviderEmptyTip": "ルールプロバイダーは必須です",
"ruleName": "ルール名",
"content": "内容",
"contentEmptyTip": "内容は必須です",
"subRule": "サブルール",
"subRuleEmptyTip": "サブルールの内容は必須です",
"ruleTarget": "ルール対象",
"ruleTargetEmptyTip": "ルール対象は必須です",
"sourceIp": "送信元IP",
"noResolve": "IPを解決しない",
"getOriginRules": "元のルールを取得",
"overrideOriginRules": "元のルールを上書き",
"addedOriginRules": "元のルールに追加",
"enableOverride": "上書きを有効化",
"deleteRuleTip": "選択したルールを削除しますか?",
"saveChanges": "変更を保存しますか?",
"generalDesc": "一般設定を変更",
"findProcessModeDesc": "有効化するとパフォーマンスが若干低下します",
"tabAnimationDesc": "モバイル表示でのみ有効",
"saveTip": "保存してもよろしいですか?",
"deleteColorTip": "現在の色を削除しますか?",
"colorExists": "この色は既に存在します",
"colorSchemes": "カラースキーム",
"palette": "パレット",
"tonalSpotScheme": "トーンスポット",
@@ -401,5 +383,20 @@
"recoveryStrategy": "リカバリー戦略",
"recoveryStrategy_override": "オーバーライド",
"recoveryStrategy_compatible": "互換性",
"logsTest": "ログテスト"
"logsTest": "ログテスト",
"emptyTip": "{label}は空欄にできません",
"urlTip": "{label}はURLである必要があります",
"proxyPortTip": "プロキシポートは1024から49151の間でなければなりません",
"numberTip": "{label}は数字でなければなりません",
"interval": "インターバル",
"existsTip": "現在の{label}は既に存在しています",
"deleteTip": "現在の{label}を削除してもよろしいですか?",
"deleteMultipTip": "選択された{label}を削除してもよろしいですか?",
"nullTip": "現在{label}はありません",
"script": "スクリプト",
"color": "カラー",
"rename": "リネーム",
"unnamed": "無題",
"pleaseEnterScriptName": "スクリプト名を入力してください",
"overrideInvalidTip": "スクリプトモードでは有効になりません"
}

View File

@@ -13,7 +13,6 @@
"resourcesDesc": "Информация, связанная с внешними ресурсами",
"trafficUsage": "Использование трафика",
"coreInfo": "Информация о ядре",
"nullCoreInfoDesc": "Не удалось получить информацию о ядре",
"networkSpeed": "Скорость сети",
"outboundMode": "Режим исходящего трафика",
"networkDetection": "Обнаружение сети",
@@ -22,7 +21,6 @@
"noProxy": "Нет прокси",
"noProxyDesc": "Пожалуйста, создайте профиль или добавьте действительный профиль",
"nullProfileDesc": "Нет профиля, пожалуйста, добавьте профиль",
"nullLogsDesc": "Нет логов",
"settings": "Настройки",
"language": "Язык",
"defaultText": "По умолчанию",
@@ -149,8 +147,6 @@
"addressHelp": "Адрес сервера WebDAV",
"addressTip": "Пожалуйста, введите действительный адрес WebDAV",
"password": "Пароль",
"passwordTip": "Пароль не может быть пустым",
"accountTip": "Аккаунт не может быть пустым",
"checkUpdate": "Проверить обновления",
"discoverNewVersion": "Обнаружена новая версия",
"checkUpdateError": "Текущее приложение уже является последней версией",
@@ -185,8 +181,6 @@
"expirationTime": "Время истечения",
"connections": "Соединения",
"connectionsDesc": "Просмотр текущих данных о соединениях",
"nullRequestsDesc": "Нет запросов",
"nullConnectionsDesc": "Нет соединений",
"intranetIP": "Внутренний IP",
"view": "Просмотр",
"cut": "Вырезать",
@@ -219,7 +213,6 @@
"autoCloseConnectionsDesc": "Автоматически закрывать соединения после смены узла",
"onlyStatisticsProxy": "Только статистика прокси",
"onlyStatisticsProxyDesc": "При включении будет учитываться только трафик прокси",
"deleteProfileTip": "Вы уверены, что хотите удалить текущий профиль?",
"pureBlackMode": "Чисто черный режим",
"keepAliveIntervalDesc": "Интервал поддержания TCP-соединения",
"entries": " записей",
@@ -250,7 +243,6 @@
"dnsDesc": "Обновление настроек, связанных с DNS",
"key": "Ключ",
"value": "Значение",
"notEmpty": "Не может быть пустым",
"hostsDesc": "Добавить Hosts",
"vpnTip": "Изменения вступят в силу после перезапуска VPN",
"vpnEnableDesc": "Автоматически направляет весь системный трафик через VpnService",
@@ -337,15 +329,12 @@
"fileIsUpdate": "Файл был изменен. Хотите сохранить изменения?",
"profileHasUpdate": "Профиль был изменен. Хотите отключить автообновление?",
"hasCacheChange": "Хотите сохранить изменения в кэше?",
"nullProxies": "Нет прокси",
"copySuccess": "Копирование успешно",
"copyLink": "Копировать ссылку",
"exportFile": "Экспорт файла",
"cacheCorrupt": "Кэш поврежден. Хотите очистить его?",
"detectionTip": "Опирается на сторонний API, только для справки",
"listen": "Слушать",
"keyExists": "Текущий ключ уже существует",
"valueExists": "Текущее значение уже существует",
"undo": "Отменить",
"redo": "Повторить",
"none": "Нет",
@@ -353,28 +342,21 @@
"basicConfigDesc": "Глобальное изменение базовых настроек",
"selectedCountTitle": "Выбрано {count} элементов",
"addRule": "Добавить правило",
"ruleProviderEmptyTip": "Поставщик правил не может быть пустым",
"ruleName": "Название правила",
"content": "Содержание",
"contentEmptyTip": "Содержание не может быть пустым",
"subRule": "Подправило",
"subRuleEmptyTip": "Содержание подправила не может быть пустым",
"ruleTarget": "Цель правила",
"ruleTargetEmptyTip": "Цель правила не может быть пустой",
"sourceIp": "Исходный IP",
"noResolve": "Не разрешать IP",
"getOriginRules": "Получить оригинальные правила",
"overrideOriginRules": "Переопределить оригинальное правило",
"addedOriginRules": "Добавить к оригинальным правилам",
"enableOverride": "Включить переопределение",
"deleteRuleTip": "Вы уверены, что хотите удалить выбранное правило?",
"saveChanges": "Сохранить изменения?",
"generalDesc": "Изменение общих настроек",
"findProcessModeDesc": "При включении возможны небольшие потери производительности",
"tabAnimationDesc": "Действительно только в мобильном виде",
"saveTip": "Вы уверены, что хотите сохранить?",
"deleteColorTip": "Удалить текущий цвет?",
"colorExists": "Этот цвет уже существует",
"colorSchemes": "Цветовые схемы",
"palette": "Палитра",
"tonalSpotScheme": "Тональный акцент",
@@ -401,5 +383,20 @@
"recoveryStrategy": "Стратегия восстановления",
"recoveryStrategy_override": "Переопределение",
"recoveryStrategy_compatible": "Совместимый",
"logsTest": "Тест журналов"
"logsTest": "Тест журналов",
"emptyTip": "{label} не может быть пустым",
"urlTip": "{label} должен быть URL",
"proxyPortTip": "Порт прокси должен быть в диапазоне от 1024 до 49151",
"numberTip": "{label} должно быть числом",
"interval": "Интервал",
"existsTip": "Текущий {label} уже существует",
"deleteTip": "Вы уверены, что хотите удалить текущий {label}?",
"deleteMultipTip": "Вы уверены, что хотите удалить выбранные {label}?",
"nullTip": "Сейчас {label} нет",
"script": "Скрипт",
"color": "Цвет",
"rename": "Переименовать",
"unnamed": "Без имени",
"pleaseEnterScriptName": "Пожалуйста, введите название скрипта",
"overrideInvalidTip": "В скриптовом режиме не действует"
}

View File

@@ -13,7 +13,6 @@
"resourcesDesc": "外部资源相关信息",
"trafficUsage": "流量统计",
"coreInfo": "内核信息",
"nullCoreInfoDesc": "无法获取内核信息",
"networkSpeed": "网络速度",
"outboundMode": "出站模式",
"networkDetection": "网络检测",
@@ -22,7 +21,6 @@
"noProxy": "暂无代理",
"noProxyDesc": "请创建配置文件或者添加有效配置文件",
"nullProfileDesc": "没有配置文件,请先添加配置文件",
"nullLogsDesc": "暂无日志",
"settings": "设置",
"language": "语言",
"defaultText": "默认",
@@ -149,8 +147,6 @@
"addressHelp": "WebDAV服务器地址",
"addressTip": "请输入有效的WebDAV地址",
"password": "密码",
"passwordTip": "密码不能为空",
"accountTip": "账号不能为空",
"checkUpdate": "检查更新",
"discoverNewVersion": "发现新版本",
"checkUpdateError": "当前应用已经是最新版了",
@@ -185,8 +181,6 @@
"expirationTime": "到期时间",
"connections": "连接",
"connectionsDesc": "查看当前连接数据",
"nullRequestsDesc": "暂无请求",
"nullConnectionsDesc": "暂无连接",
"intranetIP": "内网 IP",
"view": "查看",
"cut": "剪切",
@@ -219,7 +213,6 @@
"autoCloseConnectionsDesc": "切换节点后自动关闭连接",
"onlyStatisticsProxy": "仅统计代理",
"onlyStatisticsProxyDesc": "开启后,将只统计代理流量",
"deleteProfileTip": "确定要删除当前配置吗?",
"pureBlackMode": "纯黑模式",
"keepAliveIntervalDesc": "TCP保持活动间隔",
"entries": "个条目",
@@ -250,7 +243,6 @@
"dnsDesc": "更新DNS相关设置",
"key": "键",
"value": "值",
"notEmpty": "不能为空",
"hostsDesc": "追加Hosts",
"vpnTip": "重启VPN后改变生效",
"vpnEnableDesc": "通过VpnService自动路由系统所有流量",
@@ -337,15 +329,12 @@
"fileIsUpdate": "文件有修改,是否保存修改",
"profileHasUpdate": "配置文件已经修改,是否关闭自动更新 ",
"hasCacheChange": "是否缓存修改",
"nullProxies": "暂无代理",
"copySuccess": "复制成功",
"copyLink": "复制链接",
"exportFile": "导出文件",
"cacheCorrupt": "缓存已损坏,是否清空?",
"detectionTip": "依赖第三方api仅供参考",
"listen": "监听",
"keyExists": "当前键已存在",
"valueExists": "当前值已存在",
"undo": "撤销",
"redo": "重做",
"none": "无",
@@ -353,28 +342,21 @@
"basicConfigDesc": "全局修改基本配置",
"selectedCountTitle": "已选择 {count} 项",
"addRule": "添加规则",
"ruleProviderEmptyTip": "规则提供者不能为空",
"ruleName": "规则名称",
"content": "内容",
"contentEmptyTip": "内容不能为空",
"subRule": "子规则",
"subRuleEmptyTip": "子规则内容不能为空",
"ruleTarget": "规则目标",
"ruleTargetEmptyTip": "规则目标不能为空",
"sourceIp": "源IP",
"noResolve": "不解析IP",
"getOriginRules": "获取原始规则",
"overrideOriginRules": "覆盖原始规则",
"addedOriginRules": "附加到原始规则",
"enableOverride": "启用覆写",
"deleteRuleTip": "确定要删除选中的规则吗?",
"saveChanges": "是否保存更改?",
"generalDesc": "修改通用设置",
"findProcessModeDesc": "开启后会有一定性能损耗",
"tabAnimationDesc": "仅在移动视图中有效",
"saveTip": "确定要保存吗?",
"deleteColorTip": "确定删除当前颜色吗?",
"colorExists": "该颜色已存在",
"colorSchemes": "配色方案",
"palette": "调色板",
"tonalSpotScheme": "调性点缀",
@@ -401,5 +383,20 @@
"recoveryStrategy": "恢复策略",
"recoveryStrategy_override": "覆盖",
"recoveryStrategy_compatible": "兼容",
"logsTest": "日志测试"
"logsTest": "日志测试",
"emptyTip": "{label}不能为空",
"urlTip": "{label}必须为URL",
"proxyPortTip": "代理端口必须在1024到49151之间",
"numberTip": "{label}必须为数字",
"interval": "间隔",
"existsTip": "{label}当前已存在",
"deleteTip": "确定删除当前{label}吗?",
"deleteMultipTip": "确定删除选中的{label}吗?",
"nullTip": "暂无{label}",
"script": "脚本",
"color": "颜色",
"rename": "重命名",
"unnamed": "未命名",
"pleaseEnterScriptName": "请输入脚本名称",
"overrideInvalidTip": "在脚本模式下不生效"
}

View File

@@ -5,16 +5,17 @@ import (
)
type Action struct {
Id string `json:"id"`
Method Method `json:"method"`
Data interface{} `json:"data"`
DefaultValue interface{} `json:"default-value"`
Id string `json:"id"`
Method Method `json:"method"`
Data interface{} `json:"data"`
}
type ActionResult struct {
Id string `json:"id"`
Method Method `json:"method"`
Data interface{} `json:"data"`
Code int `json:"code"`
Port int64
}
func (result ActionResult) Json() ([]byte, error) {
@@ -22,102 +23,117 @@ func (result ActionResult) Json() ([]byte, error) {
return data, err
}
func (action Action) getResult(data interface{}) []byte {
resultAction := ActionResult{
Id: action.Id,
Method: action.Method,
Data: data,
}
res, _ := resultAction.Json()
return res
func (result ActionResult) success(data interface{}) {
result.Code = 0
result.Data = data
result.send()
}
func handleAction(action *Action, result func(data interface{})) {
func (result ActionResult) error(data interface{}) {
result.Code = -1
result.Data = data
result.send()
}
func handleAction(action *Action, result ActionResult) {
switch action.Method {
case initClashMethod:
paramsString := action.Data.(string)
result(handleInitClash(paramsString))
result.success(handleInitClash(paramsString))
return
case getIsInitMethod:
result(handleGetIsInit())
result.success(handleGetIsInit())
return
case forceGcMethod:
handleForceGc()
result(true)
result.success(true)
return
case shutdownMethod:
result(handleShutdown())
result.success(handleShutdown())
return
case validateConfigMethod:
data := []byte(action.Data.(string))
result(handleValidateConfig(data))
result.success(handleValidateConfig(data))
return
case updateConfigMethod:
data := []byte(action.Data.(string))
result(handleUpdateConfig(data))
result.success(handleUpdateConfig(data))
return
case setupConfigMethod:
data := []byte(action.Data.(string))
result.success(handleSetupConfig(data))
return
case getProxiesMethod:
result(handleGetProxies())
result.success(handleGetProxies())
return
case changeProxyMethod:
data := action.Data.(string)
handleChangeProxy(data, func(value string) {
result(value)
result.success(value)
})
return
case getTrafficMethod:
result(handleGetTraffic())
result.success(handleGetTraffic())
return
case getTotalTrafficMethod:
result(handleGetTotalTraffic())
result.success(handleGetTotalTraffic())
return
case resetTrafficMethod:
handleResetTraffic()
result(true)
result.success(true)
return
case asyncTestDelayMethod:
data := action.Data.(string)
handleAsyncTestDelay(data, func(value string) {
result(value)
result.success(value)
})
return
case getConnectionsMethod:
result(handleGetConnections())
result.success(handleGetConnections())
return
case closeConnectionsMethod:
result(handleCloseConnections())
result.success(handleCloseConnections())
return
case resetConnectionsMethod:
result(handleResetConnections())
result.success(handleResetConnections())
return
case getConfigMethod:
path := action.Data.(string)
config, err := handleGetConfig(path)
if err != nil {
result.error(err)
return
}
result.success(config)
return
case closeConnectionMethod:
id := action.Data.(string)
result(handleCloseConnection(id))
result.success(handleCloseConnection(id))
return
case getExternalProvidersMethod:
result(handleGetExternalProviders())
result.success(handleGetExternalProviders())
return
case getExternalProviderMethod:
externalProviderName := action.Data.(string)
result(handleGetExternalProvider(externalProviderName))
result.success(handleGetExternalProvider(externalProviderName))
case updateGeoDataMethod:
paramsString := action.Data.(string)
var params = map[string]string{}
err := json.Unmarshal([]byte(paramsString), &params)
if err != nil {
result(err.Error())
result.success(err.Error())
return
}
geoType := params["geo-type"]
geoName := params["geo-name"]
handleUpdateGeoData(geoType, geoName, func(value string) {
result(value)
result.success(value)
})
return
case updateExternalProviderMethod:
providerName := action.Data.(string)
handleUpdateExternalProvider(providerName, func(value string) {
result(value)
result.success(value)
})
return
case sideLoadExternalProviderMethod:
@@ -125,59 +141,48 @@ func handleAction(action *Action, result func(data interface{})) {
var params = map[string]string{}
err := json.Unmarshal([]byte(paramsString), &params)
if err != nil {
result(err.Error())
result.success(err.Error())
return
}
providerName := params["providerName"]
data := params["data"]
handleSideLoadExternalProvider(providerName, []byte(data), func(value string) {
result(value)
result.success(value)
})
return
case startLogMethod:
handleStartLog()
result(true)
result.success(true)
return
case stopLogMethod:
handleStopLog()
result(true)
result.success(true)
return
case startListenerMethod:
result(handleStartListener())
result.success(handleStartListener())
return
case stopListenerMethod:
result(handleStopListener())
result.success(handleStopListener())
return
case getCountryCodeMethod:
ip := action.Data.(string)
handleGetCountryCode(ip, func(value string) {
result(value)
result.success(value)
})
return
case getMemoryMethod:
handleGetMemory(func(value string) {
result(value)
})
return
case getProfileMethod:
profileId := action.Data.(string)
handleGetMemory(func(value string) {
result(handleGetProfile(profileId))
result.success(value)
})
return
case setStateMethod:
data := action.Data.(string)
handleSetState(data)
result(true)
result.success(true)
case crashMethod:
result(true)
result.success(true)
handleCrash()
default:
handle := nextHandle(action, result)
if handle {
return
} else {
result(action.DefaultValue)
}
nextHandle(action, result)
}
}

View File

@@ -1,9 +1,10 @@
package main
import (
b "bytes"
"context"
"encoding/json"
"errors"
"fmt"
"github.com/metacubex/mihomo/adapter"
"github.com/metacubex/mihomo/adapter/inbound"
"github.com/metacubex/mihomo/adapter/outboundgroup"
@@ -21,31 +22,16 @@ import (
"github.com/metacubex/mihomo/log"
rp "github.com/metacubex/mihomo/rules/provider"
"github.com/metacubex/mihomo/tunnel"
"github.com/samber/lo"
"os"
"path/filepath"
"strings"
"sync"
)
func splitByMultipleSeparators(s string) interface{} {
isSeparator := func(r rune) bool {
return r == ',' || r == ' ' || r == ';'
}
parts := strings.FieldsFunc(s, isSeparator)
if len(parts) > 1 {
return parts
}
return s
}
var (
version = 0
isRunning = false
runLock sync.Mutex
ips = []string{"ipwho.is", "api.ip.sb", "ipapi.co", "ipinfo.io"}
b, _ = batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](50))
currentConfig *config.Config
version = 0
isRunning = false
runLock sync.Mutex
mBatch, _ = batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](50))
)
type ExternalProviders []ExternalProvider
@@ -54,54 +40,6 @@ func (a ExternalProviders) Len() int { return len(a) }
func (a ExternalProviders) Less(i, j int) bool { return a[i].Name < a[j].Name }
func (a ExternalProviders) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func readFile(path string) ([]byte, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return data, err
}
func getProfilePath(id string) string {
return filepath.Join(constant.Path.HomeDir(), "profiles", id+".yaml")
}
func getProfileProvidersPath(id string) string {
return filepath.Join(constant.Path.HomeDir(), "providers", id)
}
func getRawConfigWithId(id string) *config.RawConfig {
path := getProfilePath(id)
bytes, err := readFile(path)
if err != nil {
return config.DefaultRawConfig()
}
prof, err := config.UnmarshalRawConfig(bytes)
if err != nil {
log.Errorln("unmarshalRawConfig error %v", err)
return config.DefaultRawConfig()
}
for _, mapping := range prof.ProxyProvider {
value, exist := mapping["path"].(string)
if !exist {
continue
}
mapping["path"] = filepath.Join(getProfileProvidersPath(id), value)
}
for _, mapping := range prof.RuleProvider {
value, exist := mapping["path"].(string)
if !exist {
continue
}
mapping["path"] = filepath.Join(getProfileProvidersPath(id), value)
}
return prof
}
func getExternalProvidersRaw() map[string]cp.Provider {
eps := make(map[string]cp.Provider)
for n, p := range tunnel.Providers() {
@@ -166,144 +104,15 @@ func sideUpdateExternalProvider(p cp.Provider, bytes []byte) error {
}
}
func decorationConfig(profileId string, cfg config.RawConfig) *config.RawConfig {
prof := getRawConfigWithId(profileId)
overwriteConfig(prof, cfg)
return prof
}
func attachHosts(hosts, patchHosts map[string]any) {
for k, v := range patchHosts {
if str, ok := v.(string); ok {
hosts[k] = splitByMultipleSeparators(str)
}
}
}
func updatePatchDns(dns config.RawDNS) {
for pair := dns.NameServerPolicy.Oldest(); pair != nil; pair = pair.Next() {
if str, ok := pair.Value.(string); ok {
dns.NameServerPolicy.Set(pair.Key, splitByMultipleSeparators(str))
}
}
}
func trimArr(arr []string) (r []string) {
for _, e := range arr {
r = append(r, strings.Trim(e, " "))
}
return
}
func overrideRules(rules, patchRules []string) []string {
target := ""
for _, line := range rules {
rule := trimArr(strings.Split(line, ","))
if len(rule) != 2 {
continue
}
if strings.EqualFold(rule[0], "MATCH") {
target = rule[1]
break
}
}
if target == "" {
return rules
}
rulesExt := lo.Map(ips, func(ip string, _ int) string {
return fmt.Sprintf("DOMAIN,%s,%s", ip, target)
})
return append(append(rulesExt, patchRules...), rules...)
}
func overwriteConfig(targetConfig *config.RawConfig, patchConfig config.RawConfig) {
targetConfig.ExternalController = patchConfig.ExternalController
targetConfig.ExternalUI = ""
targetConfig.Interface = ""
targetConfig.ExternalUIURL = ""
targetConfig.TCPConcurrent = patchConfig.TCPConcurrent
targetConfig.UnifiedDelay = patchConfig.UnifiedDelay
targetConfig.IPv6 = patchConfig.IPv6
targetConfig.LogLevel = patchConfig.LogLevel
targetConfig.Port = 0
targetConfig.SocksPort = 0
targetConfig.KeepAliveInterval = patchConfig.KeepAliveInterval
targetConfig.MixedPort = patchConfig.MixedPort
targetConfig.FindProcessMode = patchConfig.FindProcessMode
targetConfig.AllowLan = patchConfig.AllowLan
targetConfig.Mode = patchConfig.Mode
targetConfig.Tun.Enable = patchConfig.Tun.Enable
targetConfig.Tun.Device = patchConfig.Tun.Device
targetConfig.Tun.DNSHijack = patchConfig.Tun.DNSHijack
targetConfig.Tun.Stack = patchConfig.Tun.Stack
targetConfig.Tun.RouteAddress = patchConfig.Tun.RouteAddress
targetConfig.Tun.AutoRoute = patchConfig.Tun.AutoRoute
targetConfig.GeodataLoader = patchConfig.GeodataLoader
targetConfig.Profile.StoreSelected = false
targetConfig.GeoXUrl = patchConfig.GeoXUrl
targetConfig.GlobalUA = patchConfig.GlobalUA
if configParams.TestURL != nil {
constant.DefaultTestURL = *configParams.TestURL
}
for idx := range targetConfig.ProxyGroup {
targetConfig.ProxyGroup[idx]["url"] = ""
}
attachHosts(targetConfig.Hosts, patchConfig.Hosts)
if configParams.OverrideDns {
updatePatchDns(patchConfig.DNS)
targetConfig.DNS = patchConfig.DNS
} else {
if targetConfig.DNS.Enable == false {
targetConfig.DNS.Enable = true
}
}
if configParams.OverrideRule {
targetConfig.Rule = overrideRules(patchConfig.Rule, []string{})
} else {
targetConfig.Rule = overrideRules(targetConfig.Rule, patchConfig.Rule)
}
}
func patchConfig() {
log.Infoln("[Apply] patch")
general := currentConfig.General
controller := currentConfig.Controller
tls := currentConfig.TLS
tunnel.SetSniffing(general.Sniffing)
tunnel.SetFindProcessMode(general.FindProcessMode)
dialer.SetTcpConcurrent(general.TCPConcurrent)
dialer.DefaultInterface.Store(general.Interface)
adapter.UnifiedDelay.Store(general.UnifiedDelay)
tunnel.SetMode(general.Mode)
log.SetLevel(general.LogLevel)
resolver.DisableIPv6 = !general.IPv6
route.ReCreateServer(&route.Config{
Addr: controller.ExternalController,
TLSAddr: controller.ExternalControllerTLS,
UnixAddr: controller.ExternalControllerUnix,
PipeAddr: controller.ExternalControllerPipe,
Secret: controller.Secret,
Certificate: tls.Certificate,
PrivateKey: tls.PrivateKey,
DohServer: controller.ExternalDohServer,
IsDebug: false,
Cors: route.Cors{
AllowOrigins: controller.Cors.AllowOrigins,
AllowPrivateNetwork: controller.Cors.AllowPrivateNetwork,
},
})
}
func updateListeners(force bool) {
func updateListeners() {
if !isRunning {
return
}
general := currentConfig.General
listeners := currentConfig.Listeners
if force == true {
stopListeners()
if currentConfig == nil {
return
}
listeners := currentConfig.Listeners
general := currentConfig.General
listener.PatchInboundListeners(listeners, tunnel.Tunnel, true)
listener.SetAllowLan(general.AllowLan)
inbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)
@@ -327,11 +136,7 @@ func stopListeners() {
listener.StopListener()
}
func patchSelectGroup() {
mapping := configParams.SelectedMap
if mapping == nil {
return
}
func patchSelectGroup(mapping map[string]string) {
for name, proxy := range tunnel.ProxiesWithProviders() {
outbound, ok := proxy.(*adapter.Proxy)
if !ok {
@@ -352,21 +157,102 @@ func patchSelectGroup() {
}
}
func applyConfig(rawConfig *config.RawConfig) error {
func defaultSetupParams() *SetupParams {
return &SetupParams{
Config: config.DefaultRawConfig(),
TestURL: "https://www.gstatic.com/generate_204",
SelectedMap: map[string]string{},
}
}
func readFile(path string) ([]byte, error) {
if _, err := os.Stat(path); os.IsNotExist(err) {
return nil, err
}
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
return data, err
}
func updateConfig(params *UpdateParams) {
runLock.Lock()
defer runLock.Unlock()
general := currentConfig.General
if params.MixedPort != nil {
general.MixedPort = *params.MixedPort
}
if params.Sniffing != nil {
general.Sniffing = *params.Sniffing
tunnel.SetSniffing(general.Sniffing)
}
if params.FindProcessMode != nil {
general.FindProcessMode = *params.FindProcessMode
tunnel.SetFindProcessMode(general.FindProcessMode)
}
if params.TCPConcurrent != nil {
general.TCPConcurrent = *params.TCPConcurrent
dialer.SetTcpConcurrent(general.TCPConcurrent)
}
if params.Interface != nil {
general.Interface = *params.Interface
dialer.DefaultInterface.Store(general.Interface)
}
if params.UnifiedDelay != nil {
general.UnifiedDelay = *params.UnifiedDelay
adapter.UnifiedDelay.Store(general.UnifiedDelay)
}
if params.Mode != nil {
general.Mode = *params.Mode
tunnel.SetMode(general.Mode)
}
if params.LogLevel != nil {
general.LogLevel = *params.LogLevel
log.SetLevel(general.LogLevel)
}
if params.IPv6 != nil {
general.IPv6 = *params.IPv6
resolver.DisableIPv6 = !general.IPv6
}
if params.ExternalController != nil {
currentConfig.Controller.ExternalController = *params.ExternalController
route.ReCreateServer(&route.Config{
Addr: currentConfig.Controller.ExternalController,
})
}
if params.Tun != nil {
general.Tun.Enable = params.Tun.Enable
general.Tun.AutoRoute = *params.Tun.AutoRoute
general.Tun.Device = *params.Tun.Device
general.Tun.RouteAddress = *params.Tun.RouteAddress
general.Tun.DNSHijack = *params.Tun.DNSHijack
general.Tun.Stack = *params.Tun.Stack
}
updateListeners()
}
func setupConfig(params *SetupParams) error {
runLock.Lock()
defer runLock.Unlock()
var err error
currentConfig, err = config.ParseRawConfig(rawConfig)
constant.DefaultTestURL = params.TestURL
currentConfig, err = config.ParseRawConfig(params.Config)
if err != nil {
currentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig())
}
if configParams.IsPatch {
patchConfig()
updateListeners(false)
} else {
hub.ApplyConfig(currentConfig)
patchSelectGroup()
updateListeners(true)
}
hub.ApplyConfig(currentConfig)
patchSelectGroup(params.SelectedMap)
updateListeners()
return err
}
func UnmarshalJson(data []byte, v any) error {
decoder := json.NewDecoder(b.NewReader(data))
decoder.UseNumber()
err := decoder.Decode(v)
return err
}

View File

@@ -3,7 +3,12 @@ package main
import (
"encoding/json"
"github.com/metacubex/mihomo/adapter/provider"
P "github.com/metacubex/mihomo/component/process"
"github.com/metacubex/mihomo/config"
"github.com/metacubex/mihomo/constant"
"github.com/metacubex/mihomo/log"
"github.com/metacubex/mihomo/tunnel"
"net/netip"
"time"
)
@@ -12,19 +17,34 @@ type InitParams struct {
Version int `json:"version"`
}
type ConfigExtendedParams struct {
IsPatch bool `json:"is-patch"`
IsCompatible bool `json:"is-compatible"`
SelectedMap map[string]string `json:"selected-map"`
TestURL *string `json:"test-url"`
OverrideDns bool `json:"override-dns"`
OverrideRule bool `json:"override-rule"`
type SetupParams struct {
Config *config.RawConfig `json:"config"`
SelectedMap map[string]string `json:"selected-map"`
TestURL string `json:"test-url"`
}
type GenerateConfigParams struct {
ProfileId string `json:"profile-id"`
Config config.RawConfig `json:"config" `
Params ConfigExtendedParams `json:"params"`
type UpdateParams struct {
Tun *tunSchema `json:"tun"`
AllowLan *bool `json:"allow-lan"`
MixedPort *int `json:"mixed-port"`
FindProcessMode *P.FindProcessMode `json:"find-process-mode"`
Mode *tunnel.TunnelMode `json:"mode"`
LogLevel *log.LogLevel `json:"log-level"`
IPv6 *bool `json:"ipv6"`
Sniffing *bool `json:"sniffing"`
TCPConcurrent *bool `json:"tcp-concurrent"`
ExternalController *string `json:"external-controller"`
Interface *string `json:"interface-name"`
UnifiedDelay *bool `json:"unified-delay"`
}
type tunSchema struct {
Enable bool `yaml:"enable" json:"enable"`
Device *string `yaml:"device" json:"device"`
Stack *constant.TUNStack `yaml:"stack" json:"stack"`
DNSHijack *[]string `yaml:"dns-hijack" json:"dns-hijack"`
AutoRoute *bool `yaml:"auto-route" json:"auto-route"`
RouteAddress *[]netip.Prefix `yaml:"route-address" json:"route-address,omitempty"`
}
type ChangeProxyParams struct {
@@ -82,8 +102,9 @@ const (
getAndroidVpnOptionsMethod Method = "getAndroidVpnOptions"
getRunTimeMethod Method = "getRunTime"
getCurrentProfileNameMethod Method = "getCurrentProfileName"
getProfileMethod Method = "getProfile"
crashMethod Method = "crash"
setupConfigMethod Method = "setupConfig"
getConfigMethod Method = "getConfig"
)
type Method string

View File

@@ -29,10 +29,8 @@ import (
var (
isInit = false
configParams = ConfigExtendedParams{}
externalProviders = map[string]cp.Provider{}
logSubscriber observable.Subscription[log.Event]
currentConfig *config.Config
)
func handleInitClash(paramsString string) bool {
@@ -53,7 +51,7 @@ func handleStartListener() bool {
runLock.Lock()
defer runLock.Unlock()
isRunning = true
updateListeners(true)
updateListeners()
resolver.ResetConnection()
return true
}
@@ -93,30 +91,10 @@ func handleValidateConfig(bytes []byte) string {
return ""
}
func handleUpdateConfig(bytes []byte) string {
var params = &GenerateConfigParams{}
err := json.Unmarshal(bytes, params)
if err != nil {
return err.Error()
}
configParams = params.Params
prof := decorationConfig(params.ProfileId, params.Config)
err = applyConfig(prof)
if err != nil {
return err.Error()
}
return ""
}
func handleGetProxies() string {
func handleGetProxies() map[string]constant.Proxy {
runLock.Lock()
defer runLock.Unlock()
data, err := json.Marshal(tunnel.ProxiesWithProviders())
if err != nil {
return ""
}
return string(data)
return tunnel.ProxiesWithProviders()
}
func handleChangeProxy(data string, fn func(string string)) {
@@ -186,21 +164,12 @@ func handleGetTotalTraffic() string {
return string(data)
}
func handleGetProfile(profileId string) string {
prof := getRawConfigWithId(profileId)
data, err := json.Marshal(prof)
if err != nil {
return ""
}
return string(data)
}
func handleResetTraffic() {
statistic.DefaultManager.ResetStatistic()
}
func handleAsyncTestDelay(paramsString string, fn func(string)) {
b.Go(paramsString, func() (bool, error) {
mBatch.Go(paramsString, func() (bool, error) {
var params = &TestDelayParams{}
err := json.Unmarshal([]byte(paramsString), params)
if err != nil {
@@ -455,10 +424,47 @@ func handleSetState(params string) {
_ = json.Unmarshal([]byte(params), state.CurrentState)
}
func handleGetConfig(path string) (*config.RawConfig, error) {
bytes, err := readFile(path)
if err != nil {
return nil, err
}
prof, err := config.UnmarshalRawConfig(bytes)
if err != nil {
return nil, err
}
return prof, nil
}
func handleCrash() {
panic("handle invoke crash")
}
func handleUpdateConfig(bytes []byte) string {
var params = &UpdateParams{}
err := json.Unmarshal(bytes, params)
if err != nil {
return err.Error()
}
updateConfig(params)
return ""
}
func handleSetupConfig(bytes []byte) string {
var params = defaultSetupParams()
err := UnmarshalJson(bytes, params)
if err != nil {
log.Errorln("unmarshalRawConfig error %v", err)
_ = setupConfig(defaultSetupParams())
return err.Error()
}
err = setupConfig(params)
if err != nil {
return err.Error()
}
return ""
}
func init() {
adapter.UrlTestHook = func(url string, name string, delay uint16) {
delayData := &Delay{

View File

@@ -39,6 +39,14 @@ func freeCString(s *C.char) {
C.free(unsafe.Pointer(s))
}
func (result ActionResult) send() {
data, err := result.Json()
if err != nil {
return
}
bridge.SendToPort(result.Port, string(data))
}
//export invokeAction
func invokeAction(paramsChar *C.char, port C.longlong) {
params := C.GoString(paramsChar)
@@ -49,22 +57,38 @@ func invokeAction(paramsChar *C.char, port C.longlong) {
bridge.SendToPort(i, err.Error())
return
}
go handleAction(action, func(data interface{}) {
bridge.SendToPort(i, string(action.getResult(data)))
})
result := ActionResult{
Id: action.Id,
Method: action.Method,
Port: i,
}
go handleAction(action, result)
}
func sendMessage(message Message) {
if messagePort == -1 {
return
}
res, err := message.Json()
if err != nil {
return
}
bridge.SendToPort(messagePort, string(Action{
result := ActionResult{
Method: messageMethod,
}.getResult(res)))
Port: messagePort,
Data: message,
}
result.send()
}
//export getConfig
func getConfig(s *C.char) *C.char {
path := C.GoString(s)
config, err := handleGetConfig(path)
if err != nil {
return C.CString("")
}
marshal, err := json.Marshal(config)
if err != nil {
return C.CString("")
}
return C.CString(string(marshal))
}
//export startListener

View File

@@ -188,21 +188,21 @@ func handleGetCurrentProfileName() string {
return state.CurrentState.CurrentProfileName
}
func nextHandle(action *Action, result func(data interface{})) bool {
func nextHandle(action *Action, result ActionResult) bool {
switch action.Method {
case getAndroidVpnOptionsMethod:
result(handleGetAndroidVpnOptions())
result.success(handleGetAndroidVpnOptions())
return true
case updateDnsMethod:
data := action.Data.(string)
handleUpdateDns(data)
result(true)
result.success(true)
return true
case getRunTimeMethod:
result(handleGetRunTime())
result.success(handleGetRunTime())
return true
case getCurrentProfileNameMethod:
result(handleGetCurrentProfileName())
result.success(handleGetCurrentProfileName())
return true
}
return false
@@ -220,7 +220,7 @@ func quickStart(initParamsChar *C.char, paramsChar *C.char, stateParamsChar *C.c
bridge.SendToPort(i, "init error")
}
handleSetState(stateParams)
bridge.SendToPort(i, handleUpdateConfig(bytes))
bridge.SendToPort(i, handleSetupConfig(bytes))
}()
}

View File

@@ -12,14 +12,20 @@ import (
var conn net.Conn
func sendMessage(message Message) {
res, err := message.Json()
func (result ActionResult) send() {
data, err := result.Json()
if err != nil {
return
}
send(Action{
send(data)
}
func sendMessage(message Message) {
result := ActionResult{
Method: messageMethod,
}.getResult(res))
Data: message,
}
result.send()
}
func send(data []byte) {
@@ -61,12 +67,15 @@ func startServer(arg string) {
return
}
go handleAction(action, func(data interface{}) {
send(action.getResult(data))
})
result := ActionResult{
Id: action.Id,
Method: action.Method,
}
go handleAction(action, result)
}
}
func nextHandle(action *Action, result func(data interface{})) bool {
func nextHandle(action *Action, result ActionResult) bool {
return false
}

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/l10n/l10n.dart';
@@ -100,8 +101,10 @@ class ApplicationState extends ConsumerState<Application> {
return AppStateManager(
child: ClashManager(
child: ConnectivityManager(
onConnectivityChanged: () async {
await clashCore.resetConnections();
onConnectivityChanged: (results) async {
if (!results.contains(ConnectivityResult.vpn)) {
await clashCore.closeConnections();
}
globalState.appController.updateLocalIp();
globalState.appController.addCheckIpNumDebounce();
},

View File

@@ -89,39 +89,39 @@ class ClashCore {
return clashInterface.validateConfig(data);
}
Future<String> updateConfig(UpdateConfigParams updateConfigParams) async {
return await clashInterface.updateConfig(updateConfigParams);
Future<String> updateConfig(UpdateParams updateParams) async {
return await clashInterface.updateConfig(updateParams);
}
Future<String> setupConfig(SetupParams setupParams) async {
return await clashInterface.setupConfig(setupParams);
}
Future<List<Group>> getProxiesGroups() async {
final proxiesRawString = await clashInterface.getProxies();
return Isolate.run<List<Group>>(() {
if (proxiesRawString.isEmpty) return [];
final proxies = (json.decode(proxiesRawString) ?? {}) as Map;
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]["all"] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']);
})
];
final groupsRaw = groupNames.map((groupName) {
final group = proxies[groupName];
group["all"] = ((group["all"] ?? []) as List)
.map(
(name) => proxies[name],
)
.where((proxy) => proxy != null)
.toList();
return group;
}).toList();
return groupsRaw
final proxies = await clashInterface.getProxies();
if (proxies.isEmpty) return [];
final groupNames = [
UsedProxy.GLOBAL.name,
...(proxies[UsedProxy.GLOBAL.name]["all"] as List).where((e) {
final proxy = proxies[e] ?? {};
return GroupTypeExtension.valueList.contains(proxy['type']);
})
];
final groupsRaw = groupNames.map((groupName) {
final group = proxies[groupName];
group["all"] = ((group["all"] ?? []) as List)
.map(
(e) => Group.fromJson(e),
(name) => proxies[name],
)
.where((proxy) => proxy != null)
.toList();
});
return group;
}).toList();
return groupsRaw
.map(
(e) => Group.fromJson(e),
)
.toList();
}
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {
@@ -210,6 +210,16 @@ class ClashCore {
return Delay.fromJson(json.decode(data));
}
Future<Map<String, dynamic>> getConfig(String id) async {
final profilePath = await appPath.getProfilePath(id);
final res = await clashInterface.getConfig(profilePath);
if (res.isSuccess) {
return res.data as Map<String, dynamic>;
} else {
throw res.message;
}
}
Future<Traffic> getTraffic() async {
final trafficString = await clashInterface.getTraffic();
if (trafficString.isEmpty) {
@@ -245,14 +255,6 @@ class ClashCore {
return int.parse(value);
}
Future<ClashConfigSnippet?> getProfile(String id) async {
final res = await clashInterface.getProfile(id);
if (res.isEmpty) {
return null;
}
return Isolate.run(() => ClashConfigSnippet.fromJson(json.decode(res)));
}
resetTraffic() {
clashInterface.resetTraffic();
}

View File

@@ -154,18 +154,18 @@ class ClashFFI {
late final _setrlimit =
_setrlimitPtr.asFunction<int Function(int, ffi.Pointer<rlimit>)>();
int wait1(
int wait$1(
ffi.Pointer<ffi.Int> arg0,
) {
return _wait1(
return _wait$1(
arg0,
);
}
late final _wait1Ptr =
late final _wait$1Ptr =
_lookup<ffi.NativeFunction<pid_t Function(ffi.Pointer<ffi.Int>)>>('wait');
late final _wait1 =
_wait1Ptr.asFunction<int Function(ffi.Pointer<ffi.Int>)>();
late final _wait$1 =
_wait$1Ptr.asFunction<int Function(ffi.Pointer<ffi.Int>)>();
int waitpid(
int arg0,
@@ -2518,6 +2518,20 @@ class ClashFFI {
late final _invokeAction =
_invokeActionPtr.asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();
ffi.Pointer<ffi.Char> getConfig(
ffi.Pointer<ffi.Char> s,
) {
return _getConfig(
s,
);
}
late final _getConfigPtr = _lookup<
ffi.NativeFunction<
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>>('getConfig');
late final _getConfig = _getConfigPtr
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
void startListener() {
return _startListener();
}
@@ -2639,6 +2653,29 @@ class ClashFFI {
_updateDnsPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
}
typedef __int8_t = ffi.SignedChar;
typedef Dart__int8_t = int;
typedef __uint8_t = ffi.UnsignedChar;
typedef Dart__uint8_t = int;
typedef __int16_t = ffi.Short;
typedef Dart__int16_t = int;
typedef __uint16_t = ffi.UnsignedShort;
typedef Dart__uint16_t = int;
typedef __int32_t = ffi.Int;
typedef Dart__int32_t = int;
typedef __uint32_t = ffi.UnsignedInt;
typedef Dart__uint32_t = int;
typedef __int64_t = ffi.LongLong;
typedef Dart__int64_t = int;
typedef __uint64_t = ffi.UnsignedLongLong;
typedef Dart__uint64_t = int;
typedef __darwin_intptr_t = ffi.Long;
typedef Dart__darwin_intptr_t = int;
typedef __darwin_natural_t = ffi.UnsignedInt;
typedef Dart__darwin_natural_t = int;
typedef __darwin_ct_rune_t = ffi.Int;
typedef Dart__darwin_ct_rune_t = int;
final class __mbstate_t extends ffi.Union {
@ffi.Array.multi([128])
external ffi.Array<ffi.Char> __mbstate8;
@@ -2647,6 +2684,46 @@ final class __mbstate_t extends ffi.Union {
external int _mbstateL;
}
typedef __darwin_mbstate_t = __mbstate_t;
typedef __darwin_ptrdiff_t = ffi.Long;
typedef Dart__darwin_ptrdiff_t = int;
typedef __darwin_size_t = ffi.UnsignedLong;
typedef Dart__darwin_size_t = int;
typedef __builtin_va_list = ffi.Pointer<ffi.Char>;
typedef __darwin_va_list = __builtin_va_list;
typedef __darwin_wchar_t = ffi.Int;
typedef Dart__darwin_wchar_t = int;
typedef __darwin_rune_t = __darwin_wchar_t;
typedef __darwin_wint_t = ffi.Int;
typedef Dart__darwin_wint_t = int;
typedef __darwin_clock_t = ffi.UnsignedLong;
typedef Dart__darwin_clock_t = int;
typedef __darwin_socklen_t = __uint32_t;
typedef __darwin_ssize_t = ffi.Long;
typedef Dart__darwin_ssize_t = int;
typedef __darwin_time_t = ffi.Long;
typedef Dart__darwin_time_t = int;
typedef __darwin_blkcnt_t = __int64_t;
typedef __darwin_blksize_t = __int32_t;
typedef __darwin_dev_t = __int32_t;
typedef __darwin_fsblkcnt_t = ffi.UnsignedInt;
typedef Dart__darwin_fsblkcnt_t = int;
typedef __darwin_fsfilcnt_t = ffi.UnsignedInt;
typedef Dart__darwin_fsfilcnt_t = int;
typedef __darwin_gid_t = __uint32_t;
typedef __darwin_id_t = __uint32_t;
typedef __darwin_ino64_t = __uint64_t;
typedef __darwin_ino_t = __darwin_ino64_t;
typedef __darwin_mach_port_name_t = __darwin_natural_t;
typedef __darwin_mach_port_t = __darwin_mach_port_name_t;
typedef __darwin_mode_t = __uint16_t;
typedef __darwin_off_t = __int64_t;
typedef __darwin_pid_t = __int32_t;
typedef __darwin_sigset_t = __uint32_t;
typedef __darwin_suseconds_t = __int32_t;
typedef __darwin_uid_t = __uint32_t;
typedef __darwin_useconds_t = __uint32_t;
final class __darwin_pthread_handler_rec extends ffi.Struct {
external ffi
.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
@@ -2731,6 +2808,48 @@ final class _opaque_pthread_t extends ffi.Struct {
external ffi.Array<ffi.Char> __opaque;
}
typedef __darwin_pthread_attr_t = _opaque_pthread_attr_t;
typedef __darwin_pthread_cond_t = _opaque_pthread_cond_t;
typedef __darwin_pthread_condattr_t = _opaque_pthread_condattr_t;
typedef __darwin_pthread_key_t = ffi.UnsignedLong;
typedef Dart__darwin_pthread_key_t = int;
typedef __darwin_pthread_mutex_t = _opaque_pthread_mutex_t;
typedef __darwin_pthread_mutexattr_t = _opaque_pthread_mutexattr_t;
typedef __darwin_pthread_once_t = _opaque_pthread_once_t;
typedef __darwin_pthread_rwlock_t = _opaque_pthread_rwlock_t;
typedef __darwin_pthread_rwlockattr_t = _opaque_pthread_rwlockattr_t;
typedef __darwin_pthread_t = ffi.Pointer<_opaque_pthread_t>;
typedef __darwin_nl_item = ffi.Int;
typedef Dart__darwin_nl_item = int;
typedef __darwin_wctrans_t = ffi.Int;
typedef Dart__darwin_wctrans_t = int;
typedef __darwin_wctype_t = __uint32_t;
typedef u_int8_t = ffi.UnsignedChar;
typedef Dartu_int8_t = int;
typedef u_int16_t = ffi.UnsignedShort;
typedef Dartu_int16_t = int;
typedef u_int32_t = ffi.UnsignedInt;
typedef Dartu_int32_t = int;
typedef u_int64_t = ffi.UnsignedLongLong;
typedef Dartu_int64_t = int;
typedef register_t = ffi.Int64;
typedef Dartregister_t = int;
typedef user_addr_t = u_int64_t;
typedef user_size_t = u_int64_t;
typedef user_ssize_t = ffi.Int64;
typedef Dartuser_ssize_t = int;
typedef user_long_t = ffi.Int64;
typedef Dartuser_long_t = int;
typedef user_ulong_t = u_int64_t;
typedef user_time_t = ffi.Int64;
typedef Dartuser_time_t = int;
typedef user_off_t = ffi.Int64;
typedef Dartuser_off_t = int;
typedef syscall_arg_t = u_int64_t;
typedef ptrdiff_t = __darwin_ptrdiff_t;
typedef rsize_t = __darwin_size_t;
typedef wint_t = __darwin_wint_t;
final class _GoString_ extends ffi.Struct {
external ffi.Pointer<ffi.Char> p;
@@ -2738,10 +2857,6 @@ final class _GoString_ extends ffi.Struct {
external int n;
}
typedef ptrdiff_t = __darwin_ptrdiff_t;
typedef __darwin_ptrdiff_t = ffi.Long;
typedef Dart__darwin_ptrdiff_t = int;
enum idtype_t {
P_ALL(0),
P_PID(1),
@@ -2754,10 +2869,15 @@ enum idtype_t {
0 => P_ALL,
1 => P_PID,
2 => P_PGID,
_ => throw ArgumentError("Unknown value for idtype_t: $value"),
_ => throw ArgumentError('Unknown value for idtype_t: $value'),
};
}
typedef pid_t = __darwin_pid_t;
typedef id_t = __darwin_id_t;
typedef sig_atomic_t = ffi.Int;
typedef Dartsig_atomic_t = int;
final class __darwin_arm_exception_state extends ffi.Struct {
@__uint32_t()
external int __exception;
@@ -2769,9 +2889,6 @@ final class __darwin_arm_exception_state extends ffi.Struct {
external int __far;
}
typedef __uint32_t = ffi.UnsignedInt;
typedef Dart__uint32_t = int;
final class __darwin_arm_exception_state64 extends ffi.Struct {
@__uint64_t()
external int __far;
@@ -2783,9 +2900,6 @@ final class __darwin_arm_exception_state64 extends ffi.Struct {
external int __exception;
}
typedef __uint64_t = ffi.UnsignedLongLong;
typedef Dart__uint64_t = int;
final class __darwin_arm_exception_state64_v2 extends ffi.Struct {
@__uint64_t()
external int __far;
@@ -2914,6 +3028,9 @@ final class __darwin_mcontext32 extends ffi.Struct {
final class __darwin_mcontext64 extends ffi.Opaque {}
typedef mcontext_t = ffi.Pointer<__darwin_mcontext64>;
typedef pthread_attr_t = __darwin_pthread_attr_t;
final class __darwin_sigaltstack extends ffi.Struct {
external ffi.Pointer<ffi.Void> ss_sp;
@@ -2924,8 +3041,7 @@ final class __darwin_sigaltstack extends ffi.Struct {
external int ss_flags;
}
typedef __darwin_size_t = ffi.UnsignedLong;
typedef Dart__darwin_size_t = int;
typedef stack_t = __darwin_sigaltstack;
final class __darwin_ucontext extends ffi.Struct {
@ffi.Int()
@@ -2944,7 +3060,9 @@ final class __darwin_ucontext extends ffi.Struct {
external ffi.Pointer<__darwin_mcontext64> uc_mcontext;
}
typedef __darwin_sigset_t = __uint32_t;
typedef ucontext_t = __darwin_ucontext;
typedef sigset_t = __darwin_sigset_t;
typedef uid_t = __darwin_uid_t;
final class sigval extends ffi.Union {
@ffi.Int()
@@ -2968,9 +3086,6 @@ final class sigevent extends ffi.Struct {
external ffi.Pointer<pthread_attr_t> sigev_notify_attributes;
}
typedef pthread_attr_t = __darwin_pthread_attr_t;
typedef __darwin_pthread_attr_t = _opaque_pthread_attr_t;
final class __siginfo extends ffi.Struct {
@ffi.Int()
external int si_signo;
@@ -3001,12 +3116,7 @@ final class __siginfo extends ffi.Struct {
external ffi.Array<ffi.UnsignedLong> __pad;
}
typedef pid_t = __darwin_pid_t;
typedef __darwin_pid_t = __int32_t;
typedef __int32_t = ffi.Int;
typedef Dart__int32_t = int;
typedef uid_t = __darwin_uid_t;
typedef __darwin_uid_t = __uint32_t;
typedef siginfo_t = __siginfo;
final class __sigaction_u extends ffi.Union {
external ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>
@@ -3020,7 +3130,7 @@ final class __sigaction_u extends ffi.Union {
}
final class __sigaction extends ffi.Struct {
external __sigaction_u __sigaction_u1;
external __sigaction_u __sigaction_u$1;
external ffi.Pointer<
ffi.NativeFunction<
@@ -3034,11 +3144,8 @@ final class __sigaction extends ffi.Struct {
external int sa_flags;
}
typedef siginfo_t = __siginfo;
typedef sigset_t = __darwin_sigset_t;
final class sigaction extends ffi.Struct {
external __sigaction_u __sigaction_u1;
external __sigaction_u __sigaction_u$1;
@sigset_t()
external int sa_mask;
@@ -3047,6 +3154,10 @@ final class sigaction extends ffi.Struct {
external int sa_flags;
}
typedef sig_tFunction = ffi.Void Function(ffi.Int);
typedef Dartsig_tFunction = void Function(int);
typedef sig_t = ffi.Pointer<ffi.NativeFunction<sig_tFunction>>;
final class sigvec extends ffi.Struct {
external ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>
sv_handler;
@@ -3065,6 +3176,43 @@ final class sigstack extends ffi.Struct {
external int ss_onstack;
}
typedef int_least8_t = ffi.Int8;
typedef Dartint_least8_t = int;
typedef int_least16_t = ffi.Int16;
typedef Dartint_least16_t = int;
typedef int_least32_t = ffi.Int32;
typedef Dartint_least32_t = int;
typedef int_least64_t = ffi.Int64;
typedef Dartint_least64_t = int;
typedef uint_least8_t = ffi.Uint8;
typedef Dartuint_least8_t = int;
typedef uint_least16_t = ffi.Uint16;
typedef Dartuint_least16_t = int;
typedef uint_least32_t = ffi.Uint32;
typedef Dartuint_least32_t = int;
typedef uint_least64_t = ffi.Uint64;
typedef Dartuint_least64_t = int;
typedef int_fast8_t = ffi.Int8;
typedef Dartint_fast8_t = int;
typedef int_fast16_t = ffi.Int16;
typedef Dartint_fast16_t = int;
typedef int_fast32_t = ffi.Int32;
typedef Dartint_fast32_t = int;
typedef int_fast64_t = ffi.Int64;
typedef Dartint_fast64_t = int;
typedef uint_fast8_t = ffi.Uint8;
typedef Dartuint_fast8_t = int;
typedef uint_fast16_t = ffi.Uint16;
typedef Dartuint_fast16_t = int;
typedef uint_fast32_t = ffi.Uint32;
typedef Dartuint_fast32_t = int;
typedef uint_fast64_t = ffi.Uint64;
typedef Dartuint_fast64_t = int;
typedef intmax_t = ffi.Long;
typedef Dartintmax_t = int;
typedef uintmax_t = ffi.UnsignedLong;
typedef Dartuintmax_t = int;
final class timeval extends ffi.Struct {
@__darwin_time_t()
external int tv_sec;
@@ -3073,9 +3221,7 @@ final class timeval extends ffi.Struct {
external int tv_usec;
}
typedef __darwin_time_t = ffi.Long;
typedef Dart__darwin_time_t = int;
typedef __darwin_suseconds_t = __int32_t;
typedef rlim_t = __uint64_t;
final class rusage extends ffi.Struct {
external timeval ru_utime;
@@ -3125,6 +3271,8 @@ final class rusage extends ffi.Struct {
external int ru_nivcsw;
}
typedef rusage_info_t = ffi.Pointer<ffi.Void>;
final class rusage_info_v0 extends ffi.Struct {
@ffi.Array.multi([16])
external ffi.Array<ffi.Uint8> ri_uuid;
@@ -3730,6 +3878,8 @@ final class rusage_info_v6 extends ffi.Struct {
external ffi.Array<ffi.Uint64> ri_reserved;
}
typedef rusage_info_current = rusage_info_v6;
final class rlimit extends ffi.Struct {
@rlim_t()
external int rlim_cur;
@@ -3738,8 +3888,6 @@ final class rlimit extends ffi.Struct {
external int rlim_max;
}
typedef rlim_t = __uint64_t;
final class proc_rlimit_control_wakeupmon extends ffi.Struct {
@ffi.Uint32()
external int wm_flags;
@@ -3748,11 +3896,11 @@ final class proc_rlimit_control_wakeupmon extends ffi.Struct {
external int wm_rate;
}
typedef id_t = __darwin_id_t;
typedef __darwin_id_t = __uint32_t;
final class wait extends ffi.Opaque {}
typedef ct_rune_t = __darwin_ct_rune_t;
typedef rune_t = __darwin_rune_t;
final class div_t extends ffi.Struct {
@ffi.Int()
external int quot;
@@ -3784,18 +3932,18 @@ final class _malloc_zone_t extends ffi.Opaque {}
typedef malloc_zone_t = _malloc_zone_t;
typedef dev_t = __darwin_dev_t;
typedef __darwin_dev_t = __int32_t;
typedef mode_t = __darwin_mode_t;
typedef __darwin_mode_t = __uint16_t;
typedef __uint16_t = ffi.UnsignedShort;
typedef Dart__uint16_t = int;
typedef protect_func = ffi.Pointer<ffi.NativeFunction<protect_funcFunction>>;
typedef release_object_funcFunction = ffi.Void Function(
ffi.Pointer<ffi.Void> obj);
typedef Dartrelease_object_funcFunction = void Function(
ffi.Pointer<ffi.Void> obj);
typedef release_object_func
= ffi.Pointer<ffi.NativeFunction<release_object_funcFunction>>;
typedef protect_funcFunction = ffi.Void Function(
ffi.Pointer<ffi.Void> tun_interface, ffi.Int fd);
typedef Dartprotect_funcFunction = void Function(
ffi.Pointer<ffi.Void> tun_interface, int fd);
typedef resolve_process_func
= ffi.Pointer<ffi.NativeFunction<resolve_process_funcFunction>>;
typedef protect_func = ffi.Pointer<ffi.NativeFunction<protect_funcFunction>>;
typedef resolve_process_funcFunction = ffi.Pointer<ffi.Char> Function(
ffi.Pointer<ffi.Void> tun_interface,
ffi.Int protocol,
@@ -3808,12 +3956,35 @@ typedef Dartresolve_process_funcFunction = ffi.Pointer<ffi.Char> Function(
ffi.Pointer<ffi.Char> source,
ffi.Pointer<ffi.Char> target,
int uid);
typedef release_object_func
= ffi.Pointer<ffi.NativeFunction<release_object_funcFunction>>;
typedef release_object_funcFunction = ffi.Void Function(
ffi.Pointer<ffi.Void> obj);
typedef Dartrelease_object_funcFunction = void Function(
ffi.Pointer<ffi.Void> obj);
typedef resolve_process_func
= ffi.Pointer<ffi.NativeFunction<resolve_process_funcFunction>>;
typedef GoInt8 = ffi.SignedChar;
typedef DartGoInt8 = int;
typedef GoUint8 = ffi.UnsignedChar;
typedef DartGoUint8 = int;
typedef GoInt16 = ffi.Short;
typedef DartGoInt16 = int;
typedef GoUint16 = ffi.UnsignedShort;
typedef DartGoUint16 = int;
typedef GoInt32 = ffi.Int;
typedef DartGoInt32 = int;
typedef GoUint32 = ffi.UnsignedInt;
typedef DartGoUint32 = int;
typedef GoInt64 = ffi.LongLong;
typedef DartGoInt64 = int;
typedef GoUint64 = ffi.UnsignedLongLong;
typedef DartGoUint64 = int;
typedef GoInt = GoInt64;
typedef GoUint = GoUint64;
typedef GoUintptr = ffi.Size;
typedef DartGoUintptr = int;
typedef GoFloat32 = ffi.Float;
typedef DartGoFloat32 = double;
typedef GoFloat64 = ffi.Double;
typedef DartGoFloat64 = double;
typedef GoString = _GoString_;
typedef GoMap = ffi.Pointer<ffi.Void>;
typedef GoChan = ffi.Pointer<ffi.Void>;
final class GoInterface extends ffi.Struct {
external ffi.Pointer<ffi.Void> t;
@@ -3831,12 +4002,6 @@ final class GoSlice extends ffi.Struct {
external int cap;
}
typedef GoInt = GoInt64;
typedef GoInt64 = ffi.LongLong;
typedef DartGoInt64 = int;
typedef GoUint8 = ffi.UnsignedChar;
typedef DartGoUint8 = int;
const int __has_safe_buffers = 1;
const int __DARWIN_ONLY_64_BIT_INO_T = 1;

View File

@@ -1,5 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:isolate';
import 'package:fl_clash/clash/message.dart';
import 'package:fl_clash/common/common.dart';
@@ -19,11 +20,15 @@ mixin ClashInterface {
FutureOr<String> validateConfig(String data);
FutureOr<Result> getConfig(String path);
Future<String> asyncTestDelay(String url, String proxyName);
FutureOr<String> updateConfig(UpdateConfigParams updateConfigParams);
FutureOr<String> updateConfig(UpdateParams updateParams);
FutureOr<String> getProxies();
FutureOr<String> setupConfig(SetupParams setupParams);
FutureOr<Map> getProxies();
FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams);
@@ -68,18 +73,10 @@ mixin ClashInterface {
FutureOr<bool> resetConnections();
FutureOr<String> getProfile(String id);
Future<bool> setState(CoreState state);
}
mixin AndroidClashInterface {
Future<bool> setFdMap(int fd);
Future<bool> setProcessMap(ProcessMapItem item);
// Future<bool> stopTun();
Future<bool> updateDns(String value);
Future<AndroidVpnOptions?> getAndroidVpnOptions();
@@ -92,55 +89,23 @@ mixin AndroidClashInterface {
abstract class ClashHandlerInterface with ClashInterface {
Map<String, Completer> callbackCompleterMap = {};
Future<bool> nextHandleResult(ActionResult result, Completer? completer) =>
Future.value(false);
handleResult(ActionResult result) async {
final completer = callbackCompleterMap[result.id];
try {
switch (result.method) {
case ActionMethod.initClash:
case ActionMethod.shutdown:
case ActionMethod.getIsInit:
case ActionMethod.startListener:
case ActionMethod.resetTraffic:
case ActionMethod.closeConnections:
case ActionMethod.closeConnection:
case ActionMethod.stopListener:
case ActionMethod.setState:
case ActionMethod.crash:
completer?.complete(result.data as bool);
return;
case ActionMethod.changeProxy:
case ActionMethod.getProxies:
case ActionMethod.getTraffic:
case ActionMethod.getTotalTraffic:
case ActionMethod.asyncTestDelay:
case ActionMethod.getConnections:
case ActionMethod.getExternalProviders:
case ActionMethod.getExternalProvider:
case ActionMethod.validateConfig:
case ActionMethod.updateConfig:
case ActionMethod.updateGeoData:
case ActionMethod.updateExternalProvider:
case ActionMethod.sideLoadExternalProvider:
case ActionMethod.getCountryCode:
case ActionMethod.getMemory:
completer?.complete(result.data as String);
return;
case ActionMethod.message:
clashMessage.controller.add(result.data as String);
clashMessage.controller.add(result.data);
completer?.complete(true);
return;
case ActionMethod.getConfig:
completer?.complete(result.toResult);
return;
default:
final isHandled = await nextHandleResult(result, completer);
if (isHandled) {
return;
}
completer?.complete(result.data);
return;
}
} catch (_) {
commonPrint.log(result.id);
} catch (e) {
commonPrint.log("${result.id} error $e");
}
}
@@ -155,18 +120,21 @@ abstract class ClashHandlerInterface with ClashInterface {
dynamic data,
Duration? timeout,
FutureOr<T> Function()? onTimeout,
T? defaultValue,
}) async {
final id = "${method.name}#${utils.id}";
callbackCompleterMap[id] = Completer<T>();
dynamic defaultValue;
if (T == String) {
defaultValue = "";
}
if (T == bool) {
defaultValue = false;
dynamic mDefaultValue = defaultValue;
if (mDefaultValue == null) {
if (T == String) {
mDefaultValue = "";
} else if (T == bool) {
mDefaultValue = false;
} else if (T == Map) {
mDefaultValue = {};
}
}
sendMessage(
@@ -175,7 +143,6 @@ abstract class ClashHandlerInterface with ClashInterface {
id: id,
method: method,
data: data,
defaultValue: defaultValue,
),
),
);
@@ -187,7 +154,7 @@ abstract class ClashHandlerInterface with ClashInterface {
},
onTimeout: onTimeout ??
() {
return defaultValue;
return mDefaultValue;
},
functionName: id,
);
@@ -213,6 +180,7 @@ abstract class ClashHandlerInterface with ClashInterface {
shutdown() async {
return await invoke<bool>(
method: ActionMethod.shutdown,
timeout: Duration(seconds: 1),
);
}
@@ -239,10 +207,31 @@ abstract class ClashHandlerInterface with ClashInterface {
}
@override
Future<String> updateConfig(UpdateConfigParams updateConfigParams) async {
Future<String> updateConfig(UpdateParams updateParams) async {
return await invoke<String>(
method: ActionMethod.updateConfig,
data: json.encode(updateConfigParams),
data: json.encode(updateParams),
timeout: Duration(minutes: 2),
);
}
@override
Future<Result> getConfig(String path) async {
final res = await invoke<Result>(
method: ActionMethod.getConfig,
data: path,
timeout: Duration(minutes: 2),
defaultValue: Result.success({}),
);
return res;
}
@override
Future<String> setupConfig(SetupParams setupParams) async {
final data = await Isolate.run(() => json.encode(setupParams));
return await invoke<String>(
method: ActionMethod.setupConfig,
data: data,
timeout: Duration(minutes: 2),
);
}
@@ -255,8 +244,8 @@ abstract class ClashHandlerInterface with ClashInterface {
}
@override
Future<String> getProxies() {
return invoke<String>(
Future<Map> getProxies() {
return invoke<Map>(
method: ActionMethod.getProxies,
timeout: Duration(seconds: 5),
);
@@ -345,14 +334,6 @@ abstract class ClashHandlerInterface with ClashInterface {
);
}
@override
Future<String> getProfile(String id) {
return invoke<String>(
method: ActionMethod.getProfile,
data: id,
);
}
@override
FutureOr<String> getTotalTraffic() {
return invoke<String>(

View File

@@ -62,26 +62,6 @@ class ClashLib extends ClashHandlerInterface with AndroidClashInterface {
return _instance!;
}
@override
Future<bool> nextHandleResult(result, completer) async {
switch (result.method) {
case ActionMethod.setFdMap:
case ActionMethod.setProcessMap:
case ActionMethod.stopTun:
case ActionMethod.updateDns:
completer?.complete(result.data as bool);
return true;
case ActionMethod.getRunTime:
case ActionMethod.startTun:
case ActionMethod.getAndroidVpnOptions:
case ActionMethod.getCurrentProfileName:
completer?.complete(result.data as String);
return true;
default:
return false;
}
}
@override
destroy() async {
await service?.destroy();
@@ -106,22 +86,6 @@ class ClashLib extends ClashHandlerInterface with AndroidClashInterface {
sendPort?.send(message);
}
@override
Future<bool> setFdMap(int fd) {
return invoke<bool>(
method: ActionMethod.setFdMap,
data: json.encode(fd),
);
}
@override
Future<bool> setProcessMap(item) {
return invoke<bool>(
method: ActionMethod.setProcessMap,
data: item,
);
}
// @override
// Future<bool> stopTun() {
// return invoke<bool>(
@@ -276,9 +240,22 @@ class ClashLibHandler {
return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));
}
// Map<String, dynamic> getConfig(String path) {
// final pathChar = path.toNativeUtf8().cast<Char>();
// final configRaw = clashFFI.getConfig(pathChar);
// final configString = configRaw.cast<Utf8>().toDartString();
// if (configString.isEmpty) {
// return {};
// }
// final config = json.decode(configString);
// malloc.free(pathChar);
// clashFFI.freeCString(configRaw);
// return config;
// }
Future<String> quickStart(
InitParams initParams,
UpdateConfigParams updateConfigParams,
SetupParams setupParams,
CoreState state,
) {
final completer = Completer<String>();
@@ -289,7 +266,7 @@ class ClashLibHandler {
receiver.close();
}
});
final params = json.encode(updateConfigParams);
final params = json.encode(setupParams);
final initValue = json.encode(initParams);
final stateParams = json.encode(state);
final initParamsChar = initValue.toNativeUtf8().cast<Char>();

View File

@@ -1,20 +1,19 @@
import 'dart:async';
import 'dart:convert';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:flutter/foundation.dart';
class ClashMessage {
final controller = StreamController<String>();
final controller = StreamController<Map<String, Object?>>();
ClashMessage._() {
controller.stream.listen(
(message) {
if(message.isEmpty){
if (message.isEmpty) {
return;
}
final m = AppMessage.fromJson(json.decode(message));
final m = AppMessage.fromJson(message);
for (final AppMessageListener listener in _listeners) {
switch (m.type) {
case AppMessageType.log:

View File

@@ -1,7 +1,6 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'package:fl_clash/clash/interface.dart';
import 'package:fl_clash/common/common.dart';
@@ -51,23 +50,18 @@ class ClashService extends ClashHandlerInterface {
await _destroySocket();
socketCompleter.complete(socket);
socket
.transform(
StreamTransformer<Uint8List, String>.fromHandlers(
handleData: (Uint8List data, EventSink<String> sink) {
sink.add(utf8.decode(data, allowMalformed: true));
},
),
)
.transform(uint8ListToListIntConverter)
.transform(utf8.decoder)
.transform(LineSplitter())
.listen(
(data) {
handleResult(
ActionResult.fromJson(
json.decode(data.trim()),
),
);
},
(data) {
handleResult(
ActionResult.fromJson(
json.decode(data.trim()),
),
);
},
);
}
}, (error, stack) {
commonPrint.log(error.toString());
@@ -104,7 +98,13 @@ class ClashService extends ClashHandlerInterface {
arg,
],
);
process!.stdout.listen((_) {});
process?.stdout.listen((_) {});
process?.stderr.listen((e) {
final error = utf8.decode(e);
if (error.isNotEmpty) {
commonPrint.log(error);
}
});
isStarting = false;
}

View File

@@ -3,6 +3,7 @@ export 'app_localizations.dart';
export 'color.dart';
export 'constant.dart';
export 'context.dart';
export 'converter.dart';
export 'datetime.dart';
export 'fixed.dart';
export 'function.dart';

View File

@@ -102,3 +102,8 @@ const defaultPrimaryColors = [
defaultPrimaryColor,
0XFF665390,
];
const scriptTemplate = """
const main = (config) => {
return config;
}""";

32
lib/common/converter.dart Normal file
View File

@@ -0,0 +1,32 @@
import 'dart:convert';
import 'dart:typed_data';
class Uint8ListToListIntConverter extends Converter<Uint8List, List<int>> {
@override
List<int> convert(Uint8List input) {
return input.toList();
}
@override
Sink<Uint8List> startChunkedConversion(Sink<List<int>> sink) {
return _Uint8ListToListIntConverterSink(sink);
}
}
class _Uint8ListToListIntConverterSink implements Sink<Uint8List> {
const _Uint8ListToListIntConverterSink(this._target);
final Sink<List<int>> _target;
@override
void add(Uint8List data) {
_target.add(data.toList());
}
@override
void close() {
_target.close();
}
}
final uint8ListToListIntConverter = Uint8ListToListIntConverter();

View File

@@ -2,15 +2,11 @@ import 'package:flutter_js/flutter_js.dart';
class Javascript {
static Javascript? _instance;
late JavascriptRuntime runtime;
Javascript._internal() {
runtime = getJavascriptRuntime();
}
Javascript._internal();
Future<String> evaluate(String js) async {
final evaluate = runtime.evaluate(js);
return evaluate.stringResult;
JavascriptRuntime get runTime {
return getJavascriptRuntime();
}
factory Javascript() {

View File

@@ -1,6 +1,6 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/fragments/fragments.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/views/views.dart';
import 'package:flutter/material.dart';
class Navigation {
@@ -15,14 +15,14 @@ class Navigation {
keep: false,
icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
fragment: DashboardFragment(
view: DashboardView(
key: GlobalObjectKey(PageLabel.dashboard),
),
),
NavigationItem(
icon: const Icon(Icons.article),
label: PageLabel.proxies,
fragment: const ProxiesFragment(
view: const ProxiesView(
key: GlobalObjectKey(
PageLabel.proxies,
),
@@ -34,7 +34,7 @@ class Navigation {
const NavigationItem(
icon: Icon(Icons.folder),
label: PageLabel.profiles,
fragment: ProfilesFragment(
view: ProfilesView(
key: GlobalObjectKey(
PageLabel.profiles,
),
@@ -43,7 +43,7 @@ class Navigation {
const NavigationItem(
icon: Icon(Icons.view_timeline),
label: PageLabel.requests,
fragment: RequestsFragment(
view: RequestsView(
key: GlobalObjectKey(
PageLabel.requests,
),
@@ -54,7 +54,7 @@ class Navigation {
const NavigationItem(
icon: Icon(Icons.ballot),
label: PageLabel.connections,
fragment: ConnectionsFragment(
view: ConnectionsView(
key: GlobalObjectKey(
PageLabel.connections,
),
@@ -66,7 +66,7 @@ class Navigation {
icon: Icon(Icons.storage),
label: PageLabel.resources,
description: "resourcesDesc",
fragment: Resources(
view: ResourcesView(
key: GlobalObjectKey(
PageLabel.resources,
),
@@ -76,7 +76,7 @@ class Navigation {
NavigationItem(
icon: const Icon(Icons.adb),
label: PageLabel.logs,
fragment: const LogsFragment(
view: const LogsView(
key: GlobalObjectKey(
PageLabel.logs,
),
@@ -89,7 +89,7 @@ class Navigation {
const NavigationItem(
icon: Icon(Icons.construction),
label: PageLabel.tools,
fragment: ToolsFragment(
view: ToolsView(
key: GlobalObjectKey(
PageLabel.tools,
),

View File

@@ -2,6 +2,7 @@ import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:flutter/material.dart';
class BaseNavigator {
@@ -19,6 +20,21 @@ class BaseNavigator {
),
);
}
static Future<T?> modal<T>(BuildContext context, Widget child) async {
if (globalState.appState.viewMode != ViewMode.mobile) {
return await globalState.showCommonDialog<T>(
child: CommonModal(
child: child,
),
);
}
return await Navigator.of(context).push<T>(
CommonRoute(
builder: (context) => child,
),
);
}
}
class CommonDesktopRoute<T> extends PageRoute<T> {
@@ -218,8 +234,7 @@ class _CommonPageTransitionState extends State<CommonPageTransition> {
begin: const _CommonEdgeShadowDecoration(),
end: _CommonEdgeShadowDecoration(
<Color>[
widget.context.colorScheme.inverseSurface
.withValues(alpha: 0.02),
widget.context.colorScheme.inverseSurface.withValues(alpha: 0.02),
Colors.transparent,
],
),

View File

@@ -78,9 +78,17 @@ class AppPath {
return join(directory, "$id.yaml");
}
Future<String> getProvidersPath(String id) async {
Future<String> getProvidersPath(String id, {String filePath = ""}) async {
final directory = await profilesPath;
return join(directory, "providers", id);
final path = join(
directory,
"providers",
id,
);
if (filePath.isNotEmpty) {
return join(path, filePath);
}
return path;
}
Future<String> get tempPath async {

View File

@@ -8,6 +8,13 @@ extension StringExtension on String {
return RegExp(r'^(http|https|ftp)://').hasMatch(this);
}
dynamic get splitByMultipleSeparators {
final parts =
split(RegExp(r'[, ;]+')).where((part) => part.isNotEmpty).toList();
return parts.length > 1 ? parts : this;
}
int compareToLower(String other) {
return toLowerCase().compareTo(
other.toLowerCase(),

View File

@@ -56,7 +56,7 @@ class System {
Future<AuthorizeCode> authorizeCore() async {
if (Platform.isAndroid) {
return AuthorizeCode.none;
return AuthorizeCode.error;
}
final corePath = appPath.corePath.replaceAll(' ', '\\\\ ');
final isAdmin = await checkIsAdmin();

View File

@@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:ui';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:lpinyin/lpinyin.dart';
@@ -335,9 +337,30 @@ class Utils {
);
}
dynamic convertLoadYaml(dynamic node) {
dynamic convertYamlNode(dynamic node) {
if (node is YamlMap) {
final map = <String, dynamic>{};
YamlNode? mergeKeyNode;
for (final entry in node.nodes.entries) {
if (entry.key is YamlScalar &&
(entry.key as YamlScalar).value == '<<') {
mergeKeyNode = entry.value;
break;
}
}
if (mergeKeyNode != null) {
final mergeValue = mergeKeyNode.value;
if (mergeValue is YamlMap) {
map.addAll(convertYamlNode(mergeValue) as Map<String, dynamic>);
} else if (mergeValue is YamlList) {
for (final node in mergeValue.nodes) {
if (node.value is YamlMap) {
map.addAll(convertYamlNode(node.value) as Map<String, dynamic>);
}
}
}
}
node.nodes.forEach((key, value) {
String stringKey;
if (key is YamlScalar) {
@@ -345,13 +368,13 @@ class Utils {
} else {
stringKey = key.toString();
}
map[stringKey] = convertLoadYaml(value.value);
map[stringKey] = convertYamlNode(value.value);
});
return map;
} else if (node is YamlList) {
final list = <dynamic>[];
for (final item in node.nodes) {
list.add(convertLoadYaml(item.value));
list.add(convertYamlNode(item.value));
}
return list;
} else if (node is YamlScalar) {
@@ -359,6 +382,17 @@ class Utils {
}
return node;
}
FutureOr<T> handleWatch<T>(Function function) async {
if (kDebugMode) {
final stopwatch = Stopwatch()..start();
final res = await function();
stopwatch.stop();
commonPrint.log('耗时:${stopwatch.elapsedMilliseconds} ms');
return res;
}
return await function();
}
}
final utils = Utils();

View File

@@ -18,8 +18,8 @@ import 'package:path/path.dart';
import 'package:url_launcher/url_launcher.dart';
import 'common/common.dart';
import 'fragments/profiles/override_profile.dart';
import 'models/models.dart';
import 'views/profiles/override_profile.dart';
class AppController {
bool lastTunEnable = false;
@@ -30,10 +30,15 @@ class AppController {
AppController(this.context, WidgetRef ref) : _ref = ref;
setupClashConfigDebounce() {
debouncer.call(FunctionTag.setupClashConfig, () async {
await setupClashConfig();
});
}
updateClashConfigDebounce() {
debouncer.call(FunctionTag.updateClashConfig, () async {
final isPatch = globalState.appState.needApply ? false : true;
await updateClashConfig(isPatch);
await updateClashConfig();
});
}
@@ -71,9 +76,9 @@ class AppController {
}
restartCore() async {
commonPrint.log("restart core");
await clashService?.reStart();
await _initCore();
if (_ref.read(runTimeProvider.notifier).isStart) {
await globalState.handleStart();
}
@@ -244,55 +249,80 @@ class AppController {
);
}
Future<void> updateClashConfig([bool? isPatch]) async {
commonPrint.log("update clash patch: ${isPatch ?? false}");
Future<void> updateClashConfig() async {
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return;
await commonScaffoldState?.loadingRun(() async {
await _updateClashConfig(
isPatch,
);
await _updateClashConfig();
});
}
Future<void> _updateClashConfig([bool? isPatch]) async {
final profile = _ref.watch(currentProfileProvider);
await _ref.read(currentProfileProvider)?.checkAndUpdate();
final patchConfig = _ref.read(patchClashConfigProvider);
final appSetting = _ref.read(appSettingProvider);
bool enableTun = patchConfig.tun.enable;
Future<void> _updateClashConfig() async {
final updateParams = _ref.read(updateParamsProvider);
final res = await _requestAdmin(updateParams.tun.enable);
if (res.isError) {
return;
}
lastTunEnable = res.data == true;
final message = await clashCore.updateConfig(
updateParams.copyWith.tun(
enable: lastTunEnable,
),
);
if (message.isNotEmpty) throw message;
}
Future<Result<bool>> _requestAdmin(bool enableTun) async {
if (enableTun != lastTunEnable && lastTunEnable == false) {
final code = await system.authorizeCore();
switch (code) {
case AuthorizeCode.none:
break;
return Result.success(enableTun);
case AuthorizeCode.success:
lastTunEnable = enableTun;
await restartCore();
return;
return Result.error("");
case AuthorizeCode.error:
enableTun = false;
return Result.success(false);
}
}
if (appSetting.openLogs) {
clashCore.startLog();
} else {
clashCore.stopLog();
return Result.success(enableTun);
}
Future<void> setupClashConfig() async {
final commonScaffoldState = globalState.homeScaffoldKey.currentState;
if (commonScaffoldState?.mounted != true) return;
await commonScaffoldState?.loadingRun(() async {
await _setupClashConfig();
});
}
_setupClashConfig() async {
await _ref.read(currentProfileProvider)?.checkAndUpdate();
final patchConfig = _ref.read(patchClashConfigProvider);
final res = await _requestAdmin(patchConfig.tun.enable);
if (res.isError) {
return;
}
final res = await clashCore.updateConfig(
globalState.getUpdateConfigParams(isPatch),
lastTunEnable = res.data == true;
final realPatchConfig = patchConfig.copyWith.tun(enable: lastTunEnable);
final params = await globalState.getSetupParams(
pathConfig: realPatchConfig,
);
if (isPatch == false) {
_ref.read(needApplyProvider.notifier).value = false;
final message = await clashCore.setupConfig(params);
lastProfileModified = await _ref.read(
currentProfileProvider.select(
(state) => state?.profileLastModified,
),
);
if (message.isNotEmpty) {
throw message;
}
if (res.isNotEmpty) throw res;
lastTunEnable = enableTun;
lastProfileModified = await profile?.profileLastModified;
}
Future _applyProfile() async {
await clashCore.requestGc();
await updateClashConfig();
await setupClashConfig();
await updateGroups();
await updateProviders();
}
@@ -380,7 +410,7 @@ class AppController {
),
);
if (_ref.read(appSettingProvider).closeConnections) {
clashCore.resetConnections();
clashCore.closeConnections();
}
addCheckIpNumDebounce();
}
@@ -408,12 +438,14 @@ class AppController {
}
handleExit() async {
Future.delayed(commonDuration, () {
system.exit();
});
try {
await updateStatus(false);
await savePreferences();
await proxy?.stopProxy();
await clashCore.shutdown();
await clashService?.destroy();
await savePreferences();
} finally {
system.exit();
}
@@ -513,9 +545,9 @@ class AppController {
FlutterError.onError = (details) {
commonPrint.log(details.stack.toString());
};
updateTray(true);
await _initCore();
await _initStatus();
updateTray(true);
autoLaunch?.updateStatus(
_ref.read(appSettingProvider).autoLaunch,
);

View File

@@ -2,7 +2,7 @@
import 'dart:io';
import 'package:fl_clash/fragments/dashboard/widgets/widgets.dart';
import 'package:fl_clash/views/dashboard/widgets/widgets.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/services.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
@@ -118,7 +118,12 @@ enum AccessSortType { none, name, time }
enum ProfileType { file, url }
enum ResultType { success, error }
enum ResultType {
@JsonValue(0)
success,
@JsonValue(-1)
error,
}
enum AppMessageType {
log,
@@ -164,9 +169,13 @@ enum DnsMode {
enum ExternalControllerStatus {
@JsonValue("")
close,
close(""),
@JsonValue("127.0.0.1:9090")
open
open("127.0.0.1:9090");
final String value;
const ExternalControllerStatus(this.value);
}
enum KeyboardModifier {
@@ -248,6 +257,7 @@ enum ActionMethod {
shutdown,
validateConfig,
updateConfig,
getConfig,
getProxies,
changeProxy,
getTraffic,
@@ -269,12 +279,10 @@ enum ActionMethod {
stopListener,
getCountryCode,
getMemory,
getProfile,
crash,
setupConfig,
///Android,
setFdMap,
setProcessMap,
setState,
startTun,
stopTun,
@@ -294,6 +302,7 @@ enum WindowsHelperServiceStatus {
enum FunctionTag {
updateClashConfig,
setupClashConfig,
updateStatus,
updateGroups,
addCheckIpNum,
@@ -485,3 +494,8 @@ enum CacheTag {
rules,
requests,
}
enum Language {
yaml,
javaScript,
}

View File

@@ -20,7 +20,23 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'en';
static String m0(count) => "${count} items have been selected";
static String m0(label) =>
"Are you sure you want to delete the selected ${label}?";
static String m1(label) =>
"Are you sure you want to delete the current ${label}?";
static String m2(label) => "${label} cannot be empty";
static String m3(label) => "Current ${label} already exists";
static String m4(label) => "No ${label} at the moment";
static String m5(label) => "${label} must be a number";
static String m6(count) => "${count} items have been selected";
static String m7(label) => "${label} must be a url";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -36,9 +52,6 @@ class MessageLookup extends MessageLookupByLibrary {
"The selected application will be excluded from VPN",
),
"account": MessageLookupByLibrary.simpleMessage("Account"),
"accountTip": MessageLookupByLibrary.simpleMessage(
"Account cannot be empty",
),
"action": MessageLookupByLibrary.simpleMessage("Action"),
"action_mode": MessageLookupByLibrary.simpleMessage("Switch mode"),
"action_proxy": MessageLookupByLibrary.simpleMessage("System proxy"),
@@ -149,9 +162,7 @@ class MessageLookup extends MessageLookupByLibrary {
"clearData": MessageLookupByLibrary.simpleMessage("Clear Data"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("Export clipboard"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("Clipboard import"),
"colorExists": MessageLookupByLibrary.simpleMessage(
"Current color already exists",
),
"color": MessageLookupByLibrary.simpleMessage("Color"),
"colorSchemes": MessageLookupByLibrary.simpleMessage("Color schemes"),
"columns": MessageLookupByLibrary.simpleMessage("Columns"),
"compatible": MessageLookupByLibrary.simpleMessage("Compatibility mode"),
@@ -166,9 +177,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"contactMe": MessageLookupByLibrary.simpleMessage("Contact me"),
"content": MessageLookupByLibrary.simpleMessage("Content"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
"Content cannot be empty",
),
"contentScheme": MessageLookupByLibrary.simpleMessage("Content"),
"copy": MessageLookupByLibrary.simpleMessage("Copy"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
@@ -196,15 +204,8 @@ class MessageLookup extends MessageLookupByLibrary {
"delay": MessageLookupByLibrary.simpleMessage("Delay"),
"delaySort": MessageLookupByLibrary.simpleMessage("Sort by delay"),
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
"deleteColorTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to delete the current color?",
),
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
"Sure you want to delete the current profile?",
),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to delete the selected rule?",
),
"deleteMultipTip": m0,
"deleteTip": m1,
"desc": MessageLookupByLibrary.simpleMessage(
"A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
),
@@ -236,6 +237,7 @@ class MessageLookup extends MessageLookupByLibrary {
"domain": MessageLookupByLibrary.simpleMessage("Domain"),
"download": MessageLookupByLibrary.simpleMessage("Download"),
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
"emptyTip": m2,
"en": MessageLookupByLibrary.simpleMessage("English"),
"enableOverride": MessageLookupByLibrary.simpleMessage("Enable override"),
"entries": MessageLookupByLibrary.simpleMessage(" entries"),
@@ -243,6 +245,7 @@ class MessageLookup extends MessageLookupByLibrary {
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"When the app is in the background, the app is hidden from the recent task",
),
"existsTip": m3,
"exit": MessageLookupByLibrary.simpleMessage("Exit"),
"expand": MessageLookupByLibrary.simpleMessage("Standard"),
"expirationTime": MessageLookupByLibrary.simpleMessage("Expiration time"),
@@ -328,6 +331,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Intelligent selection",
),
"internet": MessageLookupByLibrary.simpleMessage("Internet"),
"interval": MessageLookupByLibrary.simpleMessage("Interval"),
"intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
@@ -342,9 +346,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Tcp keep alive interval",
),
"key": MessageLookupByLibrary.simpleMessage("Key"),
"keyExists": MessageLookupByLibrary.simpleMessage(
"The current key already exists",
),
"language": MessageLookupByLibrary.simpleMessage("Language"),
"layout": MessageLookupByLibrary.simpleMessage("Layout"),
"light": MessageLookupByLibrary.simpleMessage("Light"),
@@ -419,22 +420,14 @@ class MessageLookup extends MessageLookupByLibrary {
),
"noResolve": MessageLookupByLibrary.simpleMessage("No resolve IP"),
"none": MessageLookupByLibrary.simpleMessage("none"),
"notEmpty": MessageLookupByLibrary.simpleMessage("Cannot be empty"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"The current proxy group cannot be selected.",
),
"nullConnectionsDesc": MessageLookupByLibrary.simpleMessage(
"No connections",
),
"nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage(
"Unable to obtain core info",
),
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("No logs"),
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"No profile, Please add a profile",
),
"nullProxies": MessageLookupByLibrary.simpleMessage("No proxies"),
"nullRequestsDesc": MessageLookupByLibrary.simpleMessage("No requests"),
"nullTip": m4,
"numberTip": m5,
"oneColumn": MessageLookupByLibrary.simpleMessage("One column"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("Icon"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage(
@@ -460,18 +453,21 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Turning it on will override the DNS options in the profile",
),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
"Does not take effect in script mode",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
"Override the original rule",
),
"palette": MessageLookupByLibrary.simpleMessage("Palette"),
"password": MessageLookupByLibrary.simpleMessage("Password"),
"passwordTip": MessageLookupByLibrary.simpleMessage(
"Password cannot be empty",
),
"paste": MessageLookupByLibrary.simpleMessage("Paste"),
"pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage(
"Please bind WebDAV",
),
"pleaseEnterScriptName": MessageLookupByLibrary.simpleMessage(
"Please enter a script name",
),
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"Please enter the admin password",
),
@@ -528,6 +524,9 @@ class MessageLookup extends MessageLookupByLibrary {
"proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Set the Clash listening port",
),
"proxyPortTip": MessageLookupByLibrary.simpleMessage(
"proxy port must be between 1024 and 49151",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("Proxy providers"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("Pure black mode"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR code"),
@@ -560,6 +559,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Recovery data from WebDAV",
),
"remove": MessageLookupByLibrary.simpleMessage("Remove"),
"rename": MessageLookupByLibrary.simpleMessage("Rename"),
"requests": MessageLookupByLibrary.simpleMessage("Requests"),
"requestsDesc": MessageLookupByLibrary.simpleMessage(
"View recently request records",
@@ -586,14 +586,8 @@ class MessageLookup extends MessageLookupByLibrary {
"ru": MessageLookupByLibrary.simpleMessage("Russian"),
"rule": MessageLookupByLibrary.simpleMessage("Rule"),
"ruleName": MessageLookupByLibrary.simpleMessage("Rule name"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
"Rule provider cannot be empty",
),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Rule providers"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("Rule target"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage(
"Rule target cannot be empty",
),
"save": MessageLookupByLibrary.simpleMessage("Save"),
"saveChanges": MessageLookupByLibrary.simpleMessage(
"Do you want to save the changes?",
@@ -601,11 +595,12 @@ class MessageLookup extends MessageLookupByLibrary {
"saveTip": MessageLookupByLibrary.simpleMessage(
"Are you sure you want to save?",
),
"script": MessageLookupByLibrary.simpleMessage("Script"),
"search": MessageLookupByLibrary.simpleMessage("Search"),
"seconds": MessageLookupByLibrary.simpleMessage("Seconds"),
"selectAll": MessageLookupByLibrary.simpleMessage("Select all"),
"selected": MessageLookupByLibrary.simpleMessage("Selected"),
"selectedCountTitle": m0,
"selectedCountTitle": m6,
"settings": MessageLookupByLibrary.simpleMessage("Settings"),
"show": MessageLookupByLibrary.simpleMessage("Show"),
"shrink": MessageLookupByLibrary.simpleMessage("Shrink"),
@@ -629,9 +624,6 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("Stopping VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Style"),
"subRule": MessageLookupByLibrary.simpleMessage("Sub rule"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage(
"Sub rule content cannot be empty",
),
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
"system": MessageLookupByLibrary.simpleMessage("System"),
@@ -680,18 +672,17 @@ class MessageLookup extends MessageLookupByLibrary {
"Remove extra delays such as handshaking",
),
"unknown": MessageLookupByLibrary.simpleMessage("Unknown"),
"unnamed": MessageLookupByLibrary.simpleMessage("Unnamed"),
"update": MessageLookupByLibrary.simpleMessage("Update"),
"upload": MessageLookupByLibrary.simpleMessage("Upload"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage(
"Obtain profile through URL",
),
"urlTip": m7,
"useHosts": MessageLookupByLibrary.simpleMessage("Use hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("Use system hosts"),
"value": MessageLookupByLibrary.simpleMessage("Value"),
"valueExists": MessageLookupByLibrary.simpleMessage(
"The current value already exists",
),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("Vibrant"),
"view": MessageLookupByLibrary.simpleMessage("View"),
"vpnDesc": MessageLookupByLibrary.simpleMessage(

View File

@@ -20,7 +20,21 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'ja';
static String m0(count) => "${count} 項目が選択されています";
static String m0(label) => "選択された${label}を削除してもよろしいですか?";
static String m1(label) => "現在の${label}を削除してもよろしいですか?";
static String m2(label) => "${label}は空欄にできません";
static String m3(label) => "現在の${label}は既に存在しています";
static String m4(label) => "現在${label}はありません";
static String m5(label) => "${label}は数字でなければなりません";
static String m6(count) => "${count} 項目が選択されています";
static String m7(label) => "${label}はURLである必要があります";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -36,7 +50,6 @@ class MessageLookup extends MessageLookupByLibrary {
"選択したアプリをVPNから除外",
),
"account": MessageLookupByLibrary.simpleMessage("アカウント"),
"accountTip": MessageLookupByLibrary.simpleMessage("アカウントは必須です"),
"action": MessageLookupByLibrary.simpleMessage("アクション"),
"action_mode": MessageLookupByLibrary.simpleMessage("モード切替"),
"action_proxy": MessageLookupByLibrary.simpleMessage("システムプロキシ"),
@@ -107,7 +120,7 @@ class MessageLookup extends MessageLookupByLibrary {
"clearData": MessageLookupByLibrary.simpleMessage("データを消去"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("クリップボードにエクスポート"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("クリップボードからインポート"),
"colorExists": MessageLookupByLibrary.simpleMessage("この色は既に存在します"),
"color": MessageLookupByLibrary.simpleMessage("カラー"),
"colorSchemes": MessageLookupByLibrary.simpleMessage("カラースキーム"),
"columns": MessageLookupByLibrary.simpleMessage(""),
"compatible": MessageLookupByLibrary.simpleMessage("互換モード"),
@@ -120,7 +133,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("接続性:"),
"contactMe": MessageLookupByLibrary.simpleMessage("連絡する"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容は必須です"),
"contentScheme": MessageLookupByLibrary.simpleMessage("コンテンツテーマ"),
"copy": MessageLookupByLibrary.simpleMessage("コピー"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage("環境変数をコピー"),
@@ -144,11 +156,8 @@ class MessageLookup extends MessageLookupByLibrary {
"delay": MessageLookupByLibrary.simpleMessage("遅延"),
"delaySort": MessageLookupByLibrary.simpleMessage("遅延順"),
"delete": MessageLookupByLibrary.simpleMessage("削除"),
"deleteColorTip": MessageLookupByLibrary.simpleMessage("現在の色を削除しますか?"),
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
"現在のプロファイルを削除しますか?",
),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage("選択したルールを削除しますか?"),
"deleteMultipTip": m0,
"deleteTip": m1,
"desc": MessageLookupByLibrary.simpleMessage(
"ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
),
@@ -170,6 +179,7 @@ class MessageLookup extends MessageLookupByLibrary {
"domain": MessageLookupByLibrary.simpleMessage("ドメイン"),
"download": MessageLookupByLibrary.simpleMessage("ダウンロード"),
"edit": MessageLookupByLibrary.simpleMessage("編集"),
"emptyTip": m2,
"en": MessageLookupByLibrary.simpleMessage("英語"),
"enableOverride": MessageLookupByLibrary.simpleMessage("上書きを有効化"),
"entries": MessageLookupByLibrary.simpleMessage(" エントリ"),
@@ -177,6 +187,7 @@ class MessageLookup extends MessageLookupByLibrary {
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"アプリがバックグラウンド時に最近のタスクから非表示",
),
"existsTip": m3,
"exit": MessageLookupByLibrary.simpleMessage("終了"),
"expand": MessageLookupByLibrary.simpleMessage("標準"),
"expirationTime": MessageLookupByLibrary.simpleMessage("有効期限"),
@@ -238,6 +249,7 @@ class MessageLookup extends MessageLookupByLibrary {
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("正しいホットキーを入力"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("インテリジェント選択"),
"internet": MessageLookupByLibrary.simpleMessage("インターネット"),
"interval": MessageLookupByLibrary.simpleMessage("インターバル"),
"intranetIP": MessageLookupByLibrary.simpleMessage("イントラネットIP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("有効化するとIPv6トラフィックを受信可能"),
@@ -248,7 +260,6 @@ class MessageLookup extends MessageLookupByLibrary {
"TCPキープアライブ間隔",
),
"key": MessageLookupByLibrary.simpleMessage("キー"),
"keyExists": MessageLookupByLibrary.simpleMessage("現在のキーは既に存在します"),
"language": MessageLookupByLibrary.simpleMessage("言語"),
"layout": MessageLookupByLibrary.simpleMessage("レイアウト"),
"light": MessageLookupByLibrary.simpleMessage("ライト"),
@@ -305,18 +316,14 @@ class MessageLookup extends MessageLookupByLibrary {
),
"noResolve": MessageLookupByLibrary.simpleMessage("IPを解決しない"),
"none": MessageLookupByLibrary.simpleMessage("なし"),
"notEmpty": MessageLookupByLibrary.simpleMessage("空欄不可"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"現在のプロキシグループは選択できません",
),
"nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("接続なし"),
"nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage("コア情報を取得できません"),
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("ログがありません"),
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"プロファイルがありません。追加してください",
),
"nullProxies": MessageLookupByLibrary.simpleMessage("プロキシなし"),
"nullRequestsDesc": MessageLookupByLibrary.simpleMessage("リクエストなし"),
"nullTip": m4,
"numberTip": m5,
"oneColumn": MessageLookupByLibrary.simpleMessage("1列"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("アイコンのみ"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage("サードパーティアプリのみ"),
@@ -334,14 +341,19 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"有効化するとプロファイルのDNS設定を上書き",
),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
"スクリプトモードでは有効になりません",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("元のルールを上書き"),
"palette": MessageLookupByLibrary.simpleMessage("パレット"),
"password": MessageLookupByLibrary.simpleMessage("パスワード"),
"passwordTip": MessageLookupByLibrary.simpleMessage("パスワードは必須です"),
"paste": MessageLookupByLibrary.simpleMessage("貼り付け"),
"pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage(
"WebDAVをバインドしてください",
),
"pleaseEnterScriptName": MessageLookupByLibrary.simpleMessage(
"スクリプト名を入力してください",
),
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"管理者パスワードを入力",
),
@@ -388,6 +400,9 @@ class MessageLookup extends MessageLookupByLibrary {
),
"proxyPort": MessageLookupByLibrary.simpleMessage("プロキシポート"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("Clashのリスニングポートを設定"),
"proxyPortTip": MessageLookupByLibrary.simpleMessage(
"プロキシポートは1024から49151の間でなければなりません",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("プロキシプロバイダー"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("純黒モード"),
"qrcode": MessageLookupByLibrary.simpleMessage("QRコード"),
@@ -412,6 +427,7 @@ class MessageLookup extends MessageLookupByLibrary {
"WebDAVからデータを復元",
),
"remove": MessageLookupByLibrary.simpleMessage("削除"),
"rename": MessageLookupByLibrary.simpleMessage("リネーム"),
"requests": MessageLookupByLibrary.simpleMessage("リクエスト"),
"requestsDesc": MessageLookupByLibrary.simpleMessage("最近のリクエスト記録を表示"),
"reset": MessageLookupByLibrary.simpleMessage("リセット"),
@@ -432,20 +448,17 @@ class MessageLookup extends MessageLookupByLibrary {
"ru": MessageLookupByLibrary.simpleMessage("ロシア語"),
"rule": MessageLookupByLibrary.simpleMessage("ルール"),
"ruleName": MessageLookupByLibrary.simpleMessage("ルール名"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
"ルールプロバイダーは必須です",
),
"ruleProviders": MessageLookupByLibrary.simpleMessage("ルールプロバイダー"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("ルール対象"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage("ルール対象は必須です"),
"save": MessageLookupByLibrary.simpleMessage("保存"),
"saveChanges": MessageLookupByLibrary.simpleMessage("変更を保存しますか?"),
"saveTip": MessageLookupByLibrary.simpleMessage("保存してもよろしいですか?"),
"script": MessageLookupByLibrary.simpleMessage("スクリプト"),
"search": MessageLookupByLibrary.simpleMessage("検索"),
"seconds": MessageLookupByLibrary.simpleMessage(""),
"selectAll": MessageLookupByLibrary.simpleMessage("すべて選択"),
"selected": MessageLookupByLibrary.simpleMessage("選択済み"),
"selectedCountTitle": m0,
"selectedCountTitle": m6,
"settings": MessageLookupByLibrary.simpleMessage("設定"),
"show": MessageLookupByLibrary.simpleMessage("表示"),
"shrink": MessageLookupByLibrary.simpleMessage("縮小"),
@@ -465,7 +478,6 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("VPNを停止中..."),
"style": MessageLookupByLibrary.simpleMessage("スタイル"),
"subRule": MessageLookupByLibrary.simpleMessage("サブルール"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage("サブルールの内容は必須です"),
"submit": MessageLookupByLibrary.simpleMessage("送信"),
"sync": MessageLookupByLibrary.simpleMessage("同期"),
"system": MessageLookupByLibrary.simpleMessage("システム"),
@@ -506,14 +518,15 @@ class MessageLookup extends MessageLookupByLibrary {
"ハンドシェイクなどの余分な遅延を削除",
),
"unknown": MessageLookupByLibrary.simpleMessage("不明"),
"unnamed": MessageLookupByLibrary.simpleMessage("無題"),
"update": MessageLookupByLibrary.simpleMessage("更新"),
"upload": MessageLookupByLibrary.simpleMessage("アップロード"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("URL経由でプロファイルを取得"),
"urlTip": m7,
"useHosts": MessageLookupByLibrary.simpleMessage("ホストを使用"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("システムホストを使用"),
"value": MessageLookupByLibrary.simpleMessage(""),
"valueExists": MessageLookupByLibrary.simpleMessage("現在の値は既に存在します"),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("ビブラント"),
"view": MessageLookupByLibrary.simpleMessage("表示"),
"vpnDesc": MessageLookupByLibrary.simpleMessage("VPN関連設定の変更"),

View File

@@ -20,7 +20,22 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'ru';
static String m0(count) => "Выбрано ${count} элементов";
static String m0(label) =>
"Вы уверены, что хотите удалить выбранные ${label}?";
static String m1(label) => "Вы уверены, что хотите удалить текущий ${label}?";
static String m2(label) => "${label} не может быть пустым";
static String m3(label) => "Текущий ${label} уже существует";
static String m4(label) => "Сейчас ${label} нет";
static String m5(label) => "${label} должно быть числом";
static String m6(count) => "Выбрано ${count} элементов";
static String m7(label) => "${label} должен быть URL";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -36,9 +51,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Выбранные приложения будут исключены из VPN",
),
"account": MessageLookupByLibrary.simpleMessage("Аккаунт"),
"accountTip": MessageLookupByLibrary.simpleMessage(
"Аккаунт не может быть пустым",
),
"action": MessageLookupByLibrary.simpleMessage("Действие"),
"action_mode": MessageLookupByLibrary.simpleMessage("Переключить режим"),
"action_proxy": MessageLookupByLibrary.simpleMessage("Системный прокси"),
@@ -155,9 +167,7 @@ class MessageLookup extends MessageLookupByLibrary {
"clipboardImport": MessageLookupByLibrary.simpleMessage(
"Импорт из буфера обмена",
),
"colorExists": MessageLookupByLibrary.simpleMessage(
"Этот цвет уже существует",
),
"color": MessageLookupByLibrary.simpleMessage("Цвет"),
"colorSchemes": MessageLookupByLibrary.simpleMessage("Цветовые схемы"),
"columns": MessageLookupByLibrary.simpleMessage("Столбцы"),
"compatible": MessageLookupByLibrary.simpleMessage("Режим совместимости"),
@@ -172,9 +182,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("Связь:"),
"contactMe": MessageLookupByLibrary.simpleMessage("Свяжитесь со мной"),
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
"Содержание не может быть пустым",
),
"contentScheme": MessageLookupByLibrary.simpleMessage("Контентная тема"),
"copy": MessageLookupByLibrary.simpleMessage("Копировать"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage(
@@ -204,15 +211,8 @@ class MessageLookup extends MessageLookupByLibrary {
"delay": MessageLookupByLibrary.simpleMessage("Задержка"),
"delaySort": MessageLookupByLibrary.simpleMessage("Сортировка по задержке"),
"delete": MessageLookupByLibrary.simpleMessage("Удалить"),
"deleteColorTip": MessageLookupByLibrary.simpleMessage(
"Удалить текущий цвет?",
),
"deleteProfileTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите удалить текущий профиль?",
),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите удалить выбранное правило?",
),
"deleteMultipTip": m0,
"deleteTip": m1,
"desc": MessageLookupByLibrary.simpleMessage(
"Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
),
@@ -246,6 +246,7 @@ class MessageLookup extends MessageLookupByLibrary {
"domain": MessageLookupByLibrary.simpleMessage("Домен"),
"download": MessageLookupByLibrary.simpleMessage("Скачивание"),
"edit": MessageLookupByLibrary.simpleMessage("Редактировать"),
"emptyTip": m2,
"en": MessageLookupByLibrary.simpleMessage("Английский"),
"enableOverride": MessageLookupByLibrary.simpleMessage(
"Включить переопределение",
@@ -257,6 +258,7 @@ class MessageLookup extends MessageLookupByLibrary {
"excludeDesc": MessageLookupByLibrary.simpleMessage(
"Когда приложение находится в фоновом режиме, оно скрыто из последних задач",
),
"existsTip": m3,
"exit": MessageLookupByLibrary.simpleMessage("Выход"),
"expand": MessageLookupByLibrary.simpleMessage("Стандартный"),
"expirationTime": MessageLookupByLibrary.simpleMessage("Время истечения"),
@@ -350,6 +352,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Интеллектуальный выбор",
),
"internet": MessageLookupByLibrary.simpleMessage("Интернет"),
"interval": MessageLookupByLibrary.simpleMessage("Интервал"),
"intranetIP": MessageLookupByLibrary.simpleMessage("Внутренний IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
@@ -364,9 +367,6 @@ class MessageLookup extends MessageLookupByLibrary {
"Интервал поддержания TCP-соединения",
),
"key": MessageLookupByLibrary.simpleMessage("Ключ"),
"keyExists": MessageLookupByLibrary.simpleMessage(
"Текущий ключ уже существует",
),
"language": MessageLookupByLibrary.simpleMessage("Язык"),
"layout": MessageLookupByLibrary.simpleMessage("Макет"),
"light": MessageLookupByLibrary.simpleMessage("Светлый"),
@@ -447,22 +447,14 @@ class MessageLookup extends MessageLookupByLibrary {
),
"noResolve": MessageLookupByLibrary.simpleMessage("Не разрешать IP"),
"none": MessageLookupByLibrary.simpleMessage("Нет"),
"notEmpty": MessageLookupByLibrary.simpleMessage("Не может быть пустым"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage(
"Текущая группа прокси не может быть выбрана.",
),
"nullConnectionsDesc": MessageLookupByLibrary.simpleMessage(
"Нет соединений",
),
"nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage(
"Не удалось получить информацию о ядре",
),
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("Нет логов"),
"nullProfileDesc": MessageLookupByLibrary.simpleMessage(
"Нет профиля, пожалуйста, добавьте профиль",
),
"nullProxies": MessageLookupByLibrary.simpleMessage("Нет прокси"),
"nullRequestsDesc": MessageLookupByLibrary.simpleMessage("Нет запросов"),
"nullTip": m4,
"numberTip": m5,
"oneColumn": MessageLookupByLibrary.simpleMessage("Один столбец"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("Только иконка"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage(
@@ -490,18 +482,21 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage(
"Включение переопределит настройки DNS в профиле",
),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage(
"В скриптовом режиме не действует",
),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage(
"Переопределить оригинальное правило",
),
"palette": MessageLookupByLibrary.simpleMessage("Палитра"),
"password": MessageLookupByLibrary.simpleMessage("Пароль"),
"passwordTip": MessageLookupByLibrary.simpleMessage(
"Пароль не может быть пустым",
),
"paste": MessageLookupByLibrary.simpleMessage("Вставить"),
"pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, привяжите WebDAV",
),
"pleaseEnterScriptName": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите название скрипта",
),
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, введите пароль администратора",
),
@@ -560,6 +555,9 @@ class MessageLookup extends MessageLookupByLibrary {
"proxyPortDesc": MessageLookupByLibrary.simpleMessage(
"Установить порт прослушивания Clash",
),
"proxyPortTip": MessageLookupByLibrary.simpleMessage(
"Порт прокси должен быть в диапазоне от 1024 до 49151",
),
"proxyProviders": MessageLookupByLibrary.simpleMessage("Провайдеры прокси"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("Чисто черный режим"),
"qrcode": MessageLookupByLibrary.simpleMessage("QR-код"),
@@ -596,6 +594,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Восстановление данных с WebDAV",
),
"remove": MessageLookupByLibrary.simpleMessage("Удалить"),
"rename": MessageLookupByLibrary.simpleMessage("Переименовать"),
"requests": MessageLookupByLibrary.simpleMessage("Запросы"),
"requestsDesc": MessageLookupByLibrary.simpleMessage(
"Просмотр последних записей запросов",
@@ -626,24 +625,19 @@ class MessageLookup extends MessageLookupByLibrary {
"ru": MessageLookupByLibrary.simpleMessage("Русский"),
"rule": MessageLookupByLibrary.simpleMessage("Правило"),
"ruleName": MessageLookupByLibrary.simpleMessage("Название правила"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage(
"Поставщик правил не может быть пустым",
),
"ruleProviders": MessageLookupByLibrary.simpleMessage("Провайдеры правил"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("Цель правила"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage(
"Цель правила не может быть пустой",
),
"save": MessageLookupByLibrary.simpleMessage("Сохранить"),
"saveChanges": MessageLookupByLibrary.simpleMessage("Сохранить изменения?"),
"saveTip": MessageLookupByLibrary.simpleMessage(
"Вы уверены, что хотите сохранить?",
),
"script": MessageLookupByLibrary.simpleMessage("Скрипт"),
"search": MessageLookupByLibrary.simpleMessage("Поиск"),
"seconds": MessageLookupByLibrary.simpleMessage("Секунд"),
"selectAll": MessageLookupByLibrary.simpleMessage("Выбрать все"),
"selected": MessageLookupByLibrary.simpleMessage("Выбрано"),
"selectedCountTitle": m0,
"selectedCountTitle": m6,
"settings": MessageLookupByLibrary.simpleMessage("Настройки"),
"show": MessageLookupByLibrary.simpleMessage("Показать"),
"shrink": MessageLookupByLibrary.simpleMessage("Сжать"),
@@ -667,9 +661,6 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("Остановка VPN..."),
"style": MessageLookupByLibrary.simpleMessage("Стиль"),
"subRule": MessageLookupByLibrary.simpleMessage("Подправило"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage(
"Содержание подправила не может быть пустым",
),
"submit": MessageLookupByLibrary.simpleMessage("Отправить"),
"sync": MessageLookupByLibrary.simpleMessage("Синхронизация"),
"system": MessageLookupByLibrary.simpleMessage("Система"),
@@ -722,20 +713,19 @@ class MessageLookup extends MessageLookupByLibrary {
"Убрать дополнительные задержки, такие как рукопожатие",
),
"unknown": MessageLookupByLibrary.simpleMessage("Неизвестно"),
"unnamed": MessageLookupByLibrary.simpleMessage("Без имени"),
"update": MessageLookupByLibrary.simpleMessage("Обновить"),
"upload": MessageLookupByLibrary.simpleMessage("Загрузка"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage(
"Получить профиль через URL",
),
"urlTip": m7,
"useHosts": MessageLookupByLibrary.simpleMessage("Использовать hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage(
"Использовать системные hosts",
),
"value": MessageLookupByLibrary.simpleMessage("Значение"),
"valueExists": MessageLookupByLibrary.simpleMessage(
"Текущее значение уже существует",
),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("Яркие"),
"view": MessageLookupByLibrary.simpleMessage("Просмотр"),
"vpnDesc": MessageLookupByLibrary.simpleMessage(

View File

@@ -20,7 +20,21 @@ typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
class MessageLookup extends MessageLookupByLibrary {
String get localeName => 'zh_CN';
static String m0(count) => "已选择 ${count}";
static String m0(label) => "确定删除选中的${label}吗?";
static String m1(label) => "确定删除当前${label}吗?";
static String m2(label) => "${label}不能为空";
static String m3(label) => "${label}当前已存在";
static String m4(label) => "暂无${label}";
static String m5(label) => "${label}必须为数字";
static String m6(count) => "已选择 ${count}";
static String m7(label) => "${label}必须为URL";
final messages = _notInlinedMessages(_notInlinedMessages);
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
@@ -34,7 +48,6 @@ class MessageLookup extends MessageLookupByLibrary {
"选中应用将会被排除在VPN之外",
),
"account": MessageLookupByLibrary.simpleMessage("账号"),
"accountTip": MessageLookupByLibrary.simpleMessage("账号不能为空"),
"action": MessageLookupByLibrary.simpleMessage("操作"),
"action_mode": MessageLookupByLibrary.simpleMessage("切换模式"),
"action_proxy": MessageLookupByLibrary.simpleMessage("系统代理"),
@@ -97,7 +110,7 @@ class MessageLookup extends MessageLookupByLibrary {
"clearData": MessageLookupByLibrary.simpleMessage("清除数据"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("导出剪贴板"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("剪贴板导入"),
"colorExists": MessageLookupByLibrary.simpleMessage("颜色已存在"),
"color": MessageLookupByLibrary.simpleMessage("颜色"),
"colorSchemes": MessageLookupByLibrary.simpleMessage("配色方案"),
"columns": MessageLookupByLibrary.simpleMessage("列数"),
"compatible": MessageLookupByLibrary.simpleMessage("兼容模式"),
@@ -110,7 +123,6 @@ class MessageLookup extends MessageLookupByLibrary {
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"contactMe": MessageLookupByLibrary.simpleMessage("联系我"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容不能为空"),
"contentScheme": MessageLookupByLibrary.simpleMessage("内容主题"),
"copy": MessageLookupByLibrary.simpleMessage("复制"),
"copyEnvVar": MessageLookupByLibrary.simpleMessage("复制环境变量"),
@@ -132,9 +144,8 @@ class MessageLookup extends MessageLookupByLibrary {
"delay": MessageLookupByLibrary.simpleMessage("延迟"),
"delaySort": MessageLookupByLibrary.simpleMessage("按延迟排序"),
"delete": MessageLookupByLibrary.simpleMessage("删除"),
"deleteColorTip": MessageLookupByLibrary.simpleMessage("确定删除当前颜色吗?"),
"deleteProfileTip": MessageLookupByLibrary.simpleMessage("确定要删除当前配置吗?"),
"deleteRuleTip": MessageLookupByLibrary.simpleMessage("确定要删除选中的规则吗?"),
"deleteMultipTip": m0,
"deleteTip": m1,
"desc": MessageLookupByLibrary.simpleMessage(
"基于ClashMeta的多平台代理客户端简单易用开源无广告。",
),
@@ -154,11 +165,13 @@ class MessageLookup extends MessageLookupByLibrary {
"domain": MessageLookupByLibrary.simpleMessage("域名"),
"download": MessageLookupByLibrary.simpleMessage("下载"),
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
"emptyTip": m2,
"en": MessageLookupByLibrary.simpleMessage("英语"),
"enableOverride": MessageLookupByLibrary.simpleMessage("启用覆写"),
"entries": MessageLookupByLibrary.simpleMessage("个条目"),
"exclude": MessageLookupByLibrary.simpleMessage("从最近任务中隐藏"),
"excludeDesc": MessageLookupByLibrary.simpleMessage("应用在后台时,从最近任务中隐藏应用"),
"existsTip": m3,
"exit": MessageLookupByLibrary.simpleMessage("退出"),
"expand": MessageLookupByLibrary.simpleMessage("标准"),
"expirationTime": MessageLookupByLibrary.simpleMessage("到期时间"),
@@ -212,6 +225,7 @@ class MessageLookup extends MessageLookupByLibrary {
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"),
"internet": MessageLookupByLibrary.simpleMessage("互联网"),
"interval": MessageLookupByLibrary.simpleMessage("间隔"),
"intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"),
@@ -220,7 +234,6 @@ class MessageLookup extends MessageLookupByLibrary {
"just": MessageLookupByLibrary.simpleMessage("刚刚"),
"keepAliveIntervalDesc": MessageLookupByLibrary.simpleMessage("TCP保持活动间隔"),
"key": MessageLookupByLibrary.simpleMessage(""),
"keyExists": MessageLookupByLibrary.simpleMessage("当前键已存在"),
"language": MessageLookupByLibrary.simpleMessage("语言"),
"layout": MessageLookupByLibrary.simpleMessage("布局"),
"light": MessageLookupByLibrary.simpleMessage("浅色"),
@@ -271,14 +284,10 @@ class MessageLookup extends MessageLookupByLibrary {
"noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"),
"noResolve": MessageLookupByLibrary.simpleMessage("不解析IP"),
"none": MessageLookupByLibrary.simpleMessage(""),
"notEmpty": MessageLookupByLibrary.simpleMessage("不能为空"),
"notSelectedTip": MessageLookupByLibrary.simpleMessage("当前代理组无法选中"),
"nullConnectionsDesc": MessageLookupByLibrary.simpleMessage("暂无连接"),
"nullCoreInfoDesc": MessageLookupByLibrary.simpleMessage("无法获取内核信息"),
"nullLogsDesc": MessageLookupByLibrary.simpleMessage("暂无日志"),
"nullProfileDesc": MessageLookupByLibrary.simpleMessage("没有配置文件,请先添加配置文件"),
"nullProxies": MessageLookupByLibrary.simpleMessage("暂无代理"),
"nullRequestsDesc": MessageLookupByLibrary.simpleMessage("暂无请求"),
"nullTip": m4,
"numberTip": m5,
"oneColumn": MessageLookupByLibrary.simpleMessage("一列"),
"onlyIcon": MessageLookupByLibrary.simpleMessage("仅图标"),
"onlyOtherApps": MessageLookupByLibrary.simpleMessage("仅第三方应用"),
@@ -294,12 +303,13 @@ class MessageLookup extends MessageLookupByLibrary {
"overrideDesc": MessageLookupByLibrary.simpleMessage("覆写代理相关配置"),
"overrideDns": MessageLookupByLibrary.simpleMessage("覆写DNS"),
"overrideDnsDesc": MessageLookupByLibrary.simpleMessage("开启后将覆盖配置中的DNS选项"),
"overrideInvalidTip": MessageLookupByLibrary.simpleMessage("在脚本模式下不生效"),
"overrideOriginRules": MessageLookupByLibrary.simpleMessage("覆盖原始规则"),
"palette": MessageLookupByLibrary.simpleMessage("调色板"),
"password": MessageLookupByLibrary.simpleMessage("密码"),
"passwordTip": MessageLookupByLibrary.simpleMessage("密码不能为空"),
"paste": MessageLookupByLibrary.simpleMessage("粘贴"),
"pleaseBindWebDAV": MessageLookupByLibrary.simpleMessage("请绑定WebDAV"),
"pleaseEnterScriptName": MessageLookupByLibrary.simpleMessage("请输入脚本名称"),
"pleaseInputAdminPassword": MessageLookupByLibrary.simpleMessage(
"请输入管理员密码",
),
@@ -340,6 +350,7 @@ class MessageLookup extends MessageLookupByLibrary {
"proxyNameserverDesc": MessageLookupByLibrary.simpleMessage("用于解析代理节点的域名"),
"proxyPort": MessageLookupByLibrary.simpleMessage("代理端口"),
"proxyPortDesc": MessageLookupByLibrary.simpleMessage("设置Clash监听端口"),
"proxyPortTip": MessageLookupByLibrary.simpleMessage("代理端口必须在1024到49151之间"),
"proxyProviders": MessageLookupByLibrary.simpleMessage("代理提供者"),
"pureBlackMode": MessageLookupByLibrary.simpleMessage("纯黑模式"),
"qrcode": MessageLookupByLibrary.simpleMessage("二维码"),
@@ -358,6 +369,7 @@ class MessageLookup extends MessageLookupByLibrary {
"remoteBackupDesc": MessageLookupByLibrary.simpleMessage("备份数据到WebDAV"),
"remoteRecoveryDesc": MessageLookupByLibrary.simpleMessage("通过WebDAV恢复数据"),
"remove": MessageLookupByLibrary.simpleMessage("移除"),
"rename": MessageLookupByLibrary.simpleMessage("重命名"),
"requests": MessageLookupByLibrary.simpleMessage("请求"),
"requestsDesc": MessageLookupByLibrary.simpleMessage("查看最近请求记录"),
"reset": MessageLookupByLibrary.simpleMessage("重置"),
@@ -376,18 +388,17 @@ class MessageLookup extends MessageLookupByLibrary {
"ru": MessageLookupByLibrary.simpleMessage("俄语"),
"rule": MessageLookupByLibrary.simpleMessage("规则"),
"ruleName": MessageLookupByLibrary.simpleMessage("规则名称"),
"ruleProviderEmptyTip": MessageLookupByLibrary.simpleMessage("规则提供者不能为空"),
"ruleProviders": MessageLookupByLibrary.simpleMessage("规则提供者"),
"ruleTarget": MessageLookupByLibrary.simpleMessage("规则目标"),
"ruleTargetEmptyTip": MessageLookupByLibrary.simpleMessage("规则目标不能为空"),
"save": MessageLookupByLibrary.simpleMessage("保存"),
"saveChanges": MessageLookupByLibrary.simpleMessage("是否保存更改?"),
"saveTip": MessageLookupByLibrary.simpleMessage("确定要保存吗?"),
"script": MessageLookupByLibrary.simpleMessage("脚本"),
"search": MessageLookupByLibrary.simpleMessage("搜索"),
"seconds": MessageLookupByLibrary.simpleMessage(""),
"selectAll": MessageLookupByLibrary.simpleMessage("全选"),
"selected": MessageLookupByLibrary.simpleMessage("已选择"),
"selectedCountTitle": m0,
"selectedCountTitle": m6,
"settings": MessageLookupByLibrary.simpleMessage("设置"),
"show": MessageLookupByLibrary.simpleMessage("显示"),
"shrink": MessageLookupByLibrary.simpleMessage("紧凑"),
@@ -407,7 +418,6 @@ class MessageLookup extends MessageLookupByLibrary {
"stopVpn": MessageLookupByLibrary.simpleMessage("正在停止VPN..."),
"style": MessageLookupByLibrary.simpleMessage("风格"),
"subRule": MessageLookupByLibrary.simpleMessage("子规则"),
"subRuleEmptyTip": MessageLookupByLibrary.simpleMessage("子规则内容不能为空"),
"submit": MessageLookupByLibrary.simpleMessage("提交"),
"sync": MessageLookupByLibrary.simpleMessage("同步"),
"system": MessageLookupByLibrary.simpleMessage("系统"),
@@ -444,14 +454,15 @@ class MessageLookup extends MessageLookupByLibrary {
"unifiedDelay": MessageLookupByLibrary.simpleMessage("统一延迟"),
"unifiedDelayDesc": MessageLookupByLibrary.simpleMessage("去除握手等额外延迟"),
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
"unnamed": MessageLookupByLibrary.simpleMessage("未命名"),
"update": MessageLookupByLibrary.simpleMessage("更新"),
"upload": MessageLookupByLibrary.simpleMessage("上传"),
"url": MessageLookupByLibrary.simpleMessage("URL"),
"urlDesc": MessageLookupByLibrary.simpleMessage("通过URL获取配置文件"),
"urlTip": m7,
"useHosts": MessageLookupByLibrary.simpleMessage("使用Hosts"),
"useSystemHosts": MessageLookupByLibrary.simpleMessage("使用系统Hosts"),
"value": MessageLookupByLibrary.simpleMessage(""),
"valueExists": MessageLookupByLibrary.simpleMessage("当前值已存在"),
"vibrantScheme": MessageLookupByLibrary.simpleMessage("活力"),
"view": MessageLookupByLibrary.simpleMessage("查看"),
"vpnDesc": MessageLookupByLibrary.simpleMessage("修改VPN相关设置"),

View File

@@ -140,16 +140,6 @@ class AppLocalizations {
return Intl.message('Core info', name: 'coreInfo', desc: '', args: []);
}
/// `Unable to obtain core info`
String get nullCoreInfoDesc {
return Intl.message(
'Unable to obtain core info',
name: 'nullCoreInfoDesc',
desc: '',
args: [],
);
}
/// `Network speed`
String get networkSpeed {
return Intl.message(
@@ -215,11 +205,6 @@ class AppLocalizations {
);
}
/// `No logs`
String get nullLogsDesc {
return Intl.message('No logs', name: 'nullLogsDesc', desc: '', args: []);
}
/// `Settings`
String get settings {
return Intl.message('Settings', name: 'settings', desc: '', args: []);
@@ -1160,26 +1145,6 @@ class AppLocalizations {
return Intl.message('Password', name: 'password', desc: '', args: []);
}
/// `Password cannot be empty`
String get passwordTip {
return Intl.message(
'Password cannot be empty',
name: 'passwordTip',
desc: '',
args: [],
);
}
/// `Account cannot be empty`
String get accountTip {
return Intl.message(
'Account cannot be empty',
name: 'accountTip',
desc: '',
args: [],
);
}
/// `Check for updates`
String get checkUpdate {
return Intl.message(
@@ -1465,26 +1430,6 @@ class AppLocalizations {
);
}
/// `No requests`
String get nullRequestsDesc {
return Intl.message(
'No requests',
name: 'nullRequestsDesc',
desc: '',
args: [],
);
}
/// `No connections`
String get nullConnectionsDesc {
return Intl.message(
'No connections',
name: 'nullConnectionsDesc',
desc: '',
args: [],
);
}
/// `Intranet IP`
String get intranetIP {
return Intl.message('Intranet IP', name: 'intranetIP', desc: '', args: []);
@@ -1700,16 +1645,6 @@ class AppLocalizations {
);
}
/// `Sure you want to delete the current profile?`
String get deleteProfileTip {
return Intl.message(
'Sure you want to delete the current profile?',
name: 'deleteProfileTip',
desc: '',
args: [],
);
}
/// `Pure black mode`
String get pureBlackMode {
return Intl.message(
@@ -1930,16 +1865,6 @@ class AppLocalizations {
return Intl.message('Value', name: 'value', desc: '', args: []);
}
/// `Cannot be empty`
String get notEmpty {
return Intl.message(
'Cannot be empty',
name: 'notEmpty',
desc: '',
args: [],
);
}
/// `Add Hosts`
String get hostsDesc {
return Intl.message('Add Hosts', name: 'hostsDesc', desc: '', args: []);
@@ -2615,11 +2540,6 @@ class AppLocalizations {
);
}
/// `No proxies`
String get nullProxies {
return Intl.message('No proxies', name: 'nullProxies', desc: '', args: []);
}
/// `Copy success`
String get copySuccess {
return Intl.message(
@@ -2665,26 +2585,6 @@ class AppLocalizations {
return Intl.message('Listen', name: 'listen', desc: '', args: []);
}
/// `The current key already exists`
String get keyExists {
return Intl.message(
'The current key already exists',
name: 'keyExists',
desc: '',
args: [],
);
}
/// `The current value already exists`
String get valueExists {
return Intl.message(
'The current value already exists',
name: 'valueExists',
desc: '',
args: [],
);
}
/// `undo`
String get undo {
return Intl.message('undo', name: 'undo', desc: '', args: []);
@@ -2735,16 +2635,6 @@ class AppLocalizations {
return Intl.message('Add rule', name: 'addRule', desc: '', args: []);
}
/// `Rule provider cannot be empty`
String get ruleProviderEmptyTip {
return Intl.message(
'Rule provider cannot be empty',
name: 'ruleProviderEmptyTip',
desc: '',
args: [],
);
}
/// `Rule name`
String get ruleName {
return Intl.message('Rule name', name: 'ruleName', desc: '', args: []);
@@ -2755,46 +2645,16 @@ class AppLocalizations {
return Intl.message('Content', name: 'content', desc: '', args: []);
}
/// `Content cannot be empty`
String get contentEmptyTip {
return Intl.message(
'Content cannot be empty',
name: 'contentEmptyTip',
desc: '',
args: [],
);
}
/// `Sub rule`
String get subRule {
return Intl.message('Sub rule', name: 'subRule', desc: '', args: []);
}
/// `Sub rule content cannot be empty`
String get subRuleEmptyTip {
return Intl.message(
'Sub rule content cannot be empty',
name: 'subRuleEmptyTip',
desc: '',
args: [],
);
}
/// `Rule target`
String get ruleTarget {
return Intl.message('Rule target', name: 'ruleTarget', desc: '', args: []);
}
/// `Rule target cannot be empty`
String get ruleTargetEmptyTip {
return Intl.message(
'Rule target cannot be empty',
name: 'ruleTargetEmptyTip',
desc: '',
args: [],
);
}
/// `Source IP`
String get sourceIp {
return Intl.message('Source IP', name: 'sourceIp', desc: '', args: []);
@@ -2845,16 +2705,6 @@ class AppLocalizations {
);
}
/// `Are you sure you want to delete the selected rule?`
String get deleteRuleTip {
return Intl.message(
'Are you sure you want to delete the selected rule?',
name: 'deleteRuleTip',
desc: '',
args: [],
);
}
/// `Do you want to save the changes?`
String get saveChanges {
return Intl.message(
@@ -2905,26 +2755,6 @@ class AppLocalizations {
);
}
/// `Are you sure you want to delete the current color?`
String get deleteColorTip {
return Intl.message(
'Are you sure you want to delete the current color?',
name: 'deleteColorTip',
desc: '',
args: [],
);
}
/// `Current color already exists`
String get colorExists {
return Intl.message(
'Current color already exists',
name: 'colorExists',
desc: '',
args: [],
);
}
/// `Color schemes`
String get colorSchemes {
return Intl.message(
@@ -3119,6 +2949,131 @@ class AppLocalizations {
String get logsTest {
return Intl.message('Logs test', name: 'logsTest', desc: '', args: []);
}
/// `{label} cannot be empty`
String emptyTip(Object label) {
return Intl.message(
'$label cannot be empty',
name: 'emptyTip',
desc: '',
args: [label],
);
}
/// `{label} must be a url`
String urlTip(Object label) {
return Intl.message(
'$label must be a url',
name: 'urlTip',
desc: '',
args: [label],
);
}
/// `proxy port must be between 1024 and 49151`
String get proxyPortTip {
return Intl.message(
'proxy port must be between 1024 and 49151',
name: 'proxyPortTip',
desc: '',
args: [],
);
}
/// `{label} must be a number`
String numberTip(Object label) {
return Intl.message(
'$label must be a number',
name: 'numberTip',
desc: '',
args: [label],
);
}
/// `Interval`
String get interval {
return Intl.message('Interval', name: 'interval', desc: '', args: []);
}
/// `Current {label} already exists`
String existsTip(Object label) {
return Intl.message(
'Current $label already exists',
name: 'existsTip',
desc: '',
args: [label],
);
}
/// `Are you sure you want to delete the current {label}?`
String deleteTip(Object label) {
return Intl.message(
'Are you sure you want to delete the current $label?',
name: 'deleteTip',
desc: '',
args: [label],
);
}
/// `Are you sure you want to delete the selected {label}?`
String deleteMultipTip(Object label) {
return Intl.message(
'Are you sure you want to delete the selected $label?',
name: 'deleteMultipTip',
desc: '',
args: [label],
);
}
/// `No {label} at the moment`
String nullTip(Object label) {
return Intl.message(
'No $label at the moment',
name: 'nullTip',
desc: '',
args: [label],
);
}
/// `Script`
String get script {
return Intl.message('Script', name: 'script', desc: '', args: []);
}
/// `Color`
String get color {
return Intl.message('Color', name: 'color', desc: '', args: []);
}
/// `Rename`
String get rename {
return Intl.message('Rename', name: 'rename', desc: '', args: []);
}
/// `Unnamed`
String get unnamed {
return Intl.message('Unnamed', name: 'unnamed', desc: '', args: []);
}
/// `Please enter a script name`
String get pleaseEnterScriptName {
return Intl.message(
'Please enter a script name',
name: 'pleaseEnterScriptName',
desc: '',
args: [],
);
}
/// `Does not take effect in script mode`
String get overrideInvalidTip {
return Intl.message(
'Does not take effect in script mode',
name: 'overrideInvalidTip',
desc: '',
args: [],
);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -5,7 +5,6 @@ import 'dart:io';
import 'dart:isolate';
import 'dart:ui';
import 'package:fl_clash/models/core.dart';
import 'package:fl_clash/plugins/app.dart';
import 'package:fl_clash/plugins/tile.dart';
import 'package:fl_clash/plugins/vpn.dart';
@@ -17,6 +16,7 @@ import 'application.dart';
import 'clash/core.dart';
import 'clash/lib.dart';
import 'common/common.dart';
import 'models/models.dart';
Future<void> main() async {
globalState.isService = false;
@@ -74,27 +74,35 @@ Future<void> _service(List<String> flags) async {
app?.tip(appLocalizations.startVpn);
final homeDirPath = await appPath.homeDirPath;
final version = await system.version;
clashLibHandler
.quickStart(
InitParams(
homeDir: homeDirPath,
version: version,
),
globalState.getUpdateConfigParams(),
globalState.getCoreState(),
)
.then(
(res) async {
if (res.isNotEmpty) {
await vpn?.stop();
exit(0);
}
await vpn?.start(
clashLibHandler.getAndroidVpnOptions(),
);
clashLibHandler.startListener();
},
final clashConfig = globalState.config.patchClashConfig.copyWith.tun(
enable: false,
);
Future(() async {
final profileId = globalState.config.currentProfileId;
if (profileId == null) {
return;
}
final params = await globalState.getSetupParams(
pathConfig: clashConfig,
);
final res = await clashLibHandler.quickStart(
InitParams(
homeDir: homeDirPath,
version: version,
),
params,
globalState.getCoreState(),
);
debugPrint(res);
if (res.isNotEmpty) {
await vpn?.stop();
exit(0);
}
await vpn?.start(
clashLibHandler.getAndroidVpnOptions(),
);
clashLibHandler.startListener();
});
}
}

View File

@@ -32,7 +32,7 @@ class _ClashContainerState extends ConsumerState<ClashManager>
void initState() {
super.initState();
clashMessage.addListener(this);
ref.listenManual(currentProfileIdProvider, (prev, next) {
ref.listenManual(needSetupProvider, (prev, next) {
if (prev != next) {
globalState.appController.handleChangeProfile();
}
@@ -42,11 +42,23 @@ class _ClashContainerState extends ConsumerState<ClashManager>
await clashCore.setState(next);
}
});
ref.listenManual(clashConfigStateProvider, (prev, next) {
ref.listenManual(updateParamsProvider, (prev, next) {
if (prev != next) {
globalState.appController.updateClashConfigDebounce();
}
});
ref.listenManual(
appSettingProvider.select((state) => state.openLogs),
(prev, next) {
if (next) {
clashCore.startLog();
} else {
clashCore.stopLog();
}
},
fireImmediately: true,
);
}
@override

View File

@@ -4,7 +4,7 @@ import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
class ConnectivityManager extends StatefulWidget {
final VoidCallback? onConnectivityChanged;
final Function(List<ConnectivityResult> results)? onConnectivityChanged;
final Widget child;
const ConnectivityManager({
@@ -23,9 +23,9 @@ class _ConnectivityManagerState extends State<ConnectivityManager> {
@override
void initState() {
super.initState();
subscription = Connectivity().onConnectivityChanged.listen((_) async {
subscription = Connectivity().onConnectivityChanged.listen((results) async {
if (widget.onConnectivityChanged != null) {
widget.onConnectivityChanged!();
widget.onConnectivityChanged!(results);
}
});
}

View File

@@ -39,6 +39,7 @@ class MessageManagerState extends State<MessageManager> {
id: utils.uuidV4,
text: text,
);
commonPrint.log(text);
_bufferMessages.add(commonMessage);
await _showMessage();
}
@@ -87,32 +88,32 @@ class MessageManagerState extends State<MessageManager> {
child: messages.isEmpty
? SizedBox()
: LayoutBuilder(
key: Key(messages.last.id),
builder: (_, constraints) {
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12.0),
),
key: Key(messages.last.id),
builder: (_, constraints) {
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12.0),
),
),
elevation: 10,
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(
constraints.maxWidth,
500,
),
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: Text(
messages.last.text,
),
),
);
},
),
elevation: 10,
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(
constraints.maxWidth,
500,
),
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: Text(
messages.last.text,
),
),
);
},
),
);
},
),

View File

@@ -32,7 +32,6 @@ class AppState with _$AppState {
required FixedList<Traffic> traffics,
required Traffic totalTraffic,
@Default("") String proxiesQuery,
@Default(false) bool needApply,
}) = _AppState;
}

View File

@@ -1,12 +1,10 @@
// ignore_for_file: invalid_annotation_target
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../enum/enum.dart';
part 'generated/clash_config.freezed.dart';
part 'generated/clash_config.g.dart';
typedef HostsMap = Map<String, String>;
@@ -142,6 +140,41 @@ class RuleProvider with _$RuleProvider {
_$RuleProviderFromJson(json);
}
@freezed
class Sniffer with _$Sniffer {
const factory Sniffer({
@Default(false) bool enable,
@Default(true) @JsonKey(name: "override-destination") bool overrideDest,
@Default([]) List<String> sniffing,
@Default([]) @JsonKey(name: "force-domain") List<String> forceDomain,
@Default([]) @JsonKey(name: "skip-src-address") List<String> skipSrcAddress,
@Default([]) @JsonKey(name: "skip-dst-address") List<String> skipDstAddress,
@Default([]) @JsonKey(name: "skip-domain") List<String> skipDomain,
@Default([]) @JsonKey(name: "port-whitelist") List<String> port,
@Default(true) @JsonKey(name: "force-dns-mapping") bool forceDnsMapping,
@Default(true) @JsonKey(name: "parse-pure-ip") bool parsePureIp,
@Default({}) Map<String, SnifferConfig> sniff,
}) = _Sniffer;
factory Sniffer.fromJson(Map<String, Object?> json) =>
_$SnifferFromJson(json);
}
List<String> _formJsonPorts(List? ports) {
return ports?.map((item) => item.toString()).toList() ?? [];
}
@freezed
class SnifferConfig with _$SnifferConfig {
const factory SnifferConfig({
@Default([]) @JsonKey(fromJson: _formJsonPorts) List<String> ports,
@JsonKey(name: "override-destination") bool? overrideDest,
}) = _SnifferConfig;
factory SnifferConfig.fromJson(Map<String, Object?> json) =>
_$SnifferConfigFromJson(json);
}
@freezed
class Tun with _$Tun {
const factory Tun({
@@ -408,7 +441,7 @@ List<SubRule> _genSubRules(Map<String, dynamic> json) {
class ClashConfigSnippet with _$ClashConfigSnippet {
const factory ClashConfigSnippet({
@Default([]) @JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule) @Default([]) List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") @Default([]) List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
@Default([])
List<RuleProvider> ruleProvider,

View File

@@ -16,7 +16,7 @@ class NavigationItem with _$NavigationItem {
required Icon icon,
required PageLabel label,
final String? description,
required Widget fragment,
required Widget view,
@Default(true) bool keep,
String? path,
@Default([NavigationItemMode.mobile, NavigationItemMode.desktop])
@@ -527,3 +527,52 @@ class TextPainterParams with _$TextPainterParams {
class CloseWindowIntent extends Intent {
const CloseWindowIntent();
}
@freezed
class Result<T> with _$Result<T> {
const factory Result({
required T? data,
required ResultType type,
required String message,
}) = _Result;
factory Result.success(T data) => Result(
data: data,
type: ResultType.success,
message: "",
);
factory Result.error(String message) => Result(
data: null,
type: ResultType.error,
message: message,
);
}
extension ResultExt on Result {
bool get isError => type == ResultType.error;
bool get isSuccess => type == ResultType.success;
}
@freezed
class Script with _$Script {
const factory Script({
required String id,
required String label,
required String content,
}) = _Script;
factory Script.create({
required String label,
required String content,
}) {
return Script(
id: utils.uuidV4,
label: label,
content: content,
);
}
factory Script.fromJson(Map<String, Object?> json) => _$ScriptFromJson(json);
}

View File

@@ -8,7 +8,6 @@ import 'package:freezed_annotation/freezed_annotation.dart';
import 'models.dart';
part 'generated/config.freezed.dart';
part 'generated/config.g.dart';
const defaultBypassDomain = [
@@ -212,6 +211,35 @@ class ThemeProps with _$ThemeProps {
}
}
@freezed
class ScriptProps with _$ScriptProps {
const factory ScriptProps({
String? currentId,
@Default([]) List<Script> scripts,
}) = _ScriptProps;
factory ScriptProps.fromJson(Map<String, Object?> json) =>
_$ScriptPropsFromJson(json);
}
extension ScriptPropsExt on ScriptProps {
String? get realId {
final index = scripts.indexWhere((script) => script.id == currentId);
if (index != -1) {
return currentId;
}
return null;
}
Script? get currentScript {
final index = scripts.indexWhere((script) => script.id == currentId);
if (index != -1) {
return scripts[index];
}
return null;
}
}
@freezed
class Config with _$Config {
const factory Config({
@@ -229,6 +257,7 @@ class Config with _$Config {
@Default(defaultProxiesStyle) ProxiesStyle proxiesStyle,
@Default(defaultWindowProps) WindowProps windowProps,
@Default(defaultClashConfig) ClashConfig patchClashConfig,
@Default(ScriptProps()) ScriptProps scriptProps,
}) = _Config;
factory Config.fromJson(Map<String, Object?> json) => _$ConfigFromJson(json);

View File

@@ -5,7 +5,6 @@ import 'package:fl_clash/models/models.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
part 'generated/core.freezed.dart';
part 'generated/core.g.dart';
abstract mixin class AppMessageListener {
@@ -18,10 +17,52 @@ abstract mixin class AppMessageListener {
void onLoaded(String providerName) {}
}
abstract mixin class ServiceMessageListener {
onProtect(Fd fd) {}
// abstract mixin class ServiceMessageListener {
// onProtect(Fd fd) {}
//
// onProcess(ProcessData process) {}
// }
onProcess(ProcessData process) {}
@freezed
class SetupParams with _$SetupParams {
const factory SetupParams({
@JsonKey(name: "config") required Map<String, dynamic> config,
@JsonKey(name: "selected-map") required Map<String, String> selectedMap,
@JsonKey(name: "test-url") required String testUrl,
}) = _SetupParams;
factory SetupParams.fromJson(Map<String, dynamic> json) =>
_$SetupParamsFromJson(json);
}
// extension SetupParamsExt on SetupParams {
// Map<String, dynamic> get json {
// final json = Map<String, dynamic>.from(config);
// json["selected-map"] = selectedMap;
// json["test-url"] = testUrl;
// return json;
// }
// }
@freezed
class UpdateParams with _$UpdateParams {
const factory UpdateParams({
required Tun tun,
@JsonKey(name: 'mixed-port') required int mixedPort,
@JsonKey(name: 'allow-lan') required bool allowLan,
@JsonKey(name: 'find-process-mode')
required FindProcessMode findProcessMode,
required Mode mode,
@JsonKey(name: 'log-level') required LogLevel logLevel,
required bool ipv6,
@JsonKey(name: 'tcp-concurrent') required bool tcpConcurrent,
@JsonKey(name: 'external-controller')
required ExternalControllerStatus externalController,
@JsonKey(name: 'unified-delay') required bool unifiedDelay,
}) = _UpdateParams;
factory UpdateParams.fromJson(Map<String, dynamic> json) =>
_$UpdateParamsFromJson(json);
}
@freezed
@@ -56,32 +97,6 @@ class AndroidVpnOptions with _$AndroidVpnOptions {
_$AndroidVpnOptionsFromJson(json);
}
@freezed
class ConfigExtendedParams with _$ConfigExtendedParams {
const factory ConfigExtendedParams({
@JsonKey(name: "is-patch") required bool isPatch,
@JsonKey(name: "selected-map") required SelectedMap selectedMap,
@JsonKey(name: "override-dns") required bool overrideDns,
@JsonKey(name: "override-rule") required bool overrideRule,
@JsonKey(name: "test-url") required String testUrl,
}) = _ConfigExtendedParams;
factory ConfigExtendedParams.fromJson(Map<String, Object?> json) =>
_$ConfigExtendedParamsFromJson(json);
}
@freezed
class UpdateConfigParams with _$UpdateConfigParams {
const factory UpdateConfigParams({
@JsonKey(name: "profile-id") required String profileId,
required ClashConfig config,
required ConfigExtendedParams params,
}) = _UpdateConfigParams;
factory UpdateConfigParams.fromJson(Map<String, Object?> json) =>
_$UpdateConfigParamsFromJson(json);
}
@freezed
class InitParams with _$InitParams {
const factory InitParams({
@@ -158,37 +173,26 @@ class Now with _$Now {
factory Now.fromJson(Map<String, Object?> json) => _$NowFromJson(json);
}
@freezed
class ProcessData with _$ProcessData {
const factory ProcessData({
required String id,
required Metadata metadata,
}) = _ProcessData;
factory ProcessData.fromJson(Map<String, Object?> json) =>
_$ProcessDataFromJson(json);
}
@freezed
class Fd with _$Fd {
const factory Fd({
required String id,
required int value,
}) = _Fd;
factory Fd.fromJson(Map<String, Object?> json) => _$FdFromJson(json);
}
@freezed
class ProcessMapItem with _$ProcessMapItem {
const factory ProcessMapItem({
required String id,
required String value,
}) = _ProcessMapItem;
factory ProcessMapItem.fromJson(Map<String, Object?> json) =>
_$ProcessMapItemFromJson(json);
}
// @freezed
// class ProcessData with _$ProcessData {
// const factory ProcessData({
// required String id,
// required Metadata metadata,
// }) = _ProcessData;
//
// factory ProcessData.fromJson(Map<String, Object?> json) =>
// _$ProcessDataFromJson(json);
// }
//
// @freezed
// class Fd with _$Fd {
// const factory Fd({
// required String id,
// required int value,
// }) = _Fd;
//
// factory Fd.fromJson(Map<String, Object?> json) => _$FdFromJson(json);
// }
@freezed
class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {
@@ -231,28 +235,11 @@ class ExternalProvider with _$ExternalProvider {
_$ExternalProviderFromJson(json);
}
@freezed
class TunProps with _$TunProps {
const factory TunProps({
required int fd,
required String gateway,
required String gateway6,
required String portal,
required String portal6,
required String dns,
required String dns6,
}) = _TunProps;
factory TunProps.fromJson(Map<String, Object?> json) =>
_$TunPropsFromJson(json);
}
@freezed
class Action with _$Action {
const factory Action({
required ActionMethod method,
required dynamic data,
@JsonKey(name: "default-value") required dynamic defaultValue,
required String id,
}) = _Action;
@@ -265,8 +252,19 @@ class ActionResult with _$ActionResult {
required ActionMethod method,
required dynamic data,
String? id,
@Default(ResultType.success) ResultType code,
}) = _ActionResult;
factory ActionResult.fromJson(Map<String, Object?> json) =>
_$ActionResultFromJson(json);
}
extension ActionResultExt on ActionResult {
Result get toResult {
if (code == ResultType.success) {
return Result.success(data);
} else {
return Result.error(data);
}
}
}

View File

@@ -36,7 +36,6 @@ mixin _$AppState {
FixedList<Traffic> get traffics => throw _privateConstructorUsedError;
Traffic get totalTraffic => throw _privateConstructorUsedError;
String get proxiesQuery => throw _privateConstructorUsedError;
bool get needApply => throw _privateConstructorUsedError;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@@ -69,8 +68,7 @@ abstract class $AppStateCopyWith<$Res> {
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
String proxiesQuery,
bool needApply});
String proxiesQuery});
}
/// @nodoc
@@ -107,7 +105,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
Object? traffics = null,
Object? totalTraffic = null,
Object? proxiesQuery = null,
Object? needApply = null,
}) {
return _then(_value.copyWith(
isInit: null == isInit
@@ -186,10 +183,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
? _value.proxiesQuery
: proxiesQuery // ignore: cast_nullable_to_non_nullable
as String,
needApply: null == needApply
? _value.needApply
: needApply // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -221,8 +214,7 @@ abstract class _$$AppStateImplCopyWith<$Res>
FixedList<Log> logs,
FixedList<Traffic> traffics,
Traffic totalTraffic,
String proxiesQuery,
bool needApply});
String proxiesQuery});
}
/// @nodoc
@@ -257,7 +249,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
Object? traffics = null,
Object? totalTraffic = null,
Object? proxiesQuery = null,
Object? needApply = null,
}) {
return _then(_$AppStateImpl(
isInit: null == isInit
@@ -336,10 +327,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
? _value.proxiesQuery
: proxiesQuery // ignore: cast_nullable_to_non_nullable
as String,
needApply: null == needApply
? _value.needApply
: needApply // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -366,8 +353,7 @@ class _$AppStateImpl implements _AppState {
required this.logs,
required this.traffics,
required this.totalTraffic,
this.proxiesQuery = "",
this.needApply = false})
this.proxiesQuery = ""})
: _packages = packages,
_delayMap = delayMap,
_groups = groups,
@@ -445,13 +431,10 @@ class _$AppStateImpl implements _AppState {
@override
@JsonKey()
final String proxiesQuery;
@override
@JsonKey()
final bool needApply;
@override
String toString() {
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, proxiesQuery: $proxiesQuery, needApply: $needApply)';
return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, proxiesQuery: $proxiesQuery)';
}
@override
@@ -487,9 +470,7 @@ class _$AppStateImpl implements _AppState {
(identical(other.totalTraffic, totalTraffic) ||
other.totalTraffic == totalTraffic) &&
(identical(other.proxiesQuery, proxiesQuery) ||
other.proxiesQuery == proxiesQuery) &&
(identical(other.needApply, needApply) ||
other.needApply == needApply));
other.proxiesQuery == proxiesQuery));
}
@override
@@ -513,8 +494,7 @@ class _$AppStateImpl implements _AppState {
logs,
traffics,
totalTraffic,
proxiesQuery,
needApply
proxiesQuery
]);
/// Create a copy of AppState
@@ -546,8 +526,7 @@ abstract class _AppState implements AppState {
required final FixedList<Log> logs,
required final FixedList<Traffic> traffics,
required final Traffic totalTraffic,
final String proxiesQuery,
final bool needApply}) = _$AppStateImpl;
final String proxiesQuery}) = _$AppStateImpl;
@override
bool get isInit;
@@ -587,8 +566,6 @@ abstract class _AppState implements AppState {
Traffic get totalTraffic;
@override
String get proxiesQuery;
@override
bool get needApply;
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.

View File

@@ -652,6 +652,640 @@ abstract class _RuleProvider implements RuleProvider {
throw _privateConstructorUsedError;
}
Sniffer _$SnifferFromJson(Map<String, dynamic> json) {
return _Sniffer.fromJson(json);
}
/// @nodoc
mixin _$Sniffer {
bool get enable => throw _privateConstructorUsedError;
@JsonKey(name: "override-destination")
bool get overrideDest => throw _privateConstructorUsedError;
List<String> get sniffing => throw _privateConstructorUsedError;
@JsonKey(name: "force-domain")
List<String> get forceDomain => throw _privateConstructorUsedError;
@JsonKey(name: "skip-src-address")
List<String> get skipSrcAddress => throw _privateConstructorUsedError;
@JsonKey(name: "skip-dst-address")
List<String> get skipDstAddress => throw _privateConstructorUsedError;
@JsonKey(name: "skip-domain")
List<String> get skipDomain => throw _privateConstructorUsedError;
@JsonKey(name: "port-whitelist")
List<String> get port => throw _privateConstructorUsedError;
@JsonKey(name: "force-dns-mapping")
bool get forceDnsMapping => throw _privateConstructorUsedError;
@JsonKey(name: "parse-pure-ip")
bool get parsePureIp => throw _privateConstructorUsedError;
Map<String, SnifferConfig> get sniff => throw _privateConstructorUsedError;
/// Serializes this Sniffer to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Sniffer
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SnifferCopyWith<Sniffer> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SnifferCopyWith<$Res> {
factory $SnifferCopyWith(Sniffer value, $Res Function(Sniffer) then) =
_$SnifferCopyWithImpl<$Res, Sniffer>;
@useResult
$Res call(
{bool enable,
@JsonKey(name: "override-destination") bool overrideDest,
List<String> sniffing,
@JsonKey(name: "force-domain") List<String> forceDomain,
@JsonKey(name: "skip-src-address") List<String> skipSrcAddress,
@JsonKey(name: "skip-dst-address") List<String> skipDstAddress,
@JsonKey(name: "skip-domain") List<String> skipDomain,
@JsonKey(name: "port-whitelist") List<String> port,
@JsonKey(name: "force-dns-mapping") bool forceDnsMapping,
@JsonKey(name: "parse-pure-ip") bool parsePureIp,
Map<String, SnifferConfig> sniff});
}
/// @nodoc
class _$SnifferCopyWithImpl<$Res, $Val extends Sniffer>
implements $SnifferCopyWith<$Res> {
_$SnifferCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Sniffer
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = null,
Object? overrideDest = null,
Object? sniffing = null,
Object? forceDomain = null,
Object? skipSrcAddress = null,
Object? skipDstAddress = null,
Object? skipDomain = null,
Object? port = null,
Object? forceDnsMapping = null,
Object? parsePureIp = null,
Object? sniff = null,
}) {
return _then(_value.copyWith(
enable: null == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as bool,
overrideDest: null == overrideDest
? _value.overrideDest
: overrideDest // ignore: cast_nullable_to_non_nullable
as bool,
sniffing: null == sniffing
? _value.sniffing
: sniffing // ignore: cast_nullable_to_non_nullable
as List<String>,
forceDomain: null == forceDomain
? _value.forceDomain
: forceDomain // ignore: cast_nullable_to_non_nullable
as List<String>,
skipSrcAddress: null == skipSrcAddress
? _value.skipSrcAddress
: skipSrcAddress // ignore: cast_nullable_to_non_nullable
as List<String>,
skipDstAddress: null == skipDstAddress
? _value.skipDstAddress
: skipDstAddress // ignore: cast_nullable_to_non_nullable
as List<String>,
skipDomain: null == skipDomain
? _value.skipDomain
: skipDomain // ignore: cast_nullable_to_non_nullable
as List<String>,
port: null == port
? _value.port
: port // ignore: cast_nullable_to_non_nullable
as List<String>,
forceDnsMapping: null == forceDnsMapping
? _value.forceDnsMapping
: forceDnsMapping // ignore: cast_nullable_to_non_nullable
as bool,
parsePureIp: null == parsePureIp
? _value.parsePureIp
: parsePureIp // ignore: cast_nullable_to_non_nullable
as bool,
sniff: null == sniff
? _value.sniff
: sniff // ignore: cast_nullable_to_non_nullable
as Map<String, SnifferConfig>,
) as $Val);
}
}
/// @nodoc
abstract class _$$SnifferImplCopyWith<$Res> implements $SnifferCopyWith<$Res> {
factory _$$SnifferImplCopyWith(
_$SnifferImpl value, $Res Function(_$SnifferImpl) then) =
__$$SnifferImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{bool enable,
@JsonKey(name: "override-destination") bool overrideDest,
List<String> sniffing,
@JsonKey(name: "force-domain") List<String> forceDomain,
@JsonKey(name: "skip-src-address") List<String> skipSrcAddress,
@JsonKey(name: "skip-dst-address") List<String> skipDstAddress,
@JsonKey(name: "skip-domain") List<String> skipDomain,
@JsonKey(name: "port-whitelist") List<String> port,
@JsonKey(name: "force-dns-mapping") bool forceDnsMapping,
@JsonKey(name: "parse-pure-ip") bool parsePureIp,
Map<String, SnifferConfig> sniff});
}
/// @nodoc
class __$$SnifferImplCopyWithImpl<$Res>
extends _$SnifferCopyWithImpl<$Res, _$SnifferImpl>
implements _$$SnifferImplCopyWith<$Res> {
__$$SnifferImplCopyWithImpl(
_$SnifferImpl _value, $Res Function(_$SnifferImpl) _then)
: super(_value, _then);
/// Create a copy of Sniffer
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = null,
Object? overrideDest = null,
Object? sniffing = null,
Object? forceDomain = null,
Object? skipSrcAddress = null,
Object? skipDstAddress = null,
Object? skipDomain = null,
Object? port = null,
Object? forceDnsMapping = null,
Object? parsePureIp = null,
Object? sniff = null,
}) {
return _then(_$SnifferImpl(
enable: null == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as bool,
overrideDest: null == overrideDest
? _value.overrideDest
: overrideDest // ignore: cast_nullable_to_non_nullable
as bool,
sniffing: null == sniffing
? _value._sniffing
: sniffing // ignore: cast_nullable_to_non_nullable
as List<String>,
forceDomain: null == forceDomain
? _value._forceDomain
: forceDomain // ignore: cast_nullable_to_non_nullable
as List<String>,
skipSrcAddress: null == skipSrcAddress
? _value._skipSrcAddress
: skipSrcAddress // ignore: cast_nullable_to_non_nullable
as List<String>,
skipDstAddress: null == skipDstAddress
? _value._skipDstAddress
: skipDstAddress // ignore: cast_nullable_to_non_nullable
as List<String>,
skipDomain: null == skipDomain
? _value._skipDomain
: skipDomain // ignore: cast_nullable_to_non_nullable
as List<String>,
port: null == port
? _value._port
: port // ignore: cast_nullable_to_non_nullable
as List<String>,
forceDnsMapping: null == forceDnsMapping
? _value.forceDnsMapping
: forceDnsMapping // ignore: cast_nullable_to_non_nullable
as bool,
parsePureIp: null == parsePureIp
? _value.parsePureIp
: parsePureIp // ignore: cast_nullable_to_non_nullable
as bool,
sniff: null == sniff
? _value._sniff
: sniff // ignore: cast_nullable_to_non_nullable
as Map<String, SnifferConfig>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SnifferImpl implements _Sniffer {
const _$SnifferImpl(
{this.enable = false,
@JsonKey(name: "override-destination") this.overrideDest = true,
final List<String> sniffing = const [],
@JsonKey(name: "force-domain") final List<String> forceDomain = const [],
@JsonKey(name: "skip-src-address")
final List<String> skipSrcAddress = const [],
@JsonKey(name: "skip-dst-address")
final List<String> skipDstAddress = const [],
@JsonKey(name: "skip-domain") final List<String> skipDomain = const [],
@JsonKey(name: "port-whitelist") final List<String> port = const [],
@JsonKey(name: "force-dns-mapping") this.forceDnsMapping = true,
@JsonKey(name: "parse-pure-ip") this.parsePureIp = true,
final Map<String, SnifferConfig> sniff = const {}})
: _sniffing = sniffing,
_forceDomain = forceDomain,
_skipSrcAddress = skipSrcAddress,
_skipDstAddress = skipDstAddress,
_skipDomain = skipDomain,
_port = port,
_sniff = sniff;
factory _$SnifferImpl.fromJson(Map<String, dynamic> json) =>
_$$SnifferImplFromJson(json);
@override
@JsonKey()
final bool enable;
@override
@JsonKey(name: "override-destination")
final bool overrideDest;
final List<String> _sniffing;
@override
@JsonKey()
List<String> get sniffing {
if (_sniffing is EqualUnmodifiableListView) return _sniffing;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_sniffing);
}
final List<String> _forceDomain;
@override
@JsonKey(name: "force-domain")
List<String> get forceDomain {
if (_forceDomain is EqualUnmodifiableListView) return _forceDomain;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_forceDomain);
}
final List<String> _skipSrcAddress;
@override
@JsonKey(name: "skip-src-address")
List<String> get skipSrcAddress {
if (_skipSrcAddress is EqualUnmodifiableListView) return _skipSrcAddress;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_skipSrcAddress);
}
final List<String> _skipDstAddress;
@override
@JsonKey(name: "skip-dst-address")
List<String> get skipDstAddress {
if (_skipDstAddress is EqualUnmodifiableListView) return _skipDstAddress;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_skipDstAddress);
}
final List<String> _skipDomain;
@override
@JsonKey(name: "skip-domain")
List<String> get skipDomain {
if (_skipDomain is EqualUnmodifiableListView) return _skipDomain;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_skipDomain);
}
final List<String> _port;
@override
@JsonKey(name: "port-whitelist")
List<String> get port {
if (_port is EqualUnmodifiableListView) return _port;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_port);
}
@override
@JsonKey(name: "force-dns-mapping")
final bool forceDnsMapping;
@override
@JsonKey(name: "parse-pure-ip")
final bool parsePureIp;
final Map<String, SnifferConfig> _sniff;
@override
@JsonKey()
Map<String, SnifferConfig> get sniff {
if (_sniff is EqualUnmodifiableMapView) return _sniff;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_sniff);
}
@override
String toString() {
return 'Sniffer(enable: $enable, overrideDest: $overrideDest, sniffing: $sniffing, forceDomain: $forceDomain, skipSrcAddress: $skipSrcAddress, skipDstAddress: $skipDstAddress, skipDomain: $skipDomain, port: $port, forceDnsMapping: $forceDnsMapping, parsePureIp: $parsePureIp, sniff: $sniff)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SnifferImpl &&
(identical(other.enable, enable) || other.enable == enable) &&
(identical(other.overrideDest, overrideDest) ||
other.overrideDest == overrideDest) &&
const DeepCollectionEquality().equals(other._sniffing, _sniffing) &&
const DeepCollectionEquality()
.equals(other._forceDomain, _forceDomain) &&
const DeepCollectionEquality()
.equals(other._skipSrcAddress, _skipSrcAddress) &&
const DeepCollectionEquality()
.equals(other._skipDstAddress, _skipDstAddress) &&
const DeepCollectionEquality()
.equals(other._skipDomain, _skipDomain) &&
const DeepCollectionEquality().equals(other._port, _port) &&
(identical(other.forceDnsMapping, forceDnsMapping) ||
other.forceDnsMapping == forceDnsMapping) &&
(identical(other.parsePureIp, parsePureIp) ||
other.parsePureIp == parsePureIp) &&
const DeepCollectionEquality().equals(other._sniff, _sniff));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
enable,
overrideDest,
const DeepCollectionEquality().hash(_sniffing),
const DeepCollectionEquality().hash(_forceDomain),
const DeepCollectionEquality().hash(_skipSrcAddress),
const DeepCollectionEquality().hash(_skipDstAddress),
const DeepCollectionEquality().hash(_skipDomain),
const DeepCollectionEquality().hash(_port),
forceDnsMapping,
parsePureIp,
const DeepCollectionEquality().hash(_sniff));
/// Create a copy of Sniffer
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SnifferImplCopyWith<_$SnifferImpl> get copyWith =>
__$$SnifferImplCopyWithImpl<_$SnifferImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SnifferImplToJson(
this,
);
}
}
abstract class _Sniffer implements Sniffer {
const factory _Sniffer(
{final bool enable,
@JsonKey(name: "override-destination") final bool overrideDest,
final List<String> sniffing,
@JsonKey(name: "force-domain") final List<String> forceDomain,
@JsonKey(name: "skip-src-address") final List<String> skipSrcAddress,
@JsonKey(name: "skip-dst-address") final List<String> skipDstAddress,
@JsonKey(name: "skip-domain") final List<String> skipDomain,
@JsonKey(name: "port-whitelist") final List<String> port,
@JsonKey(name: "force-dns-mapping") final bool forceDnsMapping,
@JsonKey(name: "parse-pure-ip") final bool parsePureIp,
final Map<String, SnifferConfig> sniff}) = _$SnifferImpl;
factory _Sniffer.fromJson(Map<String, dynamic> json) = _$SnifferImpl.fromJson;
@override
bool get enable;
@override
@JsonKey(name: "override-destination")
bool get overrideDest;
@override
List<String> get sniffing;
@override
@JsonKey(name: "force-domain")
List<String> get forceDomain;
@override
@JsonKey(name: "skip-src-address")
List<String> get skipSrcAddress;
@override
@JsonKey(name: "skip-dst-address")
List<String> get skipDstAddress;
@override
@JsonKey(name: "skip-domain")
List<String> get skipDomain;
@override
@JsonKey(name: "port-whitelist")
List<String> get port;
@override
@JsonKey(name: "force-dns-mapping")
bool get forceDnsMapping;
@override
@JsonKey(name: "parse-pure-ip")
bool get parsePureIp;
@override
Map<String, SnifferConfig> get sniff;
/// Create a copy of Sniffer
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SnifferImplCopyWith<_$SnifferImpl> get copyWith =>
throw _privateConstructorUsedError;
}
SnifferConfig _$SnifferConfigFromJson(Map<String, dynamic> json) {
return _SnifferConfig.fromJson(json);
}
/// @nodoc
mixin _$SnifferConfig {
@JsonKey(fromJson: _formJsonPorts)
List<String> get ports => throw _privateConstructorUsedError;
@JsonKey(name: "override-destination")
bool? get overrideDest => throw _privateConstructorUsedError;
/// Serializes this SnifferConfig to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of SnifferConfig
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$SnifferConfigCopyWith<SnifferConfig> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $SnifferConfigCopyWith<$Res> {
factory $SnifferConfigCopyWith(
SnifferConfig value, $Res Function(SnifferConfig) then) =
_$SnifferConfigCopyWithImpl<$Res, SnifferConfig>;
@useResult
$Res call(
{@JsonKey(fromJson: _formJsonPorts) List<String> ports,
@JsonKey(name: "override-destination") bool? overrideDest});
}
/// @nodoc
class _$SnifferConfigCopyWithImpl<$Res, $Val extends SnifferConfig>
implements $SnifferConfigCopyWith<$Res> {
_$SnifferConfigCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of SnifferConfig
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? ports = null,
Object? overrideDest = freezed,
}) {
return _then(_value.copyWith(
ports: null == ports
? _value.ports
: ports // ignore: cast_nullable_to_non_nullable
as List<String>,
overrideDest: freezed == overrideDest
? _value.overrideDest
: overrideDest // ignore: cast_nullable_to_non_nullable
as bool?,
) as $Val);
}
}
/// @nodoc
abstract class _$$SnifferConfigImplCopyWith<$Res>
implements $SnifferConfigCopyWith<$Res> {
factory _$$SnifferConfigImplCopyWith(
_$SnifferConfigImpl value, $Res Function(_$SnifferConfigImpl) then) =
__$$SnifferConfigImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(fromJson: _formJsonPorts) List<String> ports,
@JsonKey(name: "override-destination") bool? overrideDest});
}
/// @nodoc
class __$$SnifferConfigImplCopyWithImpl<$Res>
extends _$SnifferConfigCopyWithImpl<$Res, _$SnifferConfigImpl>
implements _$$SnifferConfigImplCopyWith<$Res> {
__$$SnifferConfigImplCopyWithImpl(
_$SnifferConfigImpl _value, $Res Function(_$SnifferConfigImpl) _then)
: super(_value, _then);
/// Create a copy of SnifferConfig
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? ports = null,
Object? overrideDest = freezed,
}) {
return _then(_$SnifferConfigImpl(
ports: null == ports
? _value._ports
: ports // ignore: cast_nullable_to_non_nullable
as List<String>,
overrideDest: freezed == overrideDest
? _value.overrideDest
: overrideDest // ignore: cast_nullable_to_non_nullable
as bool?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$SnifferConfigImpl implements _SnifferConfig {
const _$SnifferConfigImpl(
{@JsonKey(fromJson: _formJsonPorts) final List<String> ports = const [],
@JsonKey(name: "override-destination") this.overrideDest})
: _ports = ports;
factory _$SnifferConfigImpl.fromJson(Map<String, dynamic> json) =>
_$$SnifferConfigImplFromJson(json);
final List<String> _ports;
@override
@JsonKey(fromJson: _formJsonPorts)
List<String> get ports {
if (_ports is EqualUnmodifiableListView) return _ports;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_ports);
}
@override
@JsonKey(name: "override-destination")
final bool? overrideDest;
@override
String toString() {
return 'SnifferConfig(ports: $ports, overrideDest: $overrideDest)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$SnifferConfigImpl &&
const DeepCollectionEquality().equals(other._ports, _ports) &&
(identical(other.overrideDest, overrideDest) ||
other.overrideDest == overrideDest));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_ports), overrideDest);
/// Create a copy of SnifferConfig
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$SnifferConfigImplCopyWith<_$SnifferConfigImpl> get copyWith =>
__$$SnifferConfigImplCopyWithImpl<_$SnifferConfigImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$SnifferConfigImplToJson(
this,
);
}
}
abstract class _SnifferConfig implements SnifferConfig {
const factory _SnifferConfig(
{@JsonKey(fromJson: _formJsonPorts) final List<String> ports,
@JsonKey(name: "override-destination") final bool? overrideDest}) =
_$SnifferConfigImpl;
factory _SnifferConfig.fromJson(Map<String, dynamic> json) =
_$SnifferConfigImpl.fromJson;
@override
@JsonKey(fromJson: _formJsonPorts)
List<String> get ports;
@override
@JsonKey(name: "override-destination")
bool? get overrideDest;
/// Create a copy of SnifferConfig
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$SnifferConfigImplCopyWith<_$SnifferConfigImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Tun _$TunFromJson(Map<String, dynamic> json) {
return _Tun.fromJson(json);
}
@@ -2582,7 +3216,7 @@ ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) {
mixin _$ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups => throw _privateConstructorUsedError;
@JsonKey(fromJson: _genRule)
@JsonKey(fromJson: _genRule, name: "rules")
List<Rule> get rule => throw _privateConstructorUsedError;
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> get ruleProvider => throw _privateConstructorUsedError;
@@ -2607,7 +3241,7 @@ abstract class $ClashConfigSnippetCopyWith<$Res> {
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule) List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -2665,7 +3299,7 @@ abstract class _$$ClashConfigSnippetImplCopyWith<$Res>
@useResult
$Res call(
{@JsonKey(name: "proxy-groups") List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule) List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -2717,7 +3351,8 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
const _$ClashConfigSnippetImpl(
{@JsonKey(name: "proxy-groups")
final List<ProxyGroup> proxyGroups = const [],
@JsonKey(fromJson: _genRule) final List<Rule> rule = const [],
@JsonKey(fromJson: _genRule, name: "rules")
final List<Rule> rule = const [],
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
final List<RuleProvider> ruleProvider = const [],
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -2741,7 +3376,7 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
final List<Rule> _rule;
@override
@JsonKey(fromJson: _genRule)
@JsonKey(fromJson: _genRule, name: "rules")
List<Rule> get rule {
if (_rule is EqualUnmodifiableListView) return _rule;
// ignore: implicit_dynamic_type
@@ -2813,7 +3448,7 @@ class _$ClashConfigSnippetImpl implements _ClashConfigSnippet {
abstract class _ClashConfigSnippet implements ClashConfigSnippet {
const factory _ClashConfigSnippet(
{@JsonKey(name: "proxy-groups") final List<ProxyGroup> proxyGroups,
@JsonKey(fromJson: _genRule) final List<Rule> rule,
@JsonKey(fromJson: _genRule, name: "rules") final List<Rule> rule,
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)
final List<RuleProvider> ruleProvider,
@JsonKey(name: "sub-rules", fromJson: _genSubRules)
@@ -2826,7 +3461,7 @@ abstract class _ClashConfigSnippet implements ClashConfigSnippet {
@JsonKey(name: "proxy-groups")
List<ProxyGroup> get proxyGroups;
@override
@JsonKey(fromJson: _genRule)
@JsonKey(fromJson: _genRule, name: "rules")
List<Rule> get rule;
@override
@JsonKey(name: "rule-providers", fromJson: _genRuleProviders)

View File

@@ -63,6 +63,72 @@ Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
'name': instance.name,
};
_$SnifferImpl _$$SnifferImplFromJson(Map<String, dynamic> json) =>
_$SnifferImpl(
enable: json['enable'] as bool? ?? false,
overrideDest: json['override-destination'] as bool? ?? true,
sniffing: (json['sniffing'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
forceDomain: (json['force-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipSrcAddress: (json['skip-src-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipDstAddress: (json['skip-dst-address'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
skipDomain: (json['skip-domain'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
port: (json['port-whitelist'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
const [],
forceDnsMapping: json['force-dns-mapping'] as bool? ?? true,
parsePureIp: json['parse-pure-ip'] as bool? ?? true,
sniff: (json['sniff'] as Map<String, dynamic>?)?.map(
(k, e) =>
MapEntry(k, SnifferConfig.fromJson(e as Map<String, dynamic>)),
) ??
const {},
);
Map<String, dynamic> _$$SnifferImplToJson(_$SnifferImpl instance) =>
<String, dynamic>{
'enable': instance.enable,
'override-destination': instance.overrideDest,
'sniffing': instance.sniffing,
'force-domain': instance.forceDomain,
'skip-src-address': instance.skipSrcAddress,
'skip-dst-address': instance.skipDstAddress,
'skip-domain': instance.skipDomain,
'port-whitelist': instance.port,
'force-dns-mapping': instance.forceDnsMapping,
'parse-pure-ip': instance.parsePureIp,
'sniff': instance.sniff,
};
_$SnifferConfigImpl _$$SnifferConfigImplFromJson(Map<String, dynamic> json) =>
_$SnifferConfigImpl(
ports: json['ports'] == null
? const []
: _formJsonPorts(json['ports'] as List?),
overrideDest: json['override-destination'] as bool?,
);
Map<String, dynamic> _$$SnifferConfigImplToJson(_$SnifferConfigImpl instance) =>
<String, dynamic>{
'ports': instance.ports,
'override-destination': instance.overrideDest,
};
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName,
@@ -246,7 +312,7 @@ _$ClashConfigSnippetImpl _$$ClashConfigSnippetImplFromJson(
?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
rule: json['rule'] == null ? const [] : _genRule(json['rule'] as List?),
rule: json['rules'] == null ? const [] : _genRule(json['rules'] as List?),
ruleProvider: json['rule-providers'] == null
? const []
: _genRuleProviders(json['rule-providers'] as Map<String, dynamic>),
@@ -259,7 +325,7 @@ Map<String, dynamic> _$$ClashConfigSnippetImplToJson(
_$ClashConfigSnippetImpl instance) =>
<String, dynamic>{
'proxy-groups': instance.proxyGroups,
'rule': instance.rule,
'rules': instance.rule,
'rule-providers': instance.ruleProvider,
'sub-rules': instance.subRules,
};

View File

@@ -19,7 +19,7 @@ mixin _$NavigationItem {
Icon get icon => throw _privateConstructorUsedError;
PageLabel get label => throw _privateConstructorUsedError;
String? get description => throw _privateConstructorUsedError;
Widget get fragment => throw _privateConstructorUsedError;
Widget get view => throw _privateConstructorUsedError;
bool get keep => throw _privateConstructorUsedError;
String? get path => throw _privateConstructorUsedError;
List<NavigationItemMode> get modes => throw _privateConstructorUsedError;
@@ -41,7 +41,7 @@ abstract class $NavigationItemCopyWith<$Res> {
{Icon icon,
PageLabel label,
String? description,
Widget fragment,
Widget view,
bool keep,
String? path,
List<NavigationItemMode> modes});
@@ -65,7 +65,7 @@ class _$NavigationItemCopyWithImpl<$Res, $Val extends NavigationItem>
Object? icon = null,
Object? label = null,
Object? description = freezed,
Object? fragment = null,
Object? view = null,
Object? keep = null,
Object? path = freezed,
Object? modes = null,
@@ -83,9 +83,9 @@ class _$NavigationItemCopyWithImpl<$Res, $Val extends NavigationItem>
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String?,
fragment: null == fragment
? _value.fragment
: fragment // ignore: cast_nullable_to_non_nullable
view: null == view
? _value.view
: view // ignore: cast_nullable_to_non_nullable
as Widget,
keep: null == keep
? _value.keep
@@ -115,7 +115,7 @@ abstract class _$$NavigationItemImplCopyWith<$Res>
{Icon icon,
PageLabel label,
String? description,
Widget fragment,
Widget view,
bool keep,
String? path,
List<NavigationItemMode> modes});
@@ -137,7 +137,7 @@ class __$$NavigationItemImplCopyWithImpl<$Res>
Object? icon = null,
Object? label = null,
Object? description = freezed,
Object? fragment = null,
Object? view = null,
Object? keep = null,
Object? path = freezed,
Object? modes = null,
@@ -155,9 +155,9 @@ class __$$NavigationItemImplCopyWithImpl<$Res>
? _value.description
: description // ignore: cast_nullable_to_non_nullable
as String?,
fragment: null == fragment
? _value.fragment
: fragment // ignore: cast_nullable_to_non_nullable
view: null == view
? _value.view
: view // ignore: cast_nullable_to_non_nullable
as Widget,
keep: null == keep
? _value.keep
@@ -182,7 +182,7 @@ class _$NavigationItemImpl implements _NavigationItem {
{required this.icon,
required this.label,
this.description,
required this.fragment,
required this.view,
this.keep = true,
this.path,
final List<NavigationItemMode> modes = const [
@@ -198,7 +198,7 @@ class _$NavigationItemImpl implements _NavigationItem {
@override
final String? description;
@override
final Widget fragment;
final Widget view;
@override
@JsonKey()
final bool keep;
@@ -215,7 +215,7 @@ class _$NavigationItemImpl implements _NavigationItem {
@override
String toString() {
return 'NavigationItem(icon: $icon, label: $label, description: $description, fragment: $fragment, keep: $keep, path: $path, modes: $modes)';
return 'NavigationItem(icon: $icon, label: $label, description: $description, view: $view, keep: $keep, path: $path, modes: $modes)';
}
@override
@@ -227,16 +227,15 @@ class _$NavigationItemImpl implements _NavigationItem {
(identical(other.label, label) || other.label == label) &&
(identical(other.description, description) ||
other.description == description) &&
(identical(other.fragment, fragment) ||
other.fragment == fragment) &&
(identical(other.view, view) || other.view == view) &&
(identical(other.keep, keep) || other.keep == keep) &&
(identical(other.path, path) || other.path == path) &&
const DeepCollectionEquality().equals(other._modes, _modes));
}
@override
int get hashCode => Object.hash(runtimeType, icon, label, description,
fragment, keep, path, const DeepCollectionEquality().hash(_modes));
int get hashCode => Object.hash(runtimeType, icon, label, description, view,
keep, path, const DeepCollectionEquality().hash(_modes));
/// Create a copy of NavigationItem
/// with the given fields replaced by the non-null parameter values.
@@ -253,7 +252,7 @@ abstract class _NavigationItem implements NavigationItem {
{required final Icon icon,
required final PageLabel label,
final String? description,
required final Widget fragment,
required final Widget view,
final bool keep,
final String? path,
final List<NavigationItemMode> modes}) = _$NavigationItemImpl;
@@ -265,7 +264,7 @@ abstract class _NavigationItem implements NavigationItem {
@override
String? get description;
@override
Widget get fragment;
Widget get view;
@override
bool get keep;
@override
@@ -3466,3 +3465,348 @@ abstract class _TextPainterParams implements TextPainterParams {
_$$TextPainterParamsImplCopyWith<_$TextPainterParamsImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$Result<T> {
T? get data => throw _privateConstructorUsedError;
ResultType get type => throw _privateConstructorUsedError;
String get message => throw _privateConstructorUsedError;
/// Create a copy of Result
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ResultCopyWith<T, Result<T>> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ResultCopyWith<T, $Res> {
factory $ResultCopyWith(Result<T> value, $Res Function(Result<T>) then) =
_$ResultCopyWithImpl<T, $Res, Result<T>>;
@useResult
$Res call({T? data, ResultType type, String message});
}
/// @nodoc
class _$ResultCopyWithImpl<T, $Res, $Val extends Result<T>>
implements $ResultCopyWith<T, $Res> {
_$ResultCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Result
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = freezed,
Object? type = null,
Object? message = null,
}) {
return _then(_value.copyWith(
data: freezed == data
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as T?,
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as ResultType,
message: null == message
? _value.message
: message // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$ResultImplCopyWith<T, $Res>
implements $ResultCopyWith<T, $Res> {
factory _$$ResultImplCopyWith(
_$ResultImpl<T> value, $Res Function(_$ResultImpl<T>) then) =
__$$ResultImplCopyWithImpl<T, $Res>;
@override
@useResult
$Res call({T? data, ResultType type, String message});
}
/// @nodoc
class __$$ResultImplCopyWithImpl<T, $Res>
extends _$ResultCopyWithImpl<T, $Res, _$ResultImpl<T>>
implements _$$ResultImplCopyWith<T, $Res> {
__$$ResultImplCopyWithImpl(
_$ResultImpl<T> _value, $Res Function(_$ResultImpl<T>) _then)
: super(_value, _then);
/// Create a copy of Result
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? data = freezed,
Object? type = null,
Object? message = null,
}) {
return _then(_$ResultImpl<T>(
data: freezed == data
? _value.data
: data // ignore: cast_nullable_to_non_nullable
as T?,
type: null == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as ResultType,
message: null == message
? _value.message
: message // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
class _$ResultImpl<T> implements _Result<T> {
const _$ResultImpl(
{required this.data, required this.type, required this.message});
@override
final T? data;
@override
final ResultType type;
@override
final String message;
@override
String toString() {
return 'Result<$T>(data: $data, type: $type, message: $message)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ResultImpl<T> &&
const DeepCollectionEquality().equals(other.data, data) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.message, message) || other.message == message));
}
@override
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(data), type, message);
/// Create a copy of Result
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ResultImplCopyWith<T, _$ResultImpl<T>> get copyWith =>
__$$ResultImplCopyWithImpl<T, _$ResultImpl<T>>(this, _$identity);
}
abstract class _Result<T> implements Result<T> {
const factory _Result(
{required final T? data,
required final ResultType type,
required final String message}) = _$ResultImpl<T>;
@override
T? get data;
@override
ResultType get type;
@override
String get message;
/// Create a copy of Result
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ResultImplCopyWith<T, _$ResultImpl<T>> get copyWith =>
throw _privateConstructorUsedError;
}
Script _$ScriptFromJson(Map<String, dynamic> json) {
return _Script.fromJson(json);
}
/// @nodoc
mixin _$Script {
String get id => throw _privateConstructorUsedError;
String get label => throw _privateConstructorUsedError;
String get content => throw _privateConstructorUsedError;
/// Serializes this Script to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Script
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ScriptCopyWith<Script> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ScriptCopyWith<$Res> {
factory $ScriptCopyWith(Script value, $Res Function(Script) then) =
_$ScriptCopyWithImpl<$Res, Script>;
@useResult
$Res call({String id, String label, String content});
}
/// @nodoc
class _$ScriptCopyWithImpl<$Res, $Val extends Script>
implements $ScriptCopyWith<$Res> {
_$ScriptCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Script
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? label = null,
Object? content = null,
}) {
return _then(_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
label: null == label
? _value.label
: label // ignore: cast_nullable_to_non_nullable
as String,
content: null == content
? _value.content
: content // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$ScriptImplCopyWith<$Res> implements $ScriptCopyWith<$Res> {
factory _$$ScriptImplCopyWith(
_$ScriptImpl value, $Res Function(_$ScriptImpl) then) =
__$$ScriptImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String id, String label, String content});
}
/// @nodoc
class __$$ScriptImplCopyWithImpl<$Res>
extends _$ScriptCopyWithImpl<$Res, _$ScriptImpl>
implements _$$ScriptImplCopyWith<$Res> {
__$$ScriptImplCopyWithImpl(
_$ScriptImpl _value, $Res Function(_$ScriptImpl) _then)
: super(_value, _then);
/// Create a copy of Script
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = null,
Object? label = null,
Object? content = null,
}) {
return _then(_$ScriptImpl(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
label: null == label
? _value.label
: label // ignore: cast_nullable_to_non_nullable
as String,
content: null == content
? _value.content
: content // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ScriptImpl implements _Script {
const _$ScriptImpl(
{required this.id, required this.label, required this.content});
factory _$ScriptImpl.fromJson(Map<String, dynamic> json) =>
_$$ScriptImplFromJson(json);
@override
final String id;
@override
final String label;
@override
final String content;
@override
String toString() {
return 'Script(id: $id, label: $label, content: $content)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ScriptImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.label, label) || other.label == label) &&
(identical(other.content, content) || other.content == content));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, id, label, content);
/// Create a copy of Script
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ScriptImplCopyWith<_$ScriptImpl> get copyWith =>
__$$ScriptImplCopyWithImpl<_$ScriptImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ScriptImplToJson(
this,
);
}
}
abstract class _Script implements Script {
const factory _Script(
{required final String id,
required final String label,
required final String content}) = _$ScriptImpl;
factory _Script.fromJson(Map<String, dynamic> json) = _$ScriptImpl.fromJson;
@override
String get id;
@override
String get label;
@override
String get content;
/// Create a copy of Script
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ScriptImplCopyWith<_$ScriptImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -218,3 +218,16 @@ Map<String, dynamic> _$$TextPainterParamsImplToJson(
'maxWidth': instance.maxWidth,
'maxLines': instance.maxLines,
};
_$ScriptImpl _$$ScriptImplFromJson(Map<String, dynamic> json) => _$ScriptImpl(
id: json['id'] as String,
label: json['label'] as String,
content: json['content'] as String,
);
Map<String, dynamic> _$$ScriptImplToJson(_$ScriptImpl instance) =>
<String, dynamic>{
'id': instance.id,
'label': instance.label,
'content': instance.content,
};

View File

@@ -2243,6 +2243,183 @@ abstract class _ThemeProps implements ThemeProps {
throw _privateConstructorUsedError;
}
ScriptProps _$ScriptPropsFromJson(Map<String, dynamic> json) {
return _ScriptProps.fromJson(json);
}
/// @nodoc
mixin _$ScriptProps {
String? get currentId => throw _privateConstructorUsedError;
List<Script> get scripts => throw _privateConstructorUsedError;
/// Serializes this ScriptProps to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of ScriptProps
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$ScriptPropsCopyWith<ScriptProps> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $ScriptPropsCopyWith<$Res> {
factory $ScriptPropsCopyWith(
ScriptProps value, $Res Function(ScriptProps) then) =
_$ScriptPropsCopyWithImpl<$Res, ScriptProps>;
@useResult
$Res call({String? currentId, List<Script> scripts});
}
/// @nodoc
class _$ScriptPropsCopyWithImpl<$Res, $Val extends ScriptProps>
implements $ScriptPropsCopyWith<$Res> {
_$ScriptPropsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of ScriptProps
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? currentId = freezed,
Object? scripts = null,
}) {
return _then(_value.copyWith(
currentId: freezed == currentId
? _value.currentId
: currentId // ignore: cast_nullable_to_non_nullable
as String?,
scripts: null == scripts
? _value.scripts
: scripts // ignore: cast_nullable_to_non_nullable
as List<Script>,
) as $Val);
}
}
/// @nodoc
abstract class _$$ScriptPropsImplCopyWith<$Res>
implements $ScriptPropsCopyWith<$Res> {
factory _$$ScriptPropsImplCopyWith(
_$ScriptPropsImpl value, $Res Function(_$ScriptPropsImpl) then) =
__$$ScriptPropsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String? currentId, List<Script> scripts});
}
/// @nodoc
class __$$ScriptPropsImplCopyWithImpl<$Res>
extends _$ScriptPropsCopyWithImpl<$Res, _$ScriptPropsImpl>
implements _$$ScriptPropsImplCopyWith<$Res> {
__$$ScriptPropsImplCopyWithImpl(
_$ScriptPropsImpl _value, $Res Function(_$ScriptPropsImpl) _then)
: super(_value, _then);
/// Create a copy of ScriptProps
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? currentId = freezed,
Object? scripts = null,
}) {
return _then(_$ScriptPropsImpl(
currentId: freezed == currentId
? _value.currentId
: currentId // ignore: cast_nullable_to_non_nullable
as String?,
scripts: null == scripts
? _value._scripts
: scripts // ignore: cast_nullable_to_non_nullable
as List<Script>,
));
}
}
/// @nodoc
@JsonSerializable()
class _$ScriptPropsImpl implements _ScriptProps {
const _$ScriptPropsImpl(
{this.currentId, final List<Script> scripts = const []})
: _scripts = scripts;
factory _$ScriptPropsImpl.fromJson(Map<String, dynamic> json) =>
_$$ScriptPropsImplFromJson(json);
@override
final String? currentId;
final List<Script> _scripts;
@override
@JsonKey()
List<Script> get scripts {
if (_scripts is EqualUnmodifiableListView) return _scripts;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_scripts);
}
@override
String toString() {
return 'ScriptProps(currentId: $currentId, scripts: $scripts)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$ScriptPropsImpl &&
(identical(other.currentId, currentId) ||
other.currentId == currentId) &&
const DeepCollectionEquality().equals(other._scripts, _scripts));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType, currentId, const DeepCollectionEquality().hash(_scripts));
/// Create a copy of ScriptProps
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$ScriptPropsImplCopyWith<_$ScriptPropsImpl> get copyWith =>
__$$ScriptPropsImplCopyWithImpl<_$ScriptPropsImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$ScriptPropsImplToJson(
this,
);
}
}
abstract class _ScriptProps implements ScriptProps {
const factory _ScriptProps(
{final String? currentId,
final List<Script> scripts}) = _$ScriptPropsImpl;
factory _ScriptProps.fromJson(Map<String, dynamic> json) =
_$ScriptPropsImpl.fromJson;
@override
String? get currentId;
@override
List<Script> get scripts;
/// Create a copy of ScriptProps
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$ScriptPropsImplCopyWith<_$ScriptPropsImpl> get copyWith =>
throw _privateConstructorUsedError;
}
Config _$ConfigFromJson(Map<String, dynamic> json) {
return _Config.fromJson(json);
}
@@ -2263,6 +2440,7 @@ mixin _$Config {
ProxiesStyle get proxiesStyle => throw _privateConstructorUsedError;
WindowProps get windowProps => throw _privateConstructorUsedError;
ClashConfig get patchClashConfig => throw _privateConstructorUsedError;
ScriptProps get scriptProps => throw _privateConstructorUsedError;
/// Serializes this Config to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -2291,7 +2469,8 @@ abstract class $ConfigCopyWith<$Res> {
@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps,
ProxiesStyle proxiesStyle,
WindowProps windowProps,
ClashConfig patchClashConfig});
ClashConfig patchClashConfig,
ScriptProps scriptProps});
$AppSettingPropsCopyWith<$Res> get appSetting;
$DAVCopyWith<$Res>? get dav;
@@ -2301,6 +2480,7 @@ abstract class $ConfigCopyWith<$Res> {
$ProxiesStyleCopyWith<$Res> get proxiesStyle;
$WindowPropsCopyWith<$Res> get windowProps;
$ClashConfigCopyWith<$Res> get patchClashConfig;
$ScriptPropsCopyWith<$Res> get scriptProps;
}
/// @nodoc
@@ -2330,6 +2510,7 @@ class _$ConfigCopyWithImpl<$Res, $Val extends Config>
Object? proxiesStyle = null,
Object? windowProps = null,
Object? patchClashConfig = null,
Object? scriptProps = null,
}) {
return _then(_value.copyWith(
appSetting: null == appSetting
@@ -2380,6 +2561,10 @@ class _$ConfigCopyWithImpl<$Res, $Val extends Config>
? _value.patchClashConfig
: patchClashConfig // ignore: cast_nullable_to_non_nullable
as ClashConfig,
scriptProps: null == scriptProps
? _value.scriptProps
: scriptProps // ignore: cast_nullable_to_non_nullable
as ScriptProps,
) as $Val);
}
@@ -2466,6 +2651,16 @@ class _$ConfigCopyWithImpl<$Res, $Val extends Config>
return _then(_value.copyWith(patchClashConfig: value) as $Val);
});
}
/// Create a copy of Config
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ScriptPropsCopyWith<$Res> get scriptProps {
return $ScriptPropsCopyWith<$Res>(_value.scriptProps, (value) {
return _then(_value.copyWith(scriptProps: value) as $Val);
});
}
}
/// @nodoc
@@ -2488,7 +2683,8 @@ abstract class _$$ConfigImplCopyWith<$Res> implements $ConfigCopyWith<$Res> {
@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps,
ProxiesStyle proxiesStyle,
WindowProps windowProps,
ClashConfig patchClashConfig});
ClashConfig patchClashConfig,
ScriptProps scriptProps});
@override
$AppSettingPropsCopyWith<$Res> get appSetting;
@@ -2506,6 +2702,8 @@ abstract class _$$ConfigImplCopyWith<$Res> implements $ConfigCopyWith<$Res> {
$WindowPropsCopyWith<$Res> get windowProps;
@override
$ClashConfigCopyWith<$Res> get patchClashConfig;
@override
$ScriptPropsCopyWith<$Res> get scriptProps;
}
/// @nodoc
@@ -2533,6 +2731,7 @@ class __$$ConfigImplCopyWithImpl<$Res>
Object? proxiesStyle = null,
Object? windowProps = null,
Object? patchClashConfig = null,
Object? scriptProps = null,
}) {
return _then(_$ConfigImpl(
appSetting: null == appSetting
@@ -2583,6 +2782,10 @@ class __$$ConfigImplCopyWithImpl<$Res>
? _value.patchClashConfig
: patchClashConfig // ignore: cast_nullable_to_non_nullable
as ClashConfig,
scriptProps: null == scriptProps
? _value.scriptProps
: scriptProps // ignore: cast_nullable_to_non_nullable
as ScriptProps,
));
}
}
@@ -2603,7 +2806,8 @@ class _$ConfigImpl implements _Config {
@JsonKey(fromJson: ThemeProps.safeFromJson) required this.themeProps,
this.proxiesStyle = defaultProxiesStyle,
this.windowProps = defaultWindowProps,
this.patchClashConfig = defaultClashConfig})
this.patchClashConfig = defaultClashConfig,
this.scriptProps = const ScriptProps()})
: _profiles = profiles,
_hotKeyActions = hotKeyActions;
@@ -2656,10 +2860,13 @@ class _$ConfigImpl implements _Config {
@override
@JsonKey()
final ClashConfig patchClashConfig;
@override
@JsonKey()
final ScriptProps scriptProps;
@override
String toString() {
return 'Config(appSetting: $appSetting, profiles: $profiles, hotKeyActions: $hotKeyActions, currentProfileId: $currentProfileId, overrideDns: $overrideDns, dav: $dav, networkProps: $networkProps, vpnProps: $vpnProps, themeProps: $themeProps, proxiesStyle: $proxiesStyle, windowProps: $windowProps, patchClashConfig: $patchClashConfig)';
return 'Config(appSetting: $appSetting, profiles: $profiles, hotKeyActions: $hotKeyActions, currentProfileId: $currentProfileId, overrideDns: $overrideDns, dav: $dav, networkProps: $networkProps, vpnProps: $vpnProps, themeProps: $themeProps, proxiesStyle: $proxiesStyle, windowProps: $windowProps, patchClashConfig: $patchClashConfig, scriptProps: $scriptProps)';
}
@override
@@ -2688,7 +2895,9 @@ class _$ConfigImpl implements _Config {
(identical(other.windowProps, windowProps) ||
other.windowProps == windowProps) &&
(identical(other.patchClashConfig, patchClashConfig) ||
other.patchClashConfig == patchClashConfig));
other.patchClashConfig == patchClashConfig) &&
(identical(other.scriptProps, scriptProps) ||
other.scriptProps == scriptProps));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -2706,7 +2915,8 @@ class _$ConfigImpl implements _Config {
themeProps,
proxiesStyle,
windowProps,
patchClashConfig);
patchClashConfig,
scriptProps);
/// Create a copy of Config
/// with the given fields replaced by the non-null parameter values.
@@ -2739,7 +2949,8 @@ abstract class _Config implements Config {
required final ThemeProps themeProps,
final ProxiesStyle proxiesStyle,
final WindowProps windowProps,
final ClashConfig patchClashConfig}) = _$ConfigImpl;
final ClashConfig patchClashConfig,
final ScriptProps scriptProps}) = _$ConfigImpl;
factory _Config.fromJson(Map<String, dynamic> json) = _$ConfigImpl.fromJson;
@@ -2769,6 +2980,8 @@ abstract class _Config implements Config {
WindowProps get windowProps;
@override
ClashConfig get patchClashConfig;
@override
ScriptProps get scriptProps;
/// Create a copy of Config
/// with the given fields replaced by the non-null parameter values.

View File

@@ -292,6 +292,21 @@ const _$DynamicSchemeVariantEnumMap = {
DynamicSchemeVariant.fruitSalad: 'fruitSalad',
};
_$ScriptPropsImpl _$$ScriptPropsImplFromJson(Map<String, dynamic> json) =>
_$ScriptPropsImpl(
currentId: json['currentId'] as String?,
scripts: (json['scripts'] as List<dynamic>?)
?.map((e) => Script.fromJson(e as Map<String, dynamic>))
.toList() ??
const [],
);
Map<String, dynamic> _$$ScriptPropsImplToJson(_$ScriptPropsImpl instance) =>
<String, dynamic>{
'currentId': instance.currentId,
'scripts': instance.scripts,
};
_$ConfigImpl _$$ConfigImplFromJson(Map<String, dynamic> json) => _$ConfigImpl(
appSetting: json['appSetting'] == null
? defaultAppSettingProps
@@ -330,6 +345,9 @@ _$ConfigImpl _$$ConfigImplFromJson(Map<String, dynamic> json) => _$ConfigImpl(
? defaultClashConfig
: ClashConfig.fromJson(
json['patchClashConfig'] as Map<String, dynamic>),
scriptProps: json['scriptProps'] == null
? const ScriptProps()
: ScriptProps.fromJson(json['scriptProps'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ConfigImplToJson(_$ConfigImpl instance) =>
@@ -346,4 +364,5 @@ Map<String, dynamic> _$$ConfigImplToJson(_$ConfigImpl instance) =>
'proxiesStyle': instance.proxiesStyle,
'windowProps': instance.windowProps,
'patchClashConfig': instance.patchClashConfig,
'scriptProps': instance.scriptProps,
};

File diff suppressed because it is too large Load Diff

View File

@@ -6,6 +6,76 @@ part of '../core.dart';
// JsonSerializableGenerator
// **************************************************************************
_$SetupParamsImpl _$$SetupParamsImplFromJson(Map<String, dynamic> json) =>
_$SetupParamsImpl(
config: json['config'] as Map<String, dynamic>,
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
testUrl: json['test-url'] as String,
);
Map<String, dynamic> _$$SetupParamsImplToJson(_$SetupParamsImpl instance) =>
<String, dynamic>{
'config': instance.config,
'selected-map': instance.selectedMap,
'test-url': instance.testUrl,
};
_$UpdateParamsImpl _$$UpdateParamsImplFromJson(Map<String, dynamic> json) =>
_$UpdateParamsImpl(
tun: Tun.fromJson(json['tun'] as Map<String, dynamic>),
mixedPort: (json['mixed-port'] as num).toInt(),
allowLan: json['allow-lan'] as bool,
findProcessMode:
$enumDecode(_$FindProcessModeEnumMap, json['find-process-mode']),
mode: $enumDecode(_$ModeEnumMap, json['mode']),
logLevel: $enumDecode(_$LogLevelEnumMap, json['log-level']),
ipv6: json['ipv6'] as bool,
tcpConcurrent: json['tcp-concurrent'] as bool,
externalController: $enumDecode(
_$ExternalControllerStatusEnumMap, json['external-controller']),
unifiedDelay: json['unified-delay'] as bool,
);
Map<String, dynamic> _$$UpdateParamsImplToJson(_$UpdateParamsImpl instance) =>
<String, dynamic>{
'tun': instance.tun,
'mixed-port': instance.mixedPort,
'allow-lan': instance.allowLan,
'find-process-mode': _$FindProcessModeEnumMap[instance.findProcessMode]!,
'mode': _$ModeEnumMap[instance.mode]!,
'log-level': _$LogLevelEnumMap[instance.logLevel]!,
'ipv6': instance.ipv6,
'tcp-concurrent': instance.tcpConcurrent,
'external-controller':
_$ExternalControllerStatusEnumMap[instance.externalController]!,
'unified-delay': instance.unifiedDelay,
};
const _$FindProcessModeEnumMap = {
FindProcessMode.always: 'always',
FindProcessMode.off: 'off',
};
const _$ModeEnumMap = {
Mode.rule: 'rule',
Mode.global: 'global',
Mode.direct: 'direct',
};
const _$LogLevelEnumMap = {
LogLevel.debug: 'debug',
LogLevel.info: 'info',
LogLevel.warning: 'warning',
LogLevel.error: 'error',
LogLevel.silent: 'silent',
LogLevel.app: 'app',
};
const _$ExternalControllerStatusEnumMap = {
ExternalControllerStatus.close: '',
ExternalControllerStatus.open: '127.0.0.1:9090',
};
_$CoreStateImpl _$$CoreStateImplFromJson(Map<String, dynamic> json) =>
_$CoreStateImpl(
vpnProps: VpnProps.fromJson(json['vpn-props'] as Map<String, dynamic>?),
@@ -63,43 +133,6 @@ Map<String, dynamic> _$$AndroidVpnOptionsImplToJson(
'dnsServerAddress': instance.dnsServerAddress,
};
_$ConfigExtendedParamsImpl _$$ConfigExtendedParamsImplFromJson(
Map<String, dynamic> json) =>
_$ConfigExtendedParamsImpl(
isPatch: json['is-patch'] as bool,
selectedMap: Map<String, String>.from(json['selected-map'] as Map),
overrideDns: json['override-dns'] as bool,
overrideRule: json['override-rule'] as bool,
testUrl: json['test-url'] as String,
);
Map<String, dynamic> _$$ConfigExtendedParamsImplToJson(
_$ConfigExtendedParamsImpl instance) =>
<String, dynamic>{
'is-patch': instance.isPatch,
'selected-map': instance.selectedMap,
'override-dns': instance.overrideDns,
'override-rule': instance.overrideRule,
'test-url': instance.testUrl,
};
_$UpdateConfigParamsImpl _$$UpdateConfigParamsImplFromJson(
Map<String, dynamic> json) =>
_$UpdateConfigParamsImpl(
profileId: json['profile-id'] as String,
config: ClashConfig.fromJson(json['config'] as Map<String, dynamic>),
params:
ConfigExtendedParams.fromJson(json['params'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$UpdateConfigParamsImplToJson(
_$UpdateConfigParamsImpl instance) =>
<String, dynamic>{
'profile-id': instance.profileId,
'config': instance.config,
'params': instance.params,
};
_$InitParamsImpl _$$InitParamsImplFromJson(Map<String, dynamic> json) =>
_$InitParamsImpl(
homeDir: json['home-dir'] as String,
@@ -199,41 +232,6 @@ Map<String, dynamic> _$$NowImplToJson(_$NowImpl instance) => <String, dynamic>{
'value': instance.value,
};
_$ProcessDataImpl _$$ProcessDataImplFromJson(Map<String, dynamic> json) =>
_$ProcessDataImpl(
id: json['id'] as String,
metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ProcessDataImplToJson(_$ProcessDataImpl instance) =>
<String, dynamic>{
'id': instance.id,
'metadata': instance.metadata,
};
_$FdImpl _$$FdImplFromJson(Map<String, dynamic> json) => _$FdImpl(
id: json['id'] as String,
value: (json['value'] as num).toInt(),
);
Map<String, dynamic> _$$FdImplToJson(_$FdImpl instance) => <String, dynamic>{
'id': instance.id,
'value': instance.value,
};
_$ProcessMapItemImpl _$$ProcessMapItemImplFromJson(Map<String, dynamic> json) =>
_$ProcessMapItemImpl(
id: json['id'] as String,
value: json['value'] as String,
);
Map<String, dynamic> _$$ProcessMapItemImplToJson(
_$ProcessMapItemImpl instance) =>
<String, dynamic>{
'id': instance.id,
'value': instance.value,
};
_$ProviderSubscriptionInfoImpl _$$ProviderSubscriptionInfoImplFromJson(
Map<String, dynamic> json) =>
_$ProviderSubscriptionInfoImpl(
@@ -279,32 +277,9 @@ Map<String, dynamic> _$$ExternalProviderImplToJson(
'update-at': instance.updateAt.toIso8601String(),
};
_$TunPropsImpl _$$TunPropsImplFromJson(Map<String, dynamic> json) =>
_$TunPropsImpl(
fd: (json['fd'] as num).toInt(),
gateway: json['gateway'] as String,
gateway6: json['gateway6'] as String,
portal: json['portal'] as String,
portal6: json['portal6'] as String,
dns: json['dns'] as String,
dns6: json['dns6'] as String,
);
Map<String, dynamic> _$$TunPropsImplToJson(_$TunPropsImpl instance) =>
<String, dynamic>{
'fd': instance.fd,
'gateway': instance.gateway,
'gateway6': instance.gateway6,
'portal': instance.portal,
'portal6': instance.portal6,
'dns': instance.dns,
'dns6': instance.dns6,
};
_$ActionImpl _$$ActionImplFromJson(Map<String, dynamic> json) => _$ActionImpl(
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
data: json['data'],
defaultValue: json['default-value'],
id: json['id'] as String,
);
@@ -312,7 +287,6 @@ Map<String, dynamic> _$$ActionImplToJson(_$ActionImpl instance) =>
<String, dynamic>{
'method': _$ActionMethodEnumMap[instance.method]!,
'data': instance.data,
'default-value': instance.defaultValue,
'id': instance.id,
};
@@ -324,6 +298,7 @@ const _$ActionMethodEnumMap = {
ActionMethod.shutdown: 'shutdown',
ActionMethod.validateConfig: 'validateConfig',
ActionMethod.updateConfig: 'updateConfig',
ActionMethod.getConfig: 'getConfig',
ActionMethod.getProxies: 'getProxies',
ActionMethod.changeProxy: 'changeProxy',
ActionMethod.getTraffic: 'getTraffic',
@@ -345,10 +320,8 @@ const _$ActionMethodEnumMap = {
ActionMethod.stopListener: 'stopListener',
ActionMethod.getCountryCode: 'getCountryCode',
ActionMethod.getMemory: 'getMemory',
ActionMethod.getProfile: 'getProfile',
ActionMethod.crash: 'crash',
ActionMethod.setFdMap: 'setFdMap',
ActionMethod.setProcessMap: 'setProcessMap',
ActionMethod.setupConfig: 'setupConfig',
ActionMethod.setState: 'setState',
ActionMethod.startTun: 'startTun',
ActionMethod.stopTun: 'stopTun',
@@ -363,6 +336,8 @@ _$ActionResultImpl _$$ActionResultImplFromJson(Map<String, dynamic> json) =>
method: $enumDecode(_$ActionMethodEnumMap, json['method']),
data: json['data'],
id: json['id'] as String?,
code: $enumDecodeNullable(_$ResultTypeEnumMap, json['code']) ??
ResultType.success,
);
Map<String, dynamic> _$$ActionResultImplToJson(_$ActionResultImpl instance) =>
@@ -370,4 +345,10 @@ Map<String, dynamic> _$$ActionResultImplToJson(_$ActionResultImpl instance) =>
'method': _$ActionMethodEnumMap[instance.method]!,
'data': instance.data,
'id': instance.id,
'code': _$ResultTypeEnumMap[instance.code]!,
};
const _$ResultTypeEnumMap = {
ResultType.success: 0,
ResultType.error: -1,
};

View File

@@ -4237,7 +4237,6 @@ abstract class _VpnState implements VpnState {
/// @nodoc
mixin _$ProfileOverrideStateModel {
ClashConfigSnippet? get snippet => throw _privateConstructorUsedError;
bool get isEdit => throw _privateConstructorUsedError;
Set<String> get selectedRules => throw _privateConstructorUsedError;
OverrideData? get overrideData => throw _privateConstructorUsedError;
@@ -4256,7 +4255,6 @@ abstract class $ProfileOverrideStateModelCopyWith<$Res> {
@useResult
$Res call(
{ClashConfigSnippet? snippet,
bool isEdit,
Set<String> selectedRules,
OverrideData? overrideData});
@@ -4281,7 +4279,6 @@ class _$ProfileOverrideStateModelCopyWithImpl<$Res,
@override
$Res call({
Object? snippet = freezed,
Object? isEdit = null,
Object? selectedRules = null,
Object? overrideData = freezed,
}) {
@@ -4290,10 +4287,6 @@ class _$ProfileOverrideStateModelCopyWithImpl<$Res,
? _value.snippet
: snippet // ignore: cast_nullable_to_non_nullable
as ClashConfigSnippet?,
isEdit: null == isEdit
? _value.isEdit
: isEdit // ignore: cast_nullable_to_non_nullable
as bool,
selectedRules: null == selectedRules
? _value.selectedRules
: selectedRules // ignore: cast_nullable_to_non_nullable
@@ -4345,7 +4338,6 @@ abstract class _$$ProfileOverrideStateModelImplCopyWith<$Res>
@useResult
$Res call(
{ClashConfigSnippet? snippet,
bool isEdit,
Set<String> selectedRules,
OverrideData? overrideData});
@@ -4371,7 +4363,6 @@ class __$$ProfileOverrideStateModelImplCopyWithImpl<$Res>
@override
$Res call({
Object? snippet = freezed,
Object? isEdit = null,
Object? selectedRules = null,
Object? overrideData = freezed,
}) {
@@ -4380,10 +4371,6 @@ class __$$ProfileOverrideStateModelImplCopyWithImpl<$Res>
? _value.snippet
: snippet // ignore: cast_nullable_to_non_nullable
as ClashConfigSnippet?,
isEdit: null == isEdit
? _value.isEdit
: isEdit // ignore: cast_nullable_to_non_nullable
as bool,
selectedRules: null == selectedRules
? _value._selectedRules
: selectedRules // ignore: cast_nullable_to_non_nullable
@@ -4401,15 +4388,12 @@ class __$$ProfileOverrideStateModelImplCopyWithImpl<$Res>
class _$ProfileOverrideStateModelImpl implements _ProfileOverrideStateModel {
const _$ProfileOverrideStateModelImpl(
{this.snippet,
required this.isEdit,
required final Set<String> selectedRules,
this.overrideData})
: _selectedRules = selectedRules;
@override
final ClashConfigSnippet? snippet;
@override
final bool isEdit;
final Set<String> _selectedRules;
@override
Set<String> get selectedRules {
@@ -4423,7 +4407,7 @@ class _$ProfileOverrideStateModelImpl implements _ProfileOverrideStateModel {
@override
String toString() {
return 'ProfileOverrideStateModel(snippet: $snippet, isEdit: $isEdit, selectedRules: $selectedRules, overrideData: $overrideData)';
return 'ProfileOverrideStateModel(snippet: $snippet, selectedRules: $selectedRules, overrideData: $overrideData)';
}
@override
@@ -4432,7 +4416,6 @@ class _$ProfileOverrideStateModelImpl implements _ProfileOverrideStateModel {
(other.runtimeType == runtimeType &&
other is _$ProfileOverrideStateModelImpl &&
(identical(other.snippet, snippet) || other.snippet == snippet) &&
(identical(other.isEdit, isEdit) || other.isEdit == isEdit) &&
const DeepCollectionEquality()
.equals(other._selectedRules, _selectedRules) &&
(identical(other.overrideData, overrideData) ||
@@ -4440,7 +4423,7 @@ class _$ProfileOverrideStateModelImpl implements _ProfileOverrideStateModel {
}
@override
int get hashCode => Object.hash(runtimeType, snippet, isEdit,
int get hashCode => Object.hash(runtimeType, snippet,
const DeepCollectionEquality().hash(_selectedRules), overrideData);
/// Create a copy of ProfileOverrideStateModel
@@ -4456,15 +4439,12 @@ class _$ProfileOverrideStateModelImpl implements _ProfileOverrideStateModel {
abstract class _ProfileOverrideStateModel implements ProfileOverrideStateModel {
const factory _ProfileOverrideStateModel(
{final ClashConfigSnippet? snippet,
required final bool isEdit,
required final Set<String> selectedRules,
final OverrideData? overrideData}) = _$ProfileOverrideStateModelImpl;
@override
ClashConfigSnippet? get snippet;
@override
bool get isEdit;
@override
Set<String> get selectedRules;
@override
OverrideData? get overrideData;

View File

@@ -260,7 +260,6 @@ class VpnState with _$VpnState {
class ProfileOverrideStateModel with _$ProfileOverrideStateModel {
const factory ProfileOverrideStateModel({
ClashConfigSnippet? snippet,
required bool isEdit,
required Set<String> selectedRules,
OverrideData? overrideData,
}) = _ProfileOverrideStateModel;

View File

@@ -2,31 +2,37 @@ import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/providers/app.dart';
import 'package:fl_clash/widgets/pop_scope.dart';
import 'package:fl_clash/widgets/popup.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:fl_clash/widgets/scroll.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:re_editor/re_editor.dart';
import 'package:re_highlight/languages/javascript.dart';
import 'package:re_highlight/languages/yaml.dart';
import 'package:re_highlight/styles/atom-one-light.dart';
typedef EditingValueChangeBuilder = Widget Function(CodeLineEditingValue value);
typedef TextEditingValueChangeBuilder = Widget Function(TextEditingValue value);
class EditorPage extends ConsumerStatefulWidget {
final String title;
final String content;
final Function(BuildContext context, String text)? onSave;
final Future<bool> Function(BuildContext context, String text)? onPop;
final List<Language> languages;
final bool titleEditable;
final Function(BuildContext context, String title, String content)? onSave;
final Future<bool> Function(
BuildContext context, String title, String content)? onPop;
const EditorPage({
super.key,
required this.title,
required this.content,
this.titleEditable = false,
this.onSave,
this.onPop,
this.languages = const [
Language.yaml,
],
});
@override
@@ -36,6 +42,7 @@ class EditorPage extends ConsumerStatefulWidget {
class _EditorPageState extends ConsumerState<EditorPage> {
late CodeLineEditingController _controller;
late CodeFindController _findController;
late TextEditingController _titleController;
final _focusNode = FocusNode();
@override
@@ -43,6 +50,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
super.initState();
_controller = CodeLineEditingController.fromText(widget.content);
_findController = CodeFindController(_controller);
_titleController = TextEditingController(text: widget.title);
if (system.isDesktop) {
return;
}
@@ -87,6 +95,15 @@ class _EditorPageState extends ConsumerState<EditorPage> {
);
}
Widget _wrapTitleController(TextEditingValueChangeBuilder builder) {
return ValueListenableBuilder(
valueListenable: _titleController,
builder: (_, value, ___) {
return builder(value);
},
);
}
_handleSearch() {
_findController.findMode();
}
@@ -99,116 +116,140 @@ class _EditorPageState extends ConsumerState<EditorPage> {
if (widget.onPop == null) {
return true;
}
final res = await widget.onPop!(context, _controller.text);
final res = await widget.onPop!(
context,
_titleController.text,
_controller.text,
);
if (res && context.mounted) {
return true;
}
return false;
},
child: CommonScaffold(
actions: [
if (widget.onSave != null)
_wrapController(
(value) => IconButton(
onPressed: _controller.text == widget.content
? null
: () {
widget.onSave!(context, _controller.text);
},
icon: const Icon(Icons.save_sharp),
),
),
_wrapController(
(value) => CommonPopupBox(
targetBuilder: (open) {
return IconButton(
onPressed: () {
open(
offset: Offset(0, 20),
);
},
icon: const Icon(Icons.more_vert),
);
},
popup: CommonPopupMenu(
items: [
PopupMenuItemData(
icon: Icons.search,
label: appLocalizations.search,
onPressed: _handleSearch,
),
PopupMenuItemData(
icon: Icons.undo,
label: appLocalizations.undo,
onPressed: _controller.canUndo ? _controller.undo : null,
),
PopupMenuItemData(
icon: Icons.redo,
label: appLocalizations.redo,
onPressed: _controller.canRedo ? _controller.redo : null,
),
],
),
appBar: AppBar(
title: TextField(
enabled: widget.titleEditable,
controller: _titleController,
decoration: InputDecoration(
border: _NoInputBorder(),
hintText: appLocalizations.unnamed,
),
style: context.textTheme.titleLarge,
autofocus: false,
),
],
body: SafeArea(
child: CodeEditor(
findController: _findController,
maxLengthSingleLineRendering: 200,
findBuilder: (context, controller, readOnly) => FindPanel(
controller: controller,
readOnly: readOnly,
isMobileView: isMobileView,
),
padding: EdgeInsets.only(
right: 16,
),
focusNode: _focusNode,
scrollbarBuilder: (context, child, details) {
return CommonScrollBar(
controller: details.controller,
child: child,
);
},
toolbarController: ContextMenuControllerImpl(),
indicatorBuilder: (
context,
editingController,
chunkController,
notifier,
) {
return Row(
children: [
DefaultCodeLineNumber(
controller: editingController,
notifier: notifier,
actions: genActions([
if (widget.onSave != null)
_wrapController(
(value) => _wrapTitleController(
(value) => IconButton(
onPressed: _controller.text != widget.content ||
_titleController.text != widget.title
? () {
widget.onSave!(
context,
_titleController.text,
_controller.text,
);
}
: null,
icon: const Icon(Icons.save_sharp),
),
DefaultCodeChunkIndicator(
width: 20,
controller: chunkController,
notifier: notifier,
)
],
);
},
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
controller: _controller,
style: CodeEditorStyle(
fontSize: context.textTheme.bodyLarge?.fontSize?.ap,
fontFamily: FontFamily.jetBrainsMono.value,
codeTheme: CodeHighlightTheme(
languages: {
),
),
_wrapController(
(value) => CommonPopupBox(
targetBuilder: (open) {
return IconButton(
onPressed: () {
open(
offset: Offset(-20, 20),
);
},
icon: const Icon(Icons.more_vert),
);
},
popup: CommonPopupMenu(
items: [
PopupMenuItemData(
icon: Icons.search,
label: appLocalizations.search,
onPressed: _handleSearch,
),
PopupMenuItemData(
icon: Icons.undo,
label: appLocalizations.undo,
onPressed: _controller.canUndo ? _controller.undo : null,
),
PopupMenuItemData(
icon: Icons.redo,
label: appLocalizations.redo,
onPressed: _controller.canRedo ? _controller.redo : null,
),
],
),
),
),
]),
),
body: CodeEditor(
findController: _findController,
findBuilder: (context, controller, readOnly) => FindPanel(
controller: controller,
readOnly: readOnly,
isMobileView: isMobileView,
),
padding: EdgeInsets.only(
right: 16,
),
focusNode: _focusNode,
scrollbarBuilder: (context, child, details) {
return CommonScrollBar(
controller: details.controller,
child: child,
);
},
toolbarController: ContextMenuControllerImpl(),
indicatorBuilder: (
context,
editingController,
chunkController,
notifier,
) {
return Row(
children: [
DefaultCodeLineNumber(
controller: editingController,
notifier: notifier,
),
DefaultCodeChunkIndicator(
width: 20,
controller: chunkController,
notifier: notifier,
)
],
);
},
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
controller: _controller,
style: CodeEditorStyle(
fontSize: context.textTheme.bodyLarge?.fontSize?.ap,
fontFamily: FontFamily.jetBrainsMono.value,
codeTheme: CodeHighlightTheme(
languages: {
if (widget.languages.contains(Language.yaml))
'yaml': CodeHighlightThemeMode(
mode: langYaml,
)
},
theme: atomOneLightTheme,
),
),
if (widget.languages.contains(Language.javaScript))
"javascript": CodeHighlightThemeMode(
mode: langJavascript,
),
},
theme: atomOneLightTheme,
),
),
),
title: widget.title,
),
);
}
@@ -544,3 +585,50 @@ class ContextMenuControllerImpl implements SelectionToolbarController {
Overlay.of(context).insert(_overlayEntry!);
}
}
class _NoInputBorder extends InputBorder {
const _NoInputBorder() : super(borderSide: BorderSide.none);
@override
_NoInputBorder copyWith({BorderSide? borderSide}) => const _NoInputBorder();
@override
bool get isOutline => false;
@override
EdgeInsetsGeometry get dimensions => EdgeInsets.zero;
@override
_NoInputBorder scale(double t) => const _NoInputBorder();
@override
Path getInnerPath(Rect rect, {TextDirection? textDirection}) {
return Path()..addRect(rect);
}
@override
Path getOuterPath(Rect rect, {TextDirection? textDirection}) {
return Path()..addRect(rect);
}
@override
void paintInterior(Canvas canvas, Rect rect, Paint paint,
{TextDirection? textDirection}) {
canvas.drawRect(rect, paint);
}
@override
bool get preferPaintInterior => true;
@override
void paint(
Canvas canvas,
Rect rect, {
double? gapStart,
double gapExtent = 0.0,
double gapPercentage = 0.0,
TextDirection? textDirection,
}) {
// Do not paint.
}
}

View File

@@ -144,7 +144,7 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
return KeepScope(
keep: navigationItem.keep,
key: Key(navigationItem.label.name),
child: navigationItem.fragment,
child: navigationItem.view,
);
},
);

View File

@@ -357,21 +357,6 @@ class DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {
}
}
@riverpod
class NeedApply extends _$NeedApply with AutoDisposeNotifierMixin {
@override
bool build() {
return globalState.appState.needApply;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
needApply: value,
);
}
}
@riverpod
class ProxiesQuery extends _$ProxiesQuery with AutoDisposeNotifierMixin {
@override

View File

@@ -240,6 +240,53 @@ class ProxiesStyleSetting extends _$ProxiesStyleSetting
}
}
@riverpod
class ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {
@override
ScriptProps build() {
return globalState.config.scriptProps;
}
@override
onUpdate(value) {
globalState.config = globalState.config.copyWith(
scriptProps: value,
);
}
setScript(Script script) {
final list = List<Script>.from(state.scripts);
final index = list.indexWhere((item) => item.id == script.id);
if (index != -1) {
list[index] = script;
} else {
list.add(script);
}
state = state.copyWith(
scripts: list,
);
}
setId(String id) {
state = state.copyWith(
currentId: state.currentId != id ? id : null,
);
}
del(String id) {
final list = List<Script>.from(state.scripts);
final index = list.indexWhere((item) => item.label == id);
if (index != -1) {
list.removeAt(index);
}
final nextId = id == state.currentId ? null : state.currentId;
state = state.copyWith(
scripts: list,
currentId: nextId,
);
}
}
@riverpod
class PatchClashConfig extends _$PatchClashConfig
with AutoDisposeNotifierMixin {

View File

@@ -335,20 +335,6 @@ final delayDataSourceProvider =
);
typedef _$DelayDataSource = AutoDisposeNotifier<DelayMap>;
String _$needApplyHash() => r'62ff248d67b0525c6a55e556fbd29a2044e97766';
/// See also [NeedApply].
@ProviderFor(NeedApply)
final needApplyProvider = AutoDisposeNotifierProvider<NeedApply, bool>.internal(
NeedApply.new,
name: r'needApplyProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$needApplyHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$NeedApply = AutoDisposeNotifier<bool>;
String _$proxiesQueryHash() => r'9f3907e06534b6882684bec47ca3ba2988297e19';
/// See also [ProxiesQuery].

View File

@@ -178,6 +178,21 @@ final proxiesStyleSettingProvider =
);
typedef _$ProxiesStyleSetting = AutoDisposeNotifier<ProxiesStyle>;
String _$scriptStateHash() => r'16d669009ffb233d95b2cb206cf771342ebc1cc1';
/// See also [ScriptState].
@ProviderFor(ScriptState)
final scriptStateProvider =
AutoDisposeNotifierProvider<ScriptState, ScriptProps>.internal(
ScriptState.new,
name: r'scriptStateProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$scriptStateHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$ScriptState = AutoDisposeNotifier<ScriptProps>;
String _$patchClashConfigHash() => r'52906195d85525d6688aec231da8b38c24364494';
/// See also [PatchClashConfig].

View File

@@ -78,23 +78,22 @@ final coreStateProvider = AutoDisposeProvider<CoreState>.internal(
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef CoreStateRef = AutoDisposeProviderRef<CoreState>;
String _$clashConfigStateHash() => r'fbbcd7221b0b9b18db523e59c9021e8e56e119ca';
String _$updateParamsHash() => r'79fd7a5a8650fabac3a2ca7ce903c1d9eb363ed2';
/// See also [clashConfigState].
@ProviderFor(clashConfigState)
final clashConfigStateProvider = AutoDisposeProvider<ClashConfigState>.internal(
clashConfigState,
name: r'clashConfigStateProvider',
debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')
? null
: _$clashConfigStateHash,
/// See also [updateParams].
@ProviderFor(updateParams)
final updateParamsProvider = AutoDisposeProvider<UpdateParams>.internal(
updateParams,
name: r'updateParamsProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$updateParamsHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef ClashConfigStateRef = AutoDisposeProviderRef<ClashConfigState>;
typedef UpdateParamsRef = AutoDisposeProviderRef<UpdateParams>;
String _$proxyStateHash() => r'22478fb593aaca11dfe2cf64472013190475a5bc';
/// See also [proxyState].
@@ -1960,8 +1959,24 @@ class _GenColorSchemeProviderElement
bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;
}
String _$needSetupHash() => r'1116c73bb2964321de63bdca631a983d31e30d69';
/// See also [needSetup].
@ProviderFor(needSetup)
final needSetupProvider = AutoDisposeProvider<VM2>.internal(
needSetup,
name: r'needSetupProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$needSetupHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef NeedSetupRef = AutoDisposeProviderRef<VM2>;
String _$profileOverrideStateHash() =>
r'16d7c75849ed077d60553e5d2bba4ed54b307971';
r'fa26570a355ab39e27b1f93d1d2f358717065592';
/// See also [ProfileOverrideState].
@ProviderFor(ProfileOverrideState)

View File

@@ -72,18 +72,22 @@ CoreState coreState(Ref ref) {
}
@riverpod
ClashConfigState clashConfigState(Ref ref) {
final clashConfig = ref.watch(patchClashConfigProvider);
final overrideDns = ref.watch(overrideDnsProvider);
final overrideData =
ref.watch(currentProfileProvider.select((state) => state?.overrideData));
final routeMode =
ref.watch(networkSettingProvider.select((state) => state.routeMode));
return ClashConfigState(
overrideDns: overrideDns,
clashConfig: clashConfig,
overrideData: overrideData ?? OverrideData(),
routeMode: routeMode,
UpdateParams updateParams(Ref ref) {
return ref.watch(
patchClashConfigProvider.select(
(state) => UpdateParams(
tun: state.tun,
allowLan: state.allowLan,
findProcessMode: state.findProcessMode,
mode: state.mode,
logLevel: state.logLevel,
ipv6: state.ipv6,
tcpConcurrent: state.tcpConcurrent,
externalController: state.externalController,
unifiedDelay: state.unifiedDelay,
mixedPort: state.mixedPort,
),
),
);
}
@@ -499,7 +503,6 @@ class ProfileOverrideState extends _$ProfileOverrideState {
@override
ProfileOverrideStateModel build() {
return ProfileOverrideStateModel(
isEdit: false,
selectedRules: {},
);
}
@@ -585,3 +588,11 @@ ColorScheme genColorScheme(
dynamicSchemeVariant: vm2.b,
);
}
@riverpod
VM2 needSetup(Ref ref) {
final profileId = ref.watch(currentProfileIdProvider);
final content = ref.watch(
scriptStateProvider.select((state) => state.currentScript?.content));
return VM2(a: profileId, b: content);
}

View File

@@ -1,4 +1,5 @@
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:isolate';
@@ -191,20 +192,24 @@ class GlobalState {
);
}
Future<Map<String, dynamic>> getProfileData(String? id) async {
if (id == null || id.isEmpty) {
return {};
}
Future<Map<String, dynamic>> getProfileMap(String id) async {
final profilePath = await appPath.getProfilePath(id);
try {
final data = await Isolate.run<dynamic>(() async {
final res = await Isolate.run<Result<dynamic>>(() async {
try {
final file = File(profilePath);
if (!await file.exists()) {
return Result.error("");
}
final value = await file.readAsString();
return loadYaml(value);
});
return utils.convertLoadYaml(data) as Map<String, dynamic>;
} catch (_) {
return {};
return Result.success(utils.convertYamlNode(loadYaml(value)));
} catch (e) {
return Result.error(e.toString());
}
});
if (res.isSuccess) {
return res.data as Map<String, dynamic>;
} else {
throw res.message;
}
}
@@ -287,34 +292,157 @@ class GlobalState {
);
}
getUpdateConfigParams([bool? isPatch]) {
final currentProfile = config.currentProfile;
final clashConfig = config.patchClashConfig;
Future<SetupParams> getSetupParams({
required ClashConfig pathConfig,
}) async {
final clashConfig = await patchRawConfig(
patchConfig: pathConfig,
);
final params = SetupParams(
config: clashConfig,
selectedMap: config.currentProfile?.selectedMap ?? {},
testUrl: config.appSetting.testUrl,
);
return params;
}
Future<Map<String, dynamic>> patchRawConfig({
required ClashConfig patchConfig,
}) async {
final profile = config.currentProfile;
if (profile == null) {
return {};
}
final profileId = profile.id;
final rawConfig =
await handleEvaluate(await globalState.getProfileMap(profileId));
final routeAddress =
config.networkProps.routeMode == RouteMode.bypassPrivate
? defaultBypassPrivateRouteAddress
: clashConfig.tun.routeAddress;
return UpdateConfigParams(
profileId: config.currentProfileId ?? "",
config: clashConfig.copyWith(
globalUa: ua,
tun: clashConfig.tun.copyWith(
autoRoute: routeAddress.isEmpty ? true : false,
routeAddress: routeAddress,
),
rule: currentProfile?.overrideData.runningRule ?? [],
),
params: ConfigExtendedParams(
isPatch: isPatch ?? false,
selectedMap: currentProfile?.selectedMap ?? {},
overrideDns: config.overrideDns,
testUrl: config.appSetting.testUrl,
overrideRule:
currentProfile?.overrideData.rule.type == OverrideRuleType.override
? true
: false,
),
: patchConfig.tun.routeAddress;
final realPatchConfig = patchConfig.copyWith.tun(
autoRoute: routeAddress.isEmpty ? true : false,
routeAddress: routeAddress,
);
rawConfig["external-controller"] = realPatchConfig.externalController.value;
rawConfig["external-ui"] = "";
rawConfig["interface-name"] = "";
rawConfig["external-ui-url"] = "";
rawConfig["tcp-concurrent"] = realPatchConfig.tcpConcurrent;
rawConfig["unified-delay"] = realPatchConfig.unifiedDelay;
rawConfig["ipv6"] = realPatchConfig.ipv6;
rawConfig["log-level"] = realPatchConfig.logLevel.name;
rawConfig["port"] = 0;
rawConfig["socks-port"] = 0;
rawConfig["keep-alive-interval"] = realPatchConfig.keepAliveInterval;
rawConfig["mixed-port"] = realPatchConfig.mixedPort;
rawConfig["find-process-mode"] = realPatchConfig.findProcessMode.name;
rawConfig["allow-lan"] = realPatchConfig.allowLan;
rawConfig["mode"] = realPatchConfig.mode.name;
if (rawConfig["tun"] == null) {
rawConfig["tun"] = {};
}
rawConfig["tun"]["enable"] = realPatchConfig.tun.enable;
rawConfig["tun"]["device"] = realPatchConfig.tun.device;
rawConfig["tun"]["dns-hijack"] = realPatchConfig.tun.dnsHijack;
rawConfig["tun"]["stack"] = realPatchConfig.tun.stack.name;
rawConfig["tun"]["route-address"] = realPatchConfig.tun.routeAddress;
rawConfig["tun"]["auto-route"] = realPatchConfig.tun.autoRoute;
rawConfig["geodata-loader"] = realPatchConfig.geodataLoader.name;
if (rawConfig["sniffer"] != null) {
rawConfig["sniffer"] = Sniffer.fromJson(rawConfig["sniffer"]);
}
if (rawConfig["profile"] == null) {
rawConfig["profile"] = {};
}
if (rawConfig["proxy-providers"] != null) {
final proxyProviders = rawConfig["proxy-providers"] as Map;
for (final key in proxyProviders.keys) {
final proxyProvider = proxyProviders[key];
if (proxyProvider["path"] != null) {
proxyProvider["path"] = await appPath.getProvidersPath(profile.id,
filePath: proxyProvider["path"]);
}
}
}
if (rawConfig["rule-providers"] != null) {
final ruleProviders = rawConfig["rule-providers"] as Map;
for (final key in ruleProviders.keys) {
final ruleProvider = ruleProviders[key];
if (ruleProvider["path"] != null) {
ruleProvider["path"] = await appPath.getProvidersPath(profile.id,
filePath: ruleProvider["path"]);
}
}
}
rawConfig["profile"]["store-selected"] = false;
rawConfig["geox-url"] = realPatchConfig.geoXUrl.toJson();
rawConfig["global-ua"] = realPatchConfig.globalUa;
if (rawConfig["hosts"] == null) {
rawConfig["hosts"] = {};
}
for (final host in realPatchConfig.hosts.entries) {
rawConfig["hosts"][host.key] = host.value.splitByMultipleSeparators;
}
final overrideDns = globalState.config.overrideDns;
if (overrideDns) {
rawConfig["dns"] = realPatchConfig.dns.toJson();
rawConfig["dns"]["nameserver-policy"] = {};
for (final entry in realPatchConfig.dns.nameserverPolicy.entries) {
rawConfig["dns"]["nameserver-policy"][entry.key] =
entry.value.splitByMultipleSeparators;
}
} else {
if (rawConfig["dns"] == null) {
rawConfig["dns"] = {};
}
if (rawConfig["dns"]["enable"] != false) {
rawConfig["dns"]["enable"] = true;
}
}
var rules = [];
// if (rawConfig["rule"] != null) {
// rules.addAll(rawConfig["rule"]);
// }
if (rawConfig["rules"] != null) {
rules.addAll(rawConfig["rules"]);
}
rawConfig.remove("rules");
final overrideData = profile.overrideData;
if (overrideData.enable && config.scriptProps.currentScript == null) {
if (overrideData.rule.type == OverrideRuleType.override) {
rules = overrideData.runningRule;
} else {
rules.addAll(overrideData.runningRule);
}
}
rawConfig["rule"] = rules;
return rawConfig;
}
Future<Map<String, dynamic>> handleEvaluate(
Map<String, dynamic> config,
) async {
final currentScript = globalState.config.scriptProps.currentScript;
if (currentScript == null) {
return config;
}
final configJs = json.encode(config);
final runtime = js.runTime;
final res = await js.runTime.evaluateAsync("""
${currentScript.content}
main($configJs)
""");
if (res.isError) {
throw res.stringResult;
}
final value = runtime.convertValue<Map<String, dynamic>>(res) ?? config;
return value;
}
}

View File

@@ -20,8 +20,8 @@ class Contributor {
});
}
class AboutFragment extends StatelessWidget {
const AboutFragment({super.key});
class AboutView extends StatelessWidget {
const AboutView({super.key});
_checkUpdate(BuildContext context) async {
final commonScaffoldState = context.commonScaffoldState;

View File

@@ -1,10 +1,10 @@
import 'dart:async';
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:fl_clash/plugins/app.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
@@ -12,14 +12,14 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AccessFragment extends ConsumerStatefulWidget {
const AccessFragment({super.key});
class AccessView extends ConsumerStatefulWidget {
const AccessView({super.key});
@override
ConsumerState<AccessFragment> createState() => _AccessFragmentState();
ConsumerState<AccessView> createState() => _AccessViewState();
}
class _AccessFragmentState extends ConsumerState<AccessFragment> {
class _AccessViewState extends ConsumerState<AccessView> {
List<String> acceptList = [];
List<String> rejectList = [];
late ScrollController _controller;

View File

@@ -257,8 +257,8 @@ class AutoCheckUpdateItem extends ConsumerWidget {
}
}
class ApplicationSettingFragment extends StatelessWidget {
const ApplicationSettingFragment({super.key});
class ApplicationSettingView extends StatelessWidget {
const ApplicationSettingView({super.key});
String getLocaleString(Locale? locale) {
if (locale == null) return appLocalizations.defaultText;

View File

@@ -439,7 +439,7 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.accountTip;
return appLocalizations.emptyTip(appLocalizations.account);
}
return null;
},
@@ -465,7 +465,8 @@ class _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {
),
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.passwordTip;
return appLocalizations
.emptyTip(appLocalizations.password);
}
return null;
},

View File

@@ -1,23 +1,22 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/fragments/config/dns.dart';
import 'package:fl_clash/fragments/config/general.dart';
import 'package:fl_clash/fragments/config/network.dart';
import 'package:fl_clash/models/clash_config.dart';
import 'package:fl_clash/providers/config.dart' show patchClashConfigProvider;
import 'package:fl_clash/state.dart';
import 'package:fl_clash/views/config/dns.dart';
import 'package:fl_clash/views/config/general.dart';
import 'package:fl_clash/views/config/network.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../../state.dart';
class ConfigFragment extends StatefulWidget {
const ConfigFragment({super.key});
class ConfigView extends StatefulWidget {
const ConfigView({super.key});
@override
State<ConfigFragment> createState() => _ConfigFragmentState();
State<ConfigView> createState() => _ConfigViewState();
}
class _ConfigFragmentState extends State<ConfigFragment> {
class _ConfigViewState extends State<ConfigView> {
@override
Widget build(BuildContext context) {
List<Widget> items = [

View File

@@ -59,6 +59,12 @@ class ListenItem extends ConsumerWidget {
delegate: InputDelegate(
title: appLocalizations.listen,
value: listen,
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.listen);
}
return null;
},
onChanged: (String? value) {
if (value == null) {
return;
@@ -182,6 +188,12 @@ class FakeIpRangeItem extends ConsumerWidget {
delegate: InputDelegate(
title: appLocalizations.fakeipRange,
value: fakeIpRange,
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.fakeipRange);
}
return null;
},
onChanged: (String? value) {
if (value == null) {
return;
@@ -487,6 +499,12 @@ class GeoipCodeItem extends ConsumerWidget {
delegate: InputDelegate(
title: appLocalizations.geoipCode,
value: geoipCode,
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.geoipCode);
}
return null;
},
onChanged: (String? value) {
if (value == null) {
return;

View File

@@ -2,7 +2,6 @@ import 'package:fl_clash/common/common.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:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@@ -86,25 +85,26 @@ class KeepAliveIntervalItem extends ConsumerWidget {
suffixText: appLocalizations.seconds,
resetValue: "$defaultKeepAliveInterval",
value: "$keepAliveInterval",
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.interval);
}
final intValue = int.tryParse(value);
if (intValue == null) {
return appLocalizations.numberTip(appLocalizations.interval);
}
return null;
},
onChanged: (String? value) {
if (value == null) {
return;
}
globalState.safeRun(
() {
final intValue = int.parse(value);
if (intValue <= 0) {
throw "Invalid keepAliveInterval";
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
keepAliveInterval: intValue,
),
);
},
silence: false,
title: appLocalizations.keepAliveIntervalDesc,
);
final intValue = int.parse(value);
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
keepAliveInterval: intValue,
),
);
},
),
);
@@ -123,28 +123,29 @@ class TestUrlItem extends ConsumerWidget {
title: Text(appLocalizations.testUrl),
subtitle: Text(testUrl),
delegate: InputDelegate(
resetValue: defaultTestUrl,
title: appLocalizations.testUrl,
value: testUrl,
onChanged: (String? value) {
if (value == null) {
return;
}
globalState.safeRun(
() {
if (!value.isUrl) {
throw "Invalid url";
}
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
testUrl: value,
),
);
},
silence: false,
title: appLocalizations.testUrl,
);
}),
resetValue: defaultTestUrl,
title: appLocalizations.testUrl,
value: testUrl,
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.testUrl);
}
if (!value.isUrl) {
return appLocalizations.urlTip(appLocalizations.testUrl);
}
return null;
},
onChanged: (String? value) {
if (value == null) {
return;
}
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
testUrl: value,
),
);
},
),
);
}
}
@@ -163,25 +164,29 @@ class MixedPortItem extends ConsumerWidget {
delegate: InputDelegate(
title: appLocalizations.proxyPort,
value: "$mixedPort",
validator: (String? value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.proxyPort);
}
final mixedPort = int.tryParse(value);
if (mixedPort == null) {
return appLocalizations.numberTip(appLocalizations.proxyPort);
}
if (mixedPort < 1024 || mixedPort > 49151) {
return appLocalizations.proxyPortTip;
}
return null;
},
onChanged: (String? value) {
if (value == null) {
return;
}
globalState.safeRun(
() {
final mixedPort = int.parse(value);
if (mixedPort < 1024 || mixedPort > 49151) {
throw "Invalid port";
}
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
mixedPort: mixedPort,
),
);
},
silence: false,
title: appLocalizations.proxyPort,
);
final mixedPort = int.parse(value);
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(
mixedPort: mixedPort,
),
);
},
resetValue: "$defaultMixedPort",
),

View File

@@ -11,15 +11,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'item.dart';
class ConnectionsFragment extends ConsumerStatefulWidget {
const ConnectionsFragment({super.key});
class ConnectionsView extends ConsumerStatefulWidget {
const ConnectionsView({super.key});
@override
ConsumerState<ConnectionsFragment> createState() =>
_ConnectionsFragmentState();
ConsumerState<ConnectionsView> createState() => _ConnectionsViewState();
}
class _ConnectionsFragmentState extends ConsumerState<ConnectionsFragment>
class _ConnectionsViewState extends ConsumerState<ConnectionsView>
with PageMixin {
final _connectionsStateNotifier = ValueNotifier<ConnectionsState>(
const ConnectionsState(),
@@ -60,13 +59,15 @@ class _ConnectionsFragmentState extends ConsumerState<ConnectionsFragment>
_updateConnections() async {
WidgetsBinding.instance.addPostFrameCallback((_) async {
_connectionsStateNotifier.value =
_connectionsStateNotifier.value.copyWith(
connections: await clashCore.getConnections(),
);
timer = Timer(Duration(seconds: 1), () async {
_updateConnections();
});
if (mounted) {
_connectionsStateNotifier.value =
_connectionsStateNotifier.value.copyWith(
connections: await clashCore.getConnections(),
);
timer = Timer(Duration(seconds: 1), () async {
_updateConnections();
});
}
});
}
@@ -113,7 +114,7 @@ class _ConnectionsFragmentState extends ConsumerState<ConnectionsFragment>
final connections = state.list;
if (connections.isEmpty) {
return NullStatus(
label: appLocalizations.nullConnectionsDesc,
label: appLocalizations.nullTip(appLocalizations.connections),
);
}
return CommonScrollBar(

View File

@@ -78,29 +78,29 @@ class ConnectionItem extends ConsumerWidget {
);
return CommonPopupBox(
targetBuilder: (open) {
openPopup(Offset offset) {
open(
offset: offset.translate(
0,
0,
),
);
}
// openPopup(Offset offset) {
// open(
// offset: offset.translate(
// 0,
// 0,
// ),
// );
// }
return InkWell(
child: GestureDetector(
onLongPressStart: (details) {
if (!system.isDesktop) {
return;
}
openPopup(details.localPosition);
},
onSecondaryTapDown: (details) {
if (!system.isDesktop) {
return;
}
openPopup(details.localPosition);
},
// onLongPressStart: (details) {
// if (!system.isDesktop) {
// return;
// }
// openPopup(details.localPosition);
// },
// onSecondaryTapDown: (details) {
// if (!system.isDesktop) {
// return;
// }
// openPopup(details.localPosition);
// },
child: ListItem(
padding: const EdgeInsets.symmetric(
horizontal: 16,

View File

@@ -11,15 +11,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'item.dart';
class RequestsFragment extends ConsumerStatefulWidget {
const RequestsFragment({super.key});
class RequestsView extends ConsumerStatefulWidget {
const RequestsView({super.key});
@override
ConsumerState<RequestsFragment> createState() => _RequestsFragmentState();
ConsumerState<RequestsView> createState() => _RequestsViewState();
}
class _RequestsFragmentState extends ConsumerState<RequestsFragment>
with PageMixin {
class _RequestsViewState extends ConsumerState<RequestsView> with PageMixin {
final _requestsStateNotifier = ValueNotifier<ConnectionsState>(
const ConnectionsState(loading: true),
);
@@ -120,7 +119,7 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
return;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
if(mounted){
if (mounted) {
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
connections: _requests,
);
@@ -187,11 +186,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
builder: (_, state, __) {
_preLoad();
final connections = state.list;
if (connections.isEmpty) {
return NullStatus(
label: appLocalizations.nullRequestsDesc,
);
}
final items = connections
.map<Widget>(
(connection) => ConnectionItem(
@@ -210,7 +204,8 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
.toList();
final content = connections.isEmpty
? NullStatus(
label: appLocalizations.nullRequestsDesc,
label:
appLocalizations.nullTip(appLocalizations.requests),
)
: Align(
alignment: Alignment.topCenter,

View File

@@ -9,15 +9,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'widgets/start_button.dart';
class DashboardFragment extends ConsumerStatefulWidget {
const DashboardFragment({super.key});
class DashboardView extends ConsumerStatefulWidget {
const DashboardView({super.key});
@override
ConsumerState<DashboardFragment> createState() => _DashboardFragmentState();
ConsumerState<DashboardView> createState() => _DashboardViewState();
}
class _DashboardFragmentState extends ConsumerState<DashboardFragment>
with PageMixin {
class _DashboardViewState extends ConsumerState<DashboardView> with PageMixin {
final key = GlobalKey<SuperGridState>();
@override

View File

@@ -1,6 +1,6 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/fragments/config/network.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/views/config/network.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

View File

@@ -16,8 +16,8 @@ extension IntlExt on Intl {
Intl.message("action_$messageText");
}
class HotKeyFragment extends StatelessWidget {
const HotKeyFragment({super.key});
class HotKeyView extends StatelessWidget {
const HotKeyView({super.key});
String getSubtitle(HotKeyAction hotKeyAction) {
final key = hotKeyAction.key;

View File

@@ -10,14 +10,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../models/models.dart';
import '../widgets/widgets.dart';
class LogsFragment extends ConsumerStatefulWidget {
const LogsFragment({super.key});
class LogsView extends ConsumerStatefulWidget {
const LogsView({super.key});
@override
ConsumerState<LogsFragment> createState() => _LogsFragmentState();
ConsumerState<LogsView> createState() => _LogsViewState();
}
class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
class _LogsViewState extends ConsumerState<LogsView> with PageMixin {
final _logsStateNotifier = ValueNotifier<LogsState>(
LogsState(loading: true),
);
@@ -213,7 +213,9 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
.toList();
final content = logs.isEmpty
? NullStatus(
label: appLocalizations.nullLogsDesc,
label: appLocalizations.nullTip(
appLocalizations.logs,
),
)
: Align(
alignment: Alignment.topCenter,

View File

@@ -4,10 +4,10 @@ import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
class AddProfile extends StatelessWidget {
class AddProfileView extends StatelessWidget {
final BuildContext context;
const AddProfile({
const AddProfileView({
super.key,
required this.context,
});
@@ -38,7 +38,21 @@ class AddProfile extends StatelessWidget {
_toAdd() async {
final url = await globalState.showCommonDialog<String>(
child: const URLFormDialog(),
child: InputDialog(
autovalidateMode: AutovalidateMode.onUnfocus,
title: appLocalizations.importFromURL,
labelText: appLocalizations.url,
value: '',
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip("").trim();
}
if (!value.isUrl) {
return appLocalizations.urlTip("").trim();
}
return null;
},
),
);
if (url != null) {
_handleAddProfileFormURL(url);

View File

@@ -11,21 +11,21 @@ import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
class EditProfile extends StatefulWidget {
class EditProfileView extends StatefulWidget {
final Profile profile;
final BuildContext context;
const EditProfile({
const EditProfileView({
super.key,
required this.context,
required this.profile,
});
@override
State<EditProfile> createState() => _EditProfileState();
State<EditProfileView> createState() => _EditProfileViewState();
}
class _EditProfileState extends State<EditProfile> {
class _EditProfileViewState extends State<EditProfileView> {
late TextEditingController labelController;
late TextEditingController urlController;
late TextEditingController autoUpdateDurationController;
@@ -149,30 +149,33 @@ class _EditProfileState extends State<EditProfile> {
}
if (!mounted) return;
final title = widget.profile.label ?? widget.profile.id;
final data = await BaseNavigator.push<String>(
final editorPage = EditorPage(
title: title,
content: rawText!,
onSave: (context, _, content) {
_handleSaveEdit(context, content);
},
onPop: (context, _, content) async {
if (content == rawText) {
return true;
}
final res = await globalState.showMessage(
title: title,
message: TextSpan(
text: appLocalizations.hasCacheChange,
),
);
if (res == true && context.mounted) {
_handleSaveEdit(context, content);
} else {
return true;
}
return false;
},
);
final data = await BaseNavigator.modal<String>(
globalState.homeScaffoldKey.currentContext!,
EditorPage(
title: title,
content: rawText!,
onSave: _handleSaveEdit,
onPop: (context, data) async {
if (data == rawText) {
return true;
}
final res = await globalState.showMessage(
title: title,
message: TextSpan(
text: appLocalizations.hasCacheChange,
),
);
if (res == true && context.mounted) {
_handleSaveEdit(context, data);
} else {
return true;
}
return false;
},
),
editorPage,
);
if (data == null) {
return;

View File

@@ -1,6 +1,4 @@
import 'dart:ui';
import 'package:fl_clash/clash/core.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
@@ -10,26 +8,27 @@ import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class OverrideProfile extends StatefulWidget {
class OverrideProfileView extends StatefulWidget {
final String profileId;
const OverrideProfile({
const OverrideProfileView({
super.key,
required this.profileId,
});
@override
State<OverrideProfile> createState() => _OverrideProfileState();
State<OverrideProfileView> createState() => _OverrideProfileViewState();
}
class _OverrideProfileState extends State<OverrideProfile> {
class _OverrideProfileViewState extends State<OverrideProfileView> {
final _controller = ScrollController();
double _currentMaxWidth = 0;
_initState(WidgetRef ref) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Future.delayed(Duration(milliseconds: 300), () async {
final snippet = await clashCore.getProfile(widget.profileId);
final rawConfig = await clashCore.getConfig(widget.profileId);
final snippet = ClashConfigSnippet.fromJson(rawConfig);
final overrideData = ref.read(
getProfileOverrideDataProvider(widget.profileId),
);
@@ -44,19 +43,23 @@ class _OverrideProfileState extends State<OverrideProfile> {
}
_handleSave(WidgetRef ref, OverrideData overrideData) {
ref.read(needApplyProvider.notifier).value = true;
ref.read(profilesProvider.notifier).updateProfile(
widget.profileId,
(state) => state.copyWith(
overrideData: overrideData,
),
);
globalState.appController.setupClashConfigDebounce();
}
_handleDelete(WidgetRef ref) async {
final res = await globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(text: appLocalizations.deleteRuleTip),
message: TextSpan(
text: appLocalizations.deleteMultipTip(
appLocalizations.rule,
),
),
);
if (res != true) {
return;
@@ -81,7 +84,9 @@ class _OverrideProfileState extends State<OverrideProfile> {
},
);
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(isEdit: false, selectedRules: {}),
(state) => state.copyWith(
selectedRules: {},
),
);
}
@@ -116,7 +121,39 @@ class _OverrideProfileState extends State<OverrideProfile> {
slivers: [
SliverToBoxAdapter(
child: SizedBox(
height: 16,
height: 8,
),
),
SliverToBoxAdapter(
child: Consumer(
builder: (_, ref, child) {
final scriptMode = ref.watch(scriptStateProvider
.select((state) => state.realId != null));
if (!scriptMode) {
return SizedBox();
}
return child!;
},
child: ListItem(
padding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 0,
),
title: Row(
spacing: 8,
children: [
Icon(Icons.info),
Text(
appLocalizations.overrideInvalidTip,
)
],
),
),
),
),
SliverToBoxAdapter(
child: SizedBox(
height: 8,
),
),
SliverPadding(
@@ -168,16 +205,12 @@ class _OverrideProfileState extends State<OverrideProfile> {
},
child: Consumer(
builder: (_, ref, ___) {
final vm2 = ref.watch(
final editCount = ref.watch(
profileOverrideStateProvider.select(
(state) => VM2(
a: state.isEdit,
b: state.selectedRules.length,
),
(state) => state.selectedRules.length,
),
);
final isEdit = vm2.a;
final editCount = vm2.b;
final isEdit = editCount != 0;
return CommonScaffold(
title: appLocalizations.override,
body: _buildContent(),
@@ -271,7 +304,6 @@ class _OverrideProfileState extends State<OverrideProfile> {
onExit: () {
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(
isEdit: false,
selectedRules: {},
),
);
@@ -347,7 +379,7 @@ class RuleTitle extends ConsumerWidget {
(state) {
final overrideRule = state.overrideData?.rule;
return VM3(
a: state.isEdit,
a: state.selectedRules.isNotEmpty,
b: state.selectedRules.containsAll(
overrideRule?.rules.map((item) => item.id).toSet() ?? {},
),
@@ -445,87 +477,59 @@ class RuleContent extends ConsumerWidget {
required this.maxWidth,
});
Widget _proxyDecorator(
Widget child,
int index,
Animation<double> animation,
) {
return AnimatedBuilder(
animation: animation,
builder: (_, Widget? child) {
final double animValue = Curves.easeInOut.transform(animation.value);
final double scale = lerpDouble(1, 1.02, animValue)!;
return Transform.scale(
scale: scale,
child: child,
);
},
child: child,
Widget _buildItem({
required Rule rule,
required bool isSelected,
required VoidCallback onTab,
required BuildContext context,
}) {
return Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.symmetric(
vertical: 4,
),
child: CommonCard(
padding: EdgeInsets.zero,
radius: 18,
type: CommonCardType.filled,
isSelected: isSelected,
// decoration: BoxDecoration(
// color: isSelected
// ? context.colorScheme.secondaryContainer.opacity80
// : context.colorScheme.surfaceContainer,
// borderRadius: BorderRadius.circular(18),
// ),
onPressed: () {
onTab();
},
child: ListTile(
minTileHeight: 0,
minVerticalPadding: 0,
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
trailing: SizedBox(
width: 24,
height: 24,
child: CommonCheckBox(
value: isSelected,
isCircle: true,
onChanged: (_) {
onTab();
},
),
),
title: Text(rule.value),
),
),
),
);
}
Widget _buildItem(Rule rule, int index) {
return Consumer(
builder: (context, ref, ___) {
final vm2 = ref.watch(profileOverrideStateProvider.select(
(item) => VM2(
a: item.isEdit,
b: item.selectedRules.contains(rule.id),
),
));
final isEdit = vm2.a;
final isSelected = vm2.b;
return Material(
color: Colors.transparent,
child: Container(
margin: EdgeInsets.symmetric(
vertical: 4,
),
alignment: Alignment.center,
decoration: BoxDecoration(
color: isSelected
? context.colorScheme.secondaryContainer.opacity80
: context.colorScheme.surfaceContainer,
borderRadius: BorderRadius.circular(18),
),
clipBehavior: Clip.hardEdge,
child: ListTile(
minTileHeight: 0,
minVerticalPadding: 0,
titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,
contentPadding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
trailing: SizedBox(
width: 24,
height: 24,
child: !isEdit
? ReorderableDragStartListener(
index: index,
child: const Icon(Icons.drag_handle),
)
: CommonCheckBox(
value: isSelected,
isCircle: true,
onChanged: (_) {
_handleSelect(ref, rule);
},
),
),
title: Text(rule.value),
),
),
);
},
);
}
_handleSelect(WidgetRef ref, ruleId) {
if (!ref.read(profileOverrideStateProvider).isEdit) {
return;
}
_handleSelect(WidgetRef ref, String ruleId) {
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) {
final newSelectedRules = Set<String>.from(state.selectedRules);
@@ -543,19 +547,21 @@ class RuleContent extends ConsumerWidget {
@override
Widget build(BuildContext context, ref) {
final vm2 = ref.watch(
final vm3 = ref.watch(
profileOverrideStateProvider.select(
(state) {
final overrideRule = state.overrideData?.rule;
return VM2(
return VM3(
a: overrideRule?.rules ?? [],
b: overrideRule?.type ?? OverrideRuleType.added,
c: state.selectedRules,
);
},
),
);
final rules = vm2.a;
final type = vm2.b;
final rules = vm3.a;
final type = vm3.b;
final selectedRules = vm3.c;
if (rules.isEmpty) {
return SliverToBoxAdapter(
child: SizedBox(
@@ -592,31 +598,20 @@ class RuleContent extends ConsumerWidget {
tag: CacheTag.rules,
itemBuilder: (context, index) {
final rule = rules[index];
return GestureDetector(
return ReorderableDragStartListener(
key: ObjectKey(rule),
index: index,
child: _buildItem(
rule,
index,
rule: rule,
isSelected: selectedRules.contains(rule.id),
onTab: () {
_handleSelect(ref, rule.id);
},
context: context,
),
onTap: () {
_handleSelect(ref, rule.id);
},
onLongPress: () {
if (ref.read(profileOverrideStateProvider).isEdit) {
return;
}
ref.read(profileOverrideStateProvider.notifier).updateState(
(state) => state.copyWith(
isEdit: true,
selectedRules: {
rule.id,
},
),
);
},
);
},
proxyDecorator: _proxyDecorator,
proxyDecorator: proxyDecorator,
itemCount: rules.length,
onReorder: (oldIndex, newIndex) {
if (oldIndex < newIndex) {
@@ -818,7 +813,8 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
? FormField(
validator: (_) {
if (_ruleProviderController.text.isEmpty) {
return appLocalizations.ruleProviderEmptyTip;
return appLocalizations
.emptyTip(appLocalizations.ruleProviders);
}
return null;
},
@@ -841,7 +837,8 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
),
validator: (_) {
if (_contentController.text.isEmpty) {
return appLocalizations.contentEmptyTip;
return appLocalizations
.emptyTip(appLocalizations.content);
}
return null;
},
@@ -853,7 +850,8 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
? FormField(
validator: (_) {
if (_subRuleController.text.isEmpty) {
return appLocalizations.subRuleEmptyTip;
return appLocalizations
.emptyTip(appLocalizations.subRule);
}
return null;
},
@@ -872,7 +870,9 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
: FormField<String>(
validator: (_) {
if (_ruleTargetController.text.isEmpty) {
return appLocalizations.ruleTargetEmptyTip;
return appLocalizations.emptyTip(
appLocalizations.ruleTarget,
);
}
return null;
},

View File

@@ -2,25 +2,26 @@ import 'dart:ui';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/fragments/profiles/edit_profile.dart';
import 'package:fl_clash/fragments/profiles/override_profile.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/views/profiles/edit_profile.dart';
import 'package:fl_clash/views/profiles/override_profile.dart';
import 'package:fl_clash/views/profiles/scripts.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'add_profile.dart';
class ProfilesFragment extends StatefulWidget {
const ProfilesFragment({super.key});
class ProfilesView extends StatefulWidget {
const ProfilesView({super.key});
@override
State<ProfilesFragment> createState() => _ProfilesFragmentState();
State<ProfilesView> createState() => _ProfilesViewState();
}
class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
class _ProfilesViewState extends State<ProfilesView> with PageMixin {
Function? applyConfigDebounce;
_handleShowAddExtendPage() {
@@ -29,7 +30,7 @@ class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: AddProfile(
body: AddProfileView(
context: globalState.navigatorKey.currentState!.context,
),
title: "${appLocalizations.add}${appLocalizations.profile}",
@@ -82,6 +83,26 @@ class _ProfilesFragmentState extends State<ProfilesFragment> with PageMixin {
},
icon: const Icon(Icons.sync),
),
IconButton(
onPressed: () {
showExtend(
context,
builder: (_, type) {
return ScriptsView();
},
);
},
icon: Consumer(
builder: (context, ref, __) {
final isScriptMode = ref.watch(
scriptStateProvider.select((state) => state.realId != null));
return Icon(
Icons.functions,
color: isScriptMode ? context.colorScheme.primary : null,
);
},
),
),
IconButton(
onPressed: () {
final profiles = globalState.config.profiles;
@@ -179,7 +200,7 @@ class ProfileItem extends StatelessWidget {
final res = await globalState.showMessage(
title: appLocalizations.tip,
message: TextSpan(
text: appLocalizations.deleteProfileTip,
text: appLocalizations.deleteTip(appLocalizations.profile),
),
);
if (res != true) {
@@ -216,7 +237,7 @@ class ProfileItem extends StatelessWidget {
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: EditProfile(
body: EditProfileView(
profile: profile,
context: context,
),
@@ -286,11 +307,12 @@ class ProfileItem extends StatelessWidget {
}
_handlePushGenProfilePage(BuildContext context, String id) {
BaseNavigator.push(
final overrideProfileView = OverrideProfileView(
profileId: id,
);
BaseNavigator.modal(
context,
OverrideProfile(
profileId: id,
),
overrideProfileView,
);
}
@@ -472,7 +494,10 @@ class _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {
)
],
body: Padding(
padding: EdgeInsets.only(bottom: 32),
padding: EdgeInsets.only(
bottom: 32,
top: 16,
),
child: ReorderableListView.builder(
buildDefaultDragHandles: false,
padding: const EdgeInsets.symmetric(

View File

@@ -0,0 +1,239 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/pages/editor.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/card.dart';
import 'package:fl_clash/widgets/input.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:fl_clash/widgets/null_status.dart';
import 'package:fl_clash/widgets/popup.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ScriptsView extends ConsumerStatefulWidget {
const ScriptsView({super.key});
@override
ConsumerState<ScriptsView> createState() => _ScriptsViewState();
}
class _ScriptsViewState extends ConsumerState<ScriptsView> {
_handleDelScript(String label) async {
final res = await globalState.showMessage(
message:
TextSpan(text: appLocalizations.deleteTip(appLocalizations.script)),
);
if (res != true) {
return;
}
ref.read(scriptStateProvider.notifier).del(label);
}
Widget _buildContent() {
return Consumer(builder: (_, ref, __) {
final vm2 = ref.watch(scriptStateProvider.select(
(state) => VM2(a: state.currentId, b: state.scripts),
));
final currentId = vm2.a;
final scripts = vm2.b;
if (scripts.isEmpty) {
return NullStatus(
label: appLocalizations.nullTip(appLocalizations.script),
);
}
return ListView.builder(
padding: kMaterialListPadding.copyWith(
bottom: 16 + 64,
),
itemCount: scripts.length,
itemBuilder: (_, index) {
final script = scripts[index];
return Container(
padding: kTabLabelPadding,
margin: EdgeInsets.symmetric(
vertical: 6,
),
child: CommonCard(
type: CommonCardType.filled,
radius: 16,
child: ListItem.radio(
padding: const EdgeInsets.only(
left: 12,
right: 12,
),
title: Text(script.label),
delegate: RadioDelegate(
value: script.id,
groupValue: currentId,
onChanged: (_) {
ref.read(scriptStateProvider.notifier).setId(
script.id,
);
},
),
trailing: CommonPopupBox(
targetBuilder: (open) {
return IconButton(
onPressed: () {
open();
},
icon: Icon(
Icons.more_vert,
),
);
},
popup: CommonPopupMenu(
items: [
PopupMenuItemData(
icon: Icons.edit,
label: appLocalizations.edit,
onPressed: () {
_handleToEditor(
script: script,
);
},
),
PopupMenuItemData(
icon: Icons.delete,
label: appLocalizations.delete,
onPressed: () {
_handleDelScript(
script.label,
);
},
),
],
),
),
),
),
);
},
);
});
}
_handleEditorSave(_, String title, String content, {Script? script}) async {
Script newScript = script?.copyWith(
label: title,
content: content,
) ??
Script.create(
label: title,
content: content,
);
if (newScript.label.isEmpty) {
final res = await globalState.showCommonDialog<String>(
child: InputDialog(
title: appLocalizations.save,
value: "",
hintText: appLocalizations.pleaseEnterScriptName,
validator: (value) {
if (value == null || value.isEmpty) {
return appLocalizations.emptyTip(appLocalizations.name);
}
if (value != script?.label) {
final index = ref
.read(scriptStateProvider.select((state) => state.scripts))
.indexWhere(
(item) => item.label == value,
);
if (index != -1) {
return appLocalizations.existsTip(
appLocalizations.name,
);
}
}
return null;
},
),
);
if (res == null || res.isEmpty) {
return;
}
newScript = newScript.copyWith(label: res);
}
ref.read(scriptStateProvider.notifier).setScript(newScript);
if (mounted) {
Navigator.of(context).pop();
}
}
Future<bool> _handleEditorPop(
_,
String title,
String content,
String raw, {
Script? script,
}) async {
if (content == raw) {
return true;
}
final res = await globalState.showMessage(
message: TextSpan(
text: appLocalizations.saveChanges,
),
);
if (res == true && mounted) {
_handleEditorSave(
context,
title,
content,
script: script,
);
} else {
return true;
}
return false;
}
_handleToEditor({Script? script}) {
final title = script?.label ?? "";
final raw = script?.content ?? scriptTemplate;
BaseNavigator.modal(
context,
EditorPage(
titleEditable: true,
title: title,
onSave: (context, title, content) {
_handleEditorSave(
context,
title,
content,
script: script,
);
},
onPop: (context, title, content) {
return _handleEditorPop(
context,
title,
content,
raw,
script: script,
);
},
languages: const [
Language.javaScript,
],
content: raw,
),
);
}
@override
Widget build(BuildContext context) {
return CommonScaffold(
floatingActionButton: FloatingActionButton(
onPressed: () {
_handleToEditor();
},
child: Icon(Icons.add),
),
body: _buildContent(),
title: appLocalizations.script,
);
}
}

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