Add windows server mode start process verify

Add linux deb dependencies

Add backup recovery strategy select

Support custom text scaling

Optimize the display of different text scale

Optimize windows setup experience

Optimize startTun performance

Optimize android tv experience

Optimize default option

Optimize computed text size

Optimize hyperOS freeform window

Add developer mode

Update core

Optimize more details
This commit is contained in:
chen08209
2025-04-18 17:50:46 +08:00
parent fb9d0cb22c
commit 676f2d058a
128 changed files with 5455 additions and 1737 deletions

View File

@@ -1,6 +1,5 @@
import 'dart:async';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/l10n/l10n.dart';
@@ -14,7 +13,6 @@ import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'controller.dart';
import 'models/models.dart';
import 'pages/pages.dart';
class Application extends ConsumerStatefulWidget {
@@ -27,7 +25,6 @@ class Application extends ConsumerStatefulWidget {
}
class ApplicationState extends ConsumerState<Application> {
late ColorSchemes systemColorSchemes;
Timer? _autoUpdateGroupTaskTimer;
Timer? _autoUpdateProfilesTaskTimer;
@@ -132,19 +129,6 @@ class ApplicationState extends ConsumerState<Application> {
);
}
_updateSystemColorSchemes(
ColorScheme? lightDynamic,
ColorScheme? darkDynamic,
) {
systemColorSchemes = ColorSchemes(
lightColorScheme: lightDynamic,
darkColorScheme: darkDynamic,
);
WidgetsBinding.instance.addPostFrameCallback((_) {
globalState.appController.updateSystemColorSchemes(systemColorSchemes);
});
}
@override
Widget build(context) {
return _buildPlatformState(
@@ -154,49 +138,44 @@ class ApplicationState extends ConsumerState<Application> {
final locale =
ref.watch(appSettingProvider.select((state) => state.locale));
final themeProps = ref.watch(themeSettingProvider);
return DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
_updateSystemColorSchemes(lightDynamic, darkDynamic);
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: globalState.navigatorKey,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
builder: (_, child) {
return AppEnvManager(
child: _buildPlatformApp(
_buildApp(child!),
),
);
},
scrollBehavior: BaseScrollBehavior(),
title: appName,
locale: utils.getLocaleForString(locale),
supportedLocales: AppLocalizations.delegate.supportedLocales,
themeMode: themeProps.themeMode,
theme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.light,
primaryColor: themeProps.primaryColor,
),
return MaterialApp(
debugShowCheckedModeBanner: false,
navigatorKey: globalState.navigatorKey,
localizationsDelegates: const [
AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate
],
builder: (_, child) {
return AppEnvManager(
child: _buildPlatformApp(
_buildApp(child!),
),
darkTheme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.dark,
primaryColor: themeProps.primaryColor,
).toPureBlack(themeProps.pureBlack),
),
home: child,
);
},
scrollBehavior: BaseScrollBehavior(),
title: appName,
locale: utils.getLocaleForString(locale),
supportedLocales: AppLocalizations.delegate.supportedLocales,
themeMode: themeProps.themeMode,
theme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.light,
primaryColor: themeProps.primaryColor,
),
),
darkTheme: ThemeData(
useMaterial3: true,
pageTransitionsTheme: _pageTransitionsTheme,
colorScheme: _getAppColorScheme(
brightness: Brightness.dark,
primaryColor: themeProps.primaryColor,
).toPureBlack(themeProps.pureBlack),
),
home: child,
);
},
child: const HomePage(),

View File

@@ -58,6 +58,8 @@ mixin ClashInterface {
stopLog();
Future<bool> crash();
FutureOr<String> getConnections();
FutureOr<bool> closeConnection(String id);
@@ -104,6 +106,7 @@ abstract class ClashHandlerInterface with ClashInterface {
case ActionMethod.closeConnection:
case ActionMethod.stopListener:
case ActionMethod.setState:
case ActionMethod.crash:
completer?.complete(result.data as bool);
return;
case ActionMethod.changeProxy:
@@ -242,6 +245,13 @@ abstract class ClashHandlerInterface with ClashInterface {
);
}
@override
Future<bool> crash() {
return invoke<bool>(
method: ActionMethod.crash,
);
}
@override
Future<String> getProxies() {
return invoke<String>(

View File

@@ -71,9 +71,9 @@ class ClashService extends ClashHandlerInterface {
}
}, (error, stack) {
commonPrint.log(error.toString());
if(error is SocketException){
if (error is SocketException) {
globalState.showNotifier(error.toString());
globalState.appController.restartCore();
// globalState.appController.restartCore();
}
});
}
@@ -92,12 +92,11 @@ class ClashService extends ClashHandlerInterface {
final arg = Platform.isWindows
? "${serverSocket.port}"
: serverSocket.address.address;
bool isSuccess = false;
if (Platform.isWindows && await system.checkIsAdmin()) {
isSuccess = await request.startCoreByHelper(arg);
}
if (isSuccess) {
return;
final isSuccess = await request.startCoreByHelper(arg);
if (isSuccess) {
return;
}
}
process = await Process.start(
appPath.corePath,

View File

@@ -12,7 +12,7 @@ export 'iterable.dart';
export 'keyboard.dart';
export 'launch.dart';
export 'link.dart';
export 'list.dart';
export 'fixed.dart';
export 'lock.dart';
export 'measure.dart';
export 'navigation.dart';

View File

@@ -16,16 +16,14 @@ const browserUa =
const packageName = "com.follow.clash";
final unixSocketPath = "/tmp/FlClashSocket_${Random().nextInt(10000)}.sock";
const helperPort = 47890;
const helperTag = "2024125";
const baseInfoEdgeInsets = EdgeInsets.symmetric(
vertical: 16,
horizontal: 16,
const maxTextScale = 1.4;
const minTextScale = 0.8;
final baseInfoEdgeInsets = EdgeInsets.symmetric(
vertical: 16.ap,
horizontal: 16.ap,
);
double textScaleFactor = min(
WidgetsBinding.instance.platformDispatcher.textScaleFactor,
1.2,
);
final defaultTextScaleFactor = WidgetsBinding.instance.platformDispatcher.textScaleFactor;
const httpTimeoutDuration = Duration(milliseconds: 5000);
const moreDuration = Duration(milliseconds: 100);
const animateDuration = Duration(milliseconds: 100);
@@ -44,7 +42,6 @@ const profilesDirectoryName = "profiles";
const localhost = "127.0.0.1";
const clashConfigKey = "clash_config";
const configKey = "config";
const listItemPadding = EdgeInsets.symmetric(horizontal: 16);
const double dialogCommonWidth = 300;
const repository = "chen08209/FlClash";
const defaultExternalController = "127.0.0.1:9090";
@@ -60,6 +57,7 @@ final commonFilter = ImageFilter.blur(
const navigationItemListEquality = ListEquality<NavigationItem>();
const connectionListEquality = ListEquality<Connection>();
const stringListEquality = ListEquality<String>();
const intListEquality = ListEquality<int>();
const logListEquality = ListEquality<Log>();
const groupListEquality = ListEquality<Group>();
const externalProviderListEquality = ListEquality<ExternalProvider>();
@@ -78,22 +76,24 @@ const viewModeColumnsMap = {
ViewMode.desktop: [4, 3],
};
const defaultPrimaryColor = 0xFF795548;
const defaultPrimaryColor = 0XFFD8C0C3;
double getWidgetHeight(num lines) {
return max(lines * 84 * textScaleFactor + (lines - 1) * 16, 0);
return max(lines * 84 + (lines - 1) * 16, 0).ap;
}
const maxLength = 150;
final mainIsolate = "FlClashMainIsolate";
final serviceIsolate = "FlClashServiceIsolate";
const defaultPrimaryColors = [
defaultPrimaryColor,
0xFF795548,
0xFF03A9F4,
0xFFFFFF00,
0XFFBBC9CC,
0XFFABD397,
0XFFD8C0C3,
defaultPrimaryColor,
0XFF665390,
];

View File

@@ -1,4 +1,4 @@
import 'package:fl_clash/manager/manager.dart';
import 'package:fl_clash/manager/message_manager.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
@@ -11,6 +11,36 @@ extension BuildContextExtension on BuildContext {
return findAncestorStateOfType<MessageManagerState>()?.message(text);
}
showSnackBar(
String message, {
SnackBarAction? action,
}) {
final width = viewWidth;
EdgeInsets margin;
if (width < 600) {
margin = const EdgeInsets.only(
bottom: 16,
right: 16,
left: 16,
);
} else {
margin = EdgeInsets.only(
bottom: 16,
left: 16,
right: width - 316,
);
}
ScaffoldMessenger.of(this).showSnackBar(
SnackBar(
action: action,
content: Text(message),
behavior: SnackBarBehavior.floating,
duration: const Duration(milliseconds: 1500),
margin: margin,
),
);
}
Size get appSize {
return MediaQuery.of(this).size;
}
@@ -27,10 +57,10 @@ extension BuildContextExtension on BuildContext {
T? state;
visitor(Element element) {
if(!element.mounted){
if (!element.mounted) {
return;
}
if(element is StatefulElement){
if (element is StatefulElement) {
if (element.state is T) {
state = element.state as T;
}

79
lib/common/fixed.dart Normal file
View File

@@ -0,0 +1,79 @@
import 'iterable.dart';
class FixedList<T> {
final int maxLength;
final List<T> _list;
FixedList(this.maxLength, {List<T>? list})
: _list = (list ?? [])..truncate(maxLength);
add(T item) {
_list.add(item);
_list.truncate(maxLength);
}
clear() {
_list.clear();
}
List<T> get list => List.unmodifiable(_list);
int get length => _list.length;
T operator [](int index) => _list[index];
FixedList<T> copyWith() {
return FixedList(
maxLength,
list: _list,
);
}
}
class FixedMap<K, V> {
int maxLength;
late Map<K, V> _map;
FixedMap(this.maxLength, {Map<K, V>? map}) {
_map = map ?? {};
}
updateCacheValue(K key, V Function() callback) {
final realValue = _map.updateCacheValue(
key,
callback,
);
_adjustMap();
return realValue;
}
clear() {
_map.clear();
}
updateMaxLength(int size) {
maxLength = size;
_adjustMap();
}
updateMap(Map<K, V> map) {
_map = map;
_adjustMap();
}
_adjustMap() {
if (_map.length > maxLength) {
_map = Map.fromEntries(
map.entries.toList()..truncate(maxLength),
);
}
}
V? get(K key) => _map[key];
bool containsKey(K key) => _map.containsKey(key);
int get length => _map.length;
Map<K, V> get map => Map.unmodifiable(_map);
}

View File

@@ -38,6 +38,43 @@ extension IterableExt<T> on Iterable<T> {
count++;
}
}
Iterable<T> takeLast({int count = 50}) {
if (count <= 0) return Iterable.empty();
return count >= length ? this : toList().skip(length - count);
}
}
extension ListExt<T> on List<T> {
void truncate(int maxLength) {
assert(maxLength > 0);
if (length > maxLength) {
removeRange(0, length - maxLength);
}
}
List<T> intersection(List<T> list) {
return where((item) => list.contains(item)).toList();
}
List<List<T>> batch(int maxConcurrent) {
final batches = (length / maxConcurrent).ceil();
final List<List<T>> res = [];
for (int i = 0; i < batches; i++) {
if (i != batches - 1) {
res.add(sublist(i * maxConcurrent, maxConcurrent * (i + 1)));
} else {
res.add(sublist(i * maxConcurrent, length));
}
}
return res;
}
List<T> safeSublist(int start) {
if (start <= 0) return this;
if (start > length) return [];
return sublist(start);
}
}
extension DoubleListExt on List<double> {
@@ -67,9 +104,9 @@ extension DoubleListExt on List<double> {
}
extension MapExt<K, V> on Map<K, V> {
getCacheValue(K key, V defaultValue) {
updateCacheValue(K key, V Function() callback) {
if (this[key] == null) {
this[key] = defaultValue;
this[key] = callback();
}
return this[key];
}

View File

@@ -1,93 +0,0 @@
import 'dart:collection';
class FixedList<T> {
final int maxLength;
final List<T> _list;
FixedList(this.maxLength, {List<T>? list}) : _list = list ?? [];
add(T item) {
if (_list.length == maxLength) {
_list.removeAt(0);
}
_list.add(item);
}
clear() {
_list.clear();
}
List<T> get list => List.unmodifiable(_list);
int get length => _list.length;
T operator [](int index) => _list[index];
FixedList<T> copyWith() {
return FixedList(
maxLength,
list: _list,
);
}
}
class FixedMap<K, V> {
int maxSize;
final Map<K, V> _map = {};
final Queue<K> _queue = Queue<K>();
FixedMap(this.maxSize);
put(K key, V value) {
if (_map.length == maxSize) {
final oldestKey = _queue.removeFirst();
_map.remove(oldestKey);
}
_map[key] = value;
_queue.add(key);
return value;
}
clear() {
_map.clear();
_queue.clear();
}
updateMaxSize(int size){
maxSize = size;
}
V? get(K key) => _map[key];
bool containsKey(K key) => _map.containsKey(key);
int get length => _map.length;
Map<K, V> get map => Map.unmodifiable(_map);
}
extension ListExtension<T> on List<T> {
List<T> intersection(List<T> list) {
return where((item) => list.contains(item)).toList();
}
List<List<T>> batch(int maxConcurrent) {
final batches = (length / maxConcurrent).ceil();
final List<List<T>> res = [];
for (int i = 0; i < batches; i++) {
if (i != batches - 1) {
res.add(sublist(i * maxConcurrent, maxConcurrent * (i + 1)));
} else {
res.add(sublist(i * maxConcurrent, length));
}
}
return res;
}
List<T> safeSublist(int start) {
if (start <= 0) return this;
if (start > length) return [];
return sublist(start);
}
}

View File

@@ -3,11 +3,13 @@ import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Measure {
final TextScaler _textScale;
final TextScaler _textScaler;
final BuildContext context;
final Map<String, dynamic> _measureMap;
Measure.of(this.context)
: _textScale = TextScaler.linear(
Measure.of(this.context, double textScaleFactor)
: _measureMap = {},
_textScaler = TextScaler.linear(
textScaleFactor,
);
@@ -21,7 +23,7 @@ class Measure {
style: text.style,
),
maxLines: text.maxLines,
textScaler: _textScale,
textScaler: _textScaler,
textDirection: text.textDirection ?? TextDirection.ltr,
)..layout(
maxWidth: maxWidth,
@@ -29,81 +31,87 @@ class Measure {
return textPainter.size;
}
double? _bodyMediumHeight;
Size? _bodyLargeSize;
double? _bodySmallHeight;
double? _labelSmallHeight;
double? _labelMediumHeight;
double? _titleLargeHeight;
double? _titleMediumHeight;
double get bodyMediumHeight {
_bodyMediumHeight ??= computeTextSize(
Text(
"X",
style: context.textTheme.bodyMedium,
),
).height;
return _bodyMediumHeight!;
return _measureMap.updateCacheValue(
"bodyMediumHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.bodyMedium,
),
).height,
);
}
Size get bodyLargeSize {
_bodyLargeSize ??= computeTextSize(
Text(
"X",
style: context.textTheme.bodyLarge,
),
double get bodyLargeHeight {
return _measureMap.updateCacheValue(
"bodyLargeHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.bodyLarge,
),
).height,
);
return _bodyLargeSize!;
}
double get bodySmallHeight {
_bodySmallHeight ??= computeTextSize(
Text(
"X",
style: context.textTheme.bodySmall,
),
).height;
return _bodySmallHeight!;
return _measureMap.updateCacheValue(
"bodySmallHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.bodySmall,
),
).height,
);
}
double get labelSmallHeight {
_labelSmallHeight ??= computeTextSize(
Text(
"X",
style: context.textTheme.labelSmall,
),
).height;
return _labelSmallHeight!;
return _measureMap.updateCacheValue(
"labelSmallHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.labelSmall,
),
).height,
);
}
double get labelMediumHeight {
_labelMediumHeight ??= computeTextSize(
Text(
"X",
style: context.textTheme.labelMedium,
),
).height;
return _labelMediumHeight!;
return _measureMap.updateCacheValue(
"labelMediumHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.labelMedium,
),
).height,
);
}
double get titleLargeHeight {
_titleLargeHeight ??= computeTextSize(
Text(
"X",
style: context.textTheme.titleLarge,
),
).height;
return _titleLargeHeight!;
return _measureMap.updateCacheValue(
"titleLargeHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.titleLarge,
),
).height,
);
}
double get titleMediumHeight {
_titleMediumHeight ??= computeTextSize(
Text(
"X",
style: context.textTheme.titleMedium,
),
).height;
return _titleMediumHeight!;
return _measureMap.updateCacheValue(
"titleMediumHeight",
() => computeTextSize(
Text(
"X",
style: context.textTheme.titleMedium,
),
).height,
);
}
}

View File

@@ -14,7 +14,6 @@ class Navigation {
const NavigationItem(
icon: Icon(Icons.space_dashboard),
label: PageLabel.dashboard,
keep: false,
fragment: DashboardFragment(
key: GlobalObjectKey(PageLabel.dashboard),
),

View File

@@ -1,3 +1,4 @@
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
@@ -12,6 +13,10 @@ extension NumExt on num {
}
return formatted;
}
double get ap {
return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);
}
}
extension DoubleExt on double {

View File

@@ -1,4 +1,3 @@
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/cupertino.dart';
@@ -20,10 +19,7 @@ class CommonPrint {
return;
}
globalState.appController.addLog(
Log(
logLevel: LogLevel.info,
payload: payload,
),
Log.app(payload),
);
}
}

View File

@@ -130,7 +130,7 @@ class Request {
if (response.statusCode != HttpStatus.ok) {
return false;
}
return (response.data as String) == helperTag;
return (response.data as String) == globalState.coreSHA256;
} catch (_) {
return false;
}

View File

@@ -55,18 +55,24 @@ class System {
}
Future<AuthorizeCode> authorizeCore() async {
if (Platform.isAndroid) {
return AuthorizeCode.none;
}
final corePath = appPath.corePath.replaceAll(' ', '\\\\ ');
final isAdmin = await checkIsAdmin();
if (isAdmin) {
return AuthorizeCode.none;
}
if (Platform.isWindows) {
final result = await windows?.registerService();
if (result == true) {
return AuthorizeCode.success;
}
return AuthorizeCode.error;
} else if (Platform.isMacOS) {
}
if (Platform.isMacOS) {
final shell = 'chown root:admin $corePath; chmod +sx $corePath';
final arguments = [
"-e",

View File

@@ -4,36 +4,43 @@ import 'package:flutter/material.dart';
class CommonTheme {
final BuildContext context;
final Map<String, Color> _colorMap;
final double textScaleFactor;
CommonTheme.of(this.context) : _colorMap = {};
CommonTheme.of(
this.context,
this.textScaleFactor,
) : _colorMap = {};
Color get darkenSecondaryContainer {
return _colorMap.getCacheValue(
return _colorMap.updateCacheValue(
"darkenSecondaryContainer",
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.1),
() => context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.1),
);
}
Color get darkenSecondaryContainerLighter {
return _colorMap.getCacheValue(
return _colorMap.updateCacheValue(
"darkenSecondaryContainerLighter",
context.colorScheme.secondaryContainer
() => context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.1)
.opacity60,
);
}
Color get darken2SecondaryContainer {
return _colorMap.getCacheValue(
return _colorMap.updateCacheValue(
"darken2SecondaryContainer",
context.colorScheme.secondaryContainer.blendDarken(context, factor: 0.2),
() => context.colorScheme.secondaryContainer
.blendDarken(context, factor: 0.2),
);
}
Color get darken3PrimaryContainer {
return _colorMap.getCacheValue(
return _colorMap.updateCacheValue(
"darken3PrimaryContainer",
context.colorScheme.primaryContainer.blendDarken(context, factor: 0.3),
() => context.colorScheme.primaryContainer
.blendDarken(context, factor: 0.3),
);
}
}

View File

@@ -80,7 +80,7 @@ class Tray {
);
}
menuItems.add(MenuItem.separator());
if (!Platform.isWindows) {
if (Platform.isMacOS) {
for (final group in trayState.groups) {
List<MenuItem> subMenuItems = [];
for (final proxy in group.all) {

View File

@@ -260,9 +260,7 @@ class AppController {
final patchConfig = _ref.read(patchClashConfigProvider);
final appSetting = _ref.read(appSettingProvider);
bool enableTun = patchConfig.tun.enable;
if (enableTun != lastTunEnable &&
lastTunEnable == false &&
!Platform.isAndroid) {
if (enableTun != lastTunEnable && lastTunEnable == false) {
final code = await system.authorizeCore();
switch (code) {
case AuthorizeCode.none:
@@ -314,6 +312,10 @@ class AppController {
handleChangeProfile() {
_ref.read(delayDataSourceProvider.notifier).value = {};
applyProfile();
_ref.read(logsProvider.notifier).value = FixedList(500);
_ref.read(requestsProvider.notifier).value = FixedList(500);
globalState.cacheHeightMap = {};
globalState.cacheScrollPosition = {};
}
updateBrightness(Brightness brightness) {
@@ -334,23 +336,22 @@ class AppController {
try {
await updateProfile(profile);
} catch (e) {
_ref.read(logsProvider.notifier).addLog(
Log(
logLevel: LogLevel.info,
payload: e.toString(),
),
);
commonPrint.log(e.toString());
}
}
}
Future<void> updateGroups() async {
_ref.read(groupsProvider.notifier).value = await retry(
task: () async {
return await clashCore.getProxiesGroups();
},
retryIf: (res) => res.isEmpty,
);
try {
_ref.read(groupsProvider.notifier).value = await retry(
task: () async {
return await clashCore.getProxiesGroups();
},
retryIf: (res) => res.isEmpty,
);
} catch (_) {
_ref.read(groupsProvider.notifier).value = [];
}
}
updateProfiles() async {
@@ -362,10 +363,6 @@ class AppController {
}
}
updateSystemColorSchemes(ColorSchemes colorSchemes) {
_ref.read(appSchemesProvider.notifier).value = colorSchemes;
}
savePreferences() async {
commonPrint.log("save preferences");
await preferences.saveConfig(globalState.config);
@@ -401,15 +398,23 @@ class AppController {
handleExit() async {
try {
await updateStatus(false);
await proxy?.stopProxy();
await clashCore.shutdown();
await clashService?.destroy();
await proxy?.stopProxy();
await savePreferences();
} finally {
system.exit();
}
}
Future handleClear() async {
await preferences.clearPreferences();
commonPrint.log("clear preferences");
globalState.config = Config(
themeProps: defaultThemeProps,
);
}
autoCheckUpdate() async {
if (!_ref.read(appSettingProvider).autoCheckUpdate) return;
final res = await request.checkForUpdate();
@@ -484,10 +489,10 @@ class AppController {
Future<void> _initCore() async {
final isInit = await clashCore.isInit;
if (!isInit) {
await clashCore.init();
await clashCore.setState(
globalState.getCoreState(),
);
await clashCore.init();
}
await applyProfile();
}
@@ -937,30 +942,39 @@ class AppController {
}
_recovery(Config config, RecoveryOption recoveryOption) {
final recoveryStrategy = _ref.read(appSettingProvider.select(
(state) => state.recoveryStrategy,
));
final profiles = config.profiles;
for (final profile in profiles) {
_ref.read(profilesProvider.notifier).setProfile(profile);
if (recoveryStrategy == RecoveryStrategy.override) {
_ref.read(profilesProvider.notifier).value = profiles;
} else {
for (final profile in profiles) {
_ref.read(profilesProvider.notifier).setProfile(
profile,
);
}
}
final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;
if (onlyProfiles) {
final currentProfile = _ref.read(currentProfileProvider);
if (currentProfile != null) {
_ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;
}
return;
if (!onlyProfiles) {
_ref.read(patchClashConfigProvider.notifier).value =
config.patchClashConfig;
_ref.read(appSettingProvider.notifier).value = config.appSetting;
_ref.read(currentProfileIdProvider.notifier).value =
config.currentProfileId;
_ref.read(appDAVSettingProvider.notifier).value = config.dav;
_ref.read(themeSettingProvider.notifier).value = config.themeProps;
_ref.read(windowSettingProvider.notifier).value = config.windowProps;
_ref.read(vpnSettingProvider.notifier).value = config.vpnProps;
_ref.read(proxiesStyleSettingProvider.notifier).value =
config.proxiesStyle;
_ref.read(overrideDnsProvider.notifier).value = config.overrideDns;
_ref.read(networkSettingProvider.notifier).value = config.networkProps;
_ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;
}
final currentProfile = _ref.read(currentProfileProvider);
if (currentProfile == null) {
_ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;
}
_ref.read(patchClashConfigProvider.notifier).value =
config.patchClashConfig;
_ref.read(appSettingProvider.notifier).value = config.appSetting;
_ref.read(currentProfileIdProvider.notifier).value =
config.currentProfileId;
_ref.read(appDAVSettingProvider.notifier).value = config.dav;
_ref.read(themeSettingProvider.notifier).value = config.themeProps;
_ref.read(windowSettingProvider.notifier).value = config.windowProps;
_ref.read(vpnSettingProvider.notifier).value = config.vpnProps;
_ref.read(proxiesStyleSettingProvider.notifier).value = config.proxiesStyle;
_ref.read(overrideDnsProvider.notifier).value = config.overrideDns;
_ref.read(networkSettingProvider.notifier).value = config.networkProps;
_ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;
}
}

View File

@@ -91,7 +91,14 @@ enum Mode { rule, global, direct }
enum ViewMode { mobile, laptop, desktop }
enum LogLevel { debug, info, warning, error, silent }
enum LogLevel {
debug,
info,
warning,
error,
silent,
app,
}
enum TransportProtocol { udp, tcp }
@@ -262,6 +269,7 @@ enum ActionMethod {
getCountryCode,
getMemory,
getProfile,
crash,
///Android,
setFdMap,
@@ -285,6 +293,7 @@ enum WindowsHelperServiceStatus {
enum DebounceTag {
updateClashConfig,
updateStatus,
updateGroups,
addCheckIpNum,
applyProfile,
@@ -308,6 +317,12 @@ enum DashboardWidget {
child: NetworkSpeed(),
),
),
outboundModeV2(
GridItem(
crossAxisCellCount: 8,
child: OutboundModeV2(),
),
),
outboundMode(
GridItem(
crossAxisCellCount: 4,
@@ -333,6 +348,15 @@ enum DashboardWidget {
),
platforms: desktopPlatforms,
),
vpnButton(
GridItem(
crossAxisCellCount: 4,
child: VpnButton(),
),
platforms: [
SupportPlatform.Android,
],
),
systemProxyButton(
GridItem(
crossAxisCellCount: 4,
@@ -447,3 +471,14 @@ enum RuleTarget {
DIRECT,
REJECT,
}
enum RecoveryStrategy {
compatible,
override,
}
enum CacheTag {
logs,
rules,
requests,
}

View File

@@ -1,7 +1,11 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
@immutable
class Contributor {
@@ -43,6 +47,15 @@ class AboutFragment extends StatelessWidget {
_checkUpdate(context);
},
),
ListItem(
title: Text(appLocalizations.contactMe),
onTap: () {
globalState.showMessage(
title: appLocalizations.contactMe,
message: TextSpan(text: "chen08209@gmail.com"),
);
},
),
ListItem(
title: const Text("Telegram"),
onTap: () {
@@ -116,33 +129,43 @@ class AboutFragment extends StatelessWidget {
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Padding(
padding: const EdgeInsets.all(12),
child: Image.asset(
'assets/images/icon.png',
width: 64,
height: 64,
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.start,
Consumer(builder: (_, ref, ___) {
return _DeveloperModeDetector(
child: Wrap(
spacing: 16,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
Text(
appName,
style: Theme.of(context).textTheme.headlineSmall,
Padding(
padding: const EdgeInsets.all(12),
child: Image.asset(
'assets/images/icon.png',
width: 64,
height: 64,
),
),
Text(
globalState.packageInfo.version,
style: Theme.of(context).textTheme.labelLarge,
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
appName,
style: Theme.of(context).textTheme.headlineSmall,
),
Text(
globalState.packageInfo.version,
style: Theme.of(context).textTheme.labelLarge,
)
],
)
],
)
],
),
),
onEnterDeveloperMode: () {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(developerMode: true),
);
context.showNotifier(appLocalizations.developerModeEnableTip);
},
);
}),
const SizedBox(
height: 24,
),
@@ -209,3 +232,52 @@ class Avatar extends StatelessWidget {
);
}
}
class _DeveloperModeDetector extends StatefulWidget {
final Widget child;
final VoidCallback onEnterDeveloperMode;
const _DeveloperModeDetector({
required this.child,
required this.onEnterDeveloperMode,
});
@override
State<_DeveloperModeDetector> createState() => _DeveloperModeDetectorState();
}
class _DeveloperModeDetectorState extends State<_DeveloperModeDetector> {
int _counter = 0;
Timer? _timer;
void _handleTap() {
_counter++;
if (_counter >= 5) {
widget.onEnterDeveloperMode();
_resetCounter();
} else {
_timer?.cancel();
_timer = Timer(Duration(seconds: 1), _resetCounter);
}
}
void _resetCounter() {
_counter = 0;
_timer?.cancel();
_timer = null;
}
@override
void dispose() {
_timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: _handleTap,
child: widget.child,
);
}
}

View File

@@ -113,15 +113,11 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
}
_intelligentSelected() async {
final appState = globalState.appState;
final config = globalState.config;
final accessControl = config.vpnProps.accessControl;
final packageNames = appState.packages
.where(
(item) =>
accessControl.isFilterSystemApp ? item.isSystem == false : true,
)
.map((item) => item.packageName);
final packageNames = ref.read(
packageListSelectorStateProvider.select(
(state) => state.list.map((item) => item.packageName),
),
);
final commonScaffoldState = context.commonScaffoldState;
if (commonScaffoldState?.mounted != true) return;
final selectedPackageNames =
@@ -194,7 +190,7 @@ class _AccessFragmentState extends ConsumerState<AccessFragment> {
final state = ref.watch(packageListSelectorStateProvider);
final accessControl = state.accessControl;
final accessControlMode = accessControl.mode;
final packages = state.getList(
final packages = state.getSortList(
accessControlMode == AccessControlMode.acceptSelected
? acceptList
: rejectList,
@@ -482,14 +478,20 @@ class AccessControlSearchDelegate extends SearchDelegate {
final lowQuery = query.toLowerCase();
return Consumer(
builder: (context, ref, __) {
final state = ref.watch(packageListSelectorStateProvider);
final accessControl = state.accessControl;
final accessControlMode = accessControl.mode;
final packages = state.getList(
accessControlMode == AccessControlMode.acceptSelected
? acceptList
: rejectList,
final vm3 = ref.watch(
packageListSelectorStateProvider.select(
(state) => VM3(
a: state.getSortList(
state.accessControl.mode == AccessControlMode.acceptSelected
? acceptList
: rejectList,
),
b: state.accessControl.enable,
c: state.accessControl.currentList,
),
),
);
final packages = vm3.a;
final queryPackages = packages
.where(
(package) =>
@@ -497,8 +499,8 @@ class AccessControlSearchDelegate extends SearchDelegate {
package.packageName.contains(lowQuery),
)
.toList();
final isAccessControl = state.accessControl.enable;
final currentList = accessControl.currentList;
final isAccessControl = vm3.b;
final currentList = vm3.c;
final packageNameList = packages.map((e) => e.packageName).toList();
final valueList = currentList.intersection(packageNameList);
return DisabledMask(
@@ -579,13 +581,6 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
};
}
String _getTextWithIsFilterSystemApp(bool isFilterSystemApp) {
return switch (isFilterSystemApp) {
true => appLocalizations.onlyOtherApps,
false => appLocalizations.allApps,
};
}
List<Widget> _buildModeSetting() {
return generateSection(
title: appLocalizations.mode,
@@ -673,25 +668,39 @@ class _AccessControlPanelState extends ConsumerState<AccessControlPanel> {
scrollDirection: Axis.horizontal,
child: Consumer(
builder: (_, ref, __) {
final isFilterSystemApp = ref.watch(
vpnSettingProvider
.select((state) => state.accessControl.isFilterSystemApp),
final vm2 = ref.watch(
vpnSettingProvider.select(
(state) => VM2(
a: state.accessControl.isFilterSystemApp,
b: state.accessControl.isFilterNonInternetApp,
),
),
);
return Wrap(
spacing: 16,
children: [
for (final item in [false, true])
SettingTextCard(
_getTextWithIsFilterSystemApp(item),
isSelected: isFilterSystemApp == item,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith.accessControl(
isFilterSystemApp: item,
),
);
},
)
SettingTextCard(
appLocalizations.systemApp,
isSelected: vm2.a == false,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith.accessControl(
isFilterSystemApp: !vm2.a,
),
);
},
),
SettingTextCard(
appLocalizations.noNetworkApp,
isSelected: vm2.b == false,
onPressed: () {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith.accessControl(
isFilterNonInternetApp: !vm2.b,
),
);
},
)
],
);
},

View File

@@ -8,10 +8,12 @@ import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/fade_box.dart';
import 'package:fl_clash/widgets/input.dart';
import 'package:fl_clash/widgets/list.dart';
import 'package:fl_clash/widgets/text.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
class BackupAndRecovery extends ConsumerWidget {
const BackupAndRecovery({super.key});
@@ -134,6 +136,30 @@ class BackupAndRecovery extends ConsumerWidget {
);
}
_handleUpdateRecoveryStrategy(WidgetRef ref) async {
final recoveryStrategy = ref.read(appSettingProvider.select(
(state) => state.recoveryStrategy,
));
final res = await globalState.showCommonDialog(
child: OptionsDialog<RecoveryStrategy>(
title: appLocalizations.recoveryStrategy,
options: RecoveryStrategy.values,
textBuilder: (mode) => Intl.message(
"recoveryStrategy_${mode.name}",
),
value: recoveryStrategy,
),
);
if (res == null) {
return;
}
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
recoveryStrategy: res,
),
);
}
@override
Widget build(BuildContext context, ref) {
final dav = ref.watch(appDAVSettingProvider);
@@ -256,6 +282,26 @@ class BackupAndRecovery extends ConsumerWidget {
title: Text(appLocalizations.recovery),
subtitle: Text(appLocalizations.localRecoveryDesc),
),
ListHeader(title: appLocalizations.options),
Consumer(builder: (_, ref, __) {
final recoveryStrategy = ref.watch(appSettingProvider.select(
(state) => state.recoveryStrategy,
));
return ListItem(
onTap: () {
_handleUpdateRecoveryStrategy(ref);
},
title: Text(appLocalizations.recoveryStrategy),
trailing: FilledButton(
onPressed: () {
_handleUpdateRecoveryStrategy(ref);
},
child: Text(
Intl.message("recoveryStrategy_${recoveryStrategy.name}"),
),
),
);
}),
],
);
}

View File

@@ -301,8 +301,11 @@ class RouteAddressItem extends ConsumerWidget {
title: appLocalizations.routeAddress,
widget: Consumer(
builder: (_, ref, __) {
final routeAddress = ref.watch(patchClashConfigProvider
.select((state) => state.tun.routeAddress));
final routeAddress = ref.watch(
patchClashConfigProvider.select(
(state) => state.tun.routeAddress,
),
);
return ListInputPage(
title: appLocalizations.routeAddress,
items: routeAddress,
@@ -371,7 +374,9 @@ class NetworkListView extends ConsumerWidget {
return;
}
ref.read(vpnSettingProvider.notifier).updateState(
(state) => defaultVpnProps,
(state) => defaultVpnProps.copyWith(
accessControl: state.accessControl,
),
);
ref.read(patchClashConfigProvider.notifier).updateState(
(state) => state.copyWith(

View File

@@ -20,12 +20,13 @@ class RequestsFragment extends ConsumerStatefulWidget {
class _RequestsFragmentState extends ConsumerState<RequestsFragment>
with PageMixin {
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
final _requestsStateNotifier =
ValueNotifier<ConnectionsState>(const ConnectionsState());
final _requestsStateNotifier = ValueNotifier<ConnectionsState>(
const ConnectionsState(loading: true),
);
List<Connection> _requests = [];
final _cacheKey = ValueKey("requests_list");
final _tag = CacheTag.requests;
late ScrollController _scrollController;
bool _isLoad = false;
double _currentMaxWidth = 0;
@@ -45,12 +46,13 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
@override
void initState() {
super.initState();
final preOffset = globalState.cacheScrollPosition[_cacheKey] ?? -1;
final preOffset = globalState.cacheScrollPosition[_tag] ?? -1;
_scrollController = ScrollController(
initialScrollOffset: preOffset > 0 ? preOffset : double.maxFinite,
);
_requests = globalState.appState.requests.list;
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
connections: globalState.appState.requests.list,
connections: _requests,
);
ref.listenManual(
isCurrentPageProvider(
@@ -73,7 +75,6 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
updateRequestsThrottler();
}
},
fireImmediately: true,
);
}
@@ -98,14 +99,7 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
final lines = (chainSize.height / baseHeight).round();
final computerHeight =
size.height + chainSize.height + 24 + 24 * (lines - 1);
return computerHeight;
}
_handleTryClearCache(double maxWidth) {
if (_currentMaxWidth != maxWidth) {
_currentMaxWidth = maxWidth;
_key.currentState?.clearCache();
}
return computerHeight + 8 + 32 + globalState.measure.bodyMediumHeight;
}
@override
@@ -133,6 +127,42 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
}, duration: commonDuration);
}
_preLoad() {
if (_isLoad == true) {
return;
}
_isLoad = true;
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!mounted) {
return;
}
final isMobileView = ref.read(isMobileViewProvider);
if (isMobileView) {
await Future.delayed(Duration(milliseconds: 300));
}
final parts = _requests.batch(10);
globalState.cacheHeightMap[_tag] ??= FixedMap(
_requests.length,
);
for (int i = 0; i < parts.length; i++) {
final part = parts[i];
await Future(
() {
for (final request in part) {
globalState.cacheHeightMap[_tag]?.updateCacheValue(
request.id,
() => _calcCacheHeight(request),
);
}
},
);
}
_requestsStateNotifier.value = _requestsStateNotifier.value.copyWith(
loading: false,
);
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
@@ -146,75 +176,86 @@ class _RequestsFragmentState extends ConsumerState<RequestsFragment>
Platform.isAndroid,
),
);
_handleTryClearCache(constraints.maxWidth - 40 - (value ? 60 : 0));
_currentMaxWidth = constraints.maxWidth - 40 - (value ? 60 : 0);
return child!;
},
child: ValueListenableBuilder<ConnectionsState>(
valueListenable: _requestsStateNotifier,
builder: (_, state, __) {
final connections = state.list;
if (connections.isEmpty) {
return NullStatus(
label: appLocalizations.nullRequestsDesc,
child: TextScaleNotification(
child: ValueListenableBuilder<ConnectionsState>(
valueListenable: _requestsStateNotifier,
builder: (_, state, __) {
_preLoad();
final connections = state.list;
if (connections.isEmpty) {
return NullStatus(
label: appLocalizations.nullRequestsDesc,
);
}
final items = connections
.map<Widget>(
(connection) => ConnectionItem(
key: Key(connection.id),
connection: connection,
onClickKeyword: (value) {
context.commonScaffoldState?.addKeyword(value);
},
),
)
.separated(
const Divider(
height: 0,
),
)
.toList();
final content = connections.isEmpty
? NullStatus(
label: appLocalizations.nullRequestsDesc,
)
: Align(
alignment: Alignment.topCenter,
child: ScrollToEndBox(
controller: _scrollController,
tag: _tag,
dataSource: connections,
child: CommonScrollBar(
controller: _scrollController,
child: CacheItemExtentListView(
tag: _tag,
reverse: true,
shrinkWrap: true,
physics: NextClampingScrollPhysics(),
controller: _scrollController,
itemExtentBuilder: (index) {
if (index.isOdd) {
return 0;
}
return _calcCacheHeight(
connections[index ~/ 2]);
},
itemBuilder: (_, index) {
return items[index];
},
itemCount: items.length,
keyBuilder: (int index) {
if (index.isOdd) {
return "divider";
}
return connections[index ~/ 2].id;
},
),
),
),
);
return FadeBox(
child: state.loading
? Center(
child: CircularProgressIndicator(),
)
: content,
);
}
final items = connections
.map<Widget>(
(connection) => ConnectionItem(
key: Key(connection.id),
connection: connection,
onClickKeyword: (value) {
context.commonScaffoldState?.addKeyword(value);
},
),
)
.separated(
const Divider(
height: 0,
),
)
.toList();
return Align(
alignment: Alignment.topCenter,
child: ScrollToEndBox(
controller: _scrollController,
cacheKey: _cacheKey,
dataSource: connections,
child: CommonScrollBar(
controller: _scrollController,
child: CacheItemExtentListView(
key: _key,
reverse: true,
shrinkWrap: true,
physics: NextClampingScrollPhysics(),
controller: _scrollController,
itemExtentBuilder: (index) {
final widget = items[index];
if (widget.runtimeType == Divider) {
return 0;
}
final measure = globalState.measure;
final bodyMediumHeight = measure.bodyMediumHeight;
final connection = connections[(index / 2).floor()];
final height = _calcCacheHeight(connection);
return height + bodyMediumHeight + 32;
},
itemBuilder: (_, index) {
return items[index];
},
itemCount: items.length,
keyBuilder: (int index) {
final widget = items[index];
if (widget.runtimeType == Divider) {
return "divider";
}
final connection = connections[(index / 2).floor()];
return connection.id;
},
),
),
),
);
},
),
onNotification: (_) {
globalState.cacheHeightMap[_tag]?.clear();
},
),
);

View File

@@ -93,7 +93,7 @@ class _DashboardFragmentState extends ConsumerState<DashboardFragment>
@override
Widget build(BuildContext context) {
final dashboardState = ref.watch(dashboardStateProvider);
final columns = max(4 * ((dashboardState.viewWidth / 350).ceil()), 8);
final columns = max(4 * ((dashboardState.viewWidth / 320).ceil()), 8);
return Align(
alignment: Alignment.topCenter,
child: SingleChildScrollView(
@@ -103,8 +103,8 @@ class _DashboardFragmentState extends ConsumerState<DashboardFragment>
child: SuperGrid(
key: key,
crossAxisCount: columns,
crossAxisSpacing: 16,
mainAxisSpacing: 16,
crossAxisSpacing: 16.ap,
mainAxisSpacing: 16.ap,
children: [
...dashboardState.dashboardWidgets
.where(

View File

@@ -4,6 +4,7 @@ import 'dart:io';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/common.dart';
import 'package:fl_clash/state.dart';
import 'package:fl_clash/widgets/widgets.dart';
import 'package:flutter/material.dart';
@@ -57,37 +58,43 @@ class _MemoryInfoState extends State<MemoryInfo> {
onPressed: () {
clashCore.requestGc();
},
child: Column(
children: [
ValueListenableBuilder(
valueListenable: _memoryInfoStateNotifier,
builder: (_, trafficValue, __) {
return Padding(
padding: baseInfoEdgeInsets.copyWith(
bottom: 0,
top: 12,
),
child: Row(
children: [
Text(
trafficValue.showValue,
style:
context.textTheme.bodyMedium?.toLight.adjustSize(1),
),
SizedBox(
width: 8,
),
Text(
trafficValue.showUnit,
style:
context.textTheme.bodyMedium?.toLight.adjustSize(1),
)
],
),
);
},
),
],
child: Container(
padding: baseInfoEdgeInsets.copyWith(
top: 0,
),
child: Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.end,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: globalState.measure.bodyMediumHeight + 2,
child: ValueListenableBuilder(
valueListenable: _memoryInfoStateNotifier,
builder: (_, trafficValue, __) {
return Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Text(
trafficValue.showValue,
style: context.textTheme.bodyMedium?.toLight
.adjustSize(1),
),
SizedBox(
width: 8,
),
Text(
trafficValue.showUnit,
style: context.textTheme.bodyMedium?.toLight
.adjustSize(1),
)
],
);
},
),
)
],
),
),
),
);

View File

@@ -206,7 +206,7 @@ class _NetworkDetectionState extends ConsumerState<NetworkDetection> {
);
},
icon: Icon(
size: 16,
size: 16.ap,
Icons.info_outline,
color: context.colorScheme.onSurfaceVariant,
),

View File

@@ -17,58 +17,146 @@ class OutboundMode extends StatelessWidget {
height: height,
child: Consumer(
builder: (_, ref, __) {
final mode =
ref.watch(patchClashConfigProvider.select((state) => state.mode));
return CommonCard(
onPressed: () {},
info: Info(
label: appLocalizations.outboundMode,
iconData: Icons.call_split_sharp,
),
child: Padding(
padding: const EdgeInsets.only(
top: 12,
bottom: 16,
),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.start,
children: [
for (final item in Mode.values)
Flexible(
child: ListItem.radio(
dense: true,
horizontalTitleGap: 4,
padding: const EdgeInsets.only(
left: 12,
right: 16,
),
delegate: RadioDelegate(
value: item,
groupValue: mode,
onChanged: (value) async {
if (value == null) {
return;
}
globalState.appController.changeMode(value);
},
),
title: Text(
Intl.message(item.name),
style: Theme.of(context)
.textTheme
.bodyMedium
?.toSoftBold,
),
),
),
],
),
final mode = ref.watch(
patchClashConfigProvider.select(
(state) => state.mode,
),
);
return Theme(
data: Theme.of(context).copyWith(
splashColor: Colors.transparent,
highlightColor: Colors.transparent,
hoverColor: Colors.transparent),
child: CommonCard(
onPressed: () {},
info: Info(
label: appLocalizations.outboundMode,
iconData: Icons.call_split_sharp,
),
child: Padding(
padding: const EdgeInsets.only(
top: 12,
bottom: 16,
),
child: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
for (final item in Mode.values)
Flexible(
fit: FlexFit.tight,
child: ListItem.radio(
dense: true,
horizontalTitleGap: 4,
padding: EdgeInsets.only(
left: 12.ap,
right: 16.ap,
),
delegate: RadioDelegate(
value: item,
groupValue: mode,
onChanged: (value) async {
if (value == null) {
return;
}
globalState.appController.changeMode(value);
},
),
title: Text(
Intl.message(item.name),
style: Theme.of(context)
.textTheme
.bodyMedium
?.toSoftBold,
),
),
),
],
),
),
));
},
),
);
}
}
class OutboundModeV2 extends StatelessWidget {
const OutboundModeV2({super.key});
Color _getTextColor(BuildContext context, Mode mode) {
return switch (mode) {
Mode.rule => context.colorScheme.onSecondaryContainer,
Mode.global => context.colorScheme.onPrimaryContainer,
Mode.direct => context.colorScheme.onTertiaryContainer,
};
}
@override
Widget build(BuildContext context) {
final height = getWidgetHeight(0.72);
return SizedBox(
height: height,
child: CommonCard(
padding: EdgeInsets.zero,
child: Consumer(
builder: (_, ref, __) {
final mode = ref.watch(
patchClashConfigProvider.select(
(state) => state.mode,
),
);
final thumbColor = switch (mode) {
Mode.rule => context.colorScheme.secondaryContainer,
Mode.global => globalState.theme.darken3PrimaryContainer,
Mode.direct => context.colorScheme.tertiaryContainer,
};
return Container(
constraints: BoxConstraints.expand(),
child: CommonTabBar<Mode>(
children: Map.fromEntries(
Mode.values.map(
(item) => MapEntry(
item,
Container(
clipBehavior: Clip.antiAlias,
alignment: Alignment.center,
decoration: BoxDecoration(),
height: height - 16,
child: Text(
Intl.message(item.name),
style: Theme.of(context)
.textTheme
.titleSmall
?.adjustSize(1)
.copyWith(
color: item == mode
? _getTextColor(
context,
item,
)
: null,
),
),
),
),
),
),
padding: EdgeInsets.symmetric(horizontal: 8),
groupValue: mode,
onValueChanged: (value) {
if (value == null) {
return;
}
globalState.appController.changeMode(value);
},
thumbColor: thumbColor,
),
);
},
),
),
);
}
}

View File

@@ -165,3 +165,87 @@ class SystemProxyButton extends StatelessWidget {
);
}
}
class VpnButton extends StatelessWidget {
const VpnButton({super.key});
@override
Widget build(BuildContext context) {
return SizedBox(
height: getWidgetHeight(1),
child: CommonCard(
onPressed: () {
showSheet(
context: context,
builder: (_, type) {
return AdaptiveSheetScaffold(
type: type,
body: generateListView(
generateSection(
items: [
const VPNItem(),
const VpnSystemProxyItem(),
const TunStackItem(),
],
),
),
title: "VPN",
);
},
);
},
info: Info(
label: "VPN",
iconData: Icons.stacked_line_chart,
),
child: Container(
padding: baseInfoEdgeInsets.copyWith(
top: 4,
bottom: 8,
right: 8,
),
child: Row(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(
flex: 1,
child: TooltipText(
text: Text(
appLocalizations.options,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.titleSmall
?.adjustSize(-2)
.toLight,
),
),
),
Consumer(
builder: (_, ref, __) {
final enable = ref.watch(
vpnSettingProvider.select(
(state) => state.enable,
),
);
return Switch(
value: enable,
onChanged: (value) {
ref.read(vpnSettingProvider.notifier).updateState(
(state) => state.copyWith(
enable: value,
),
);
},
);
},
)
],
),
),
),
);
}
}

View File

@@ -1,5 +1,5 @@
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
@@ -35,11 +35,15 @@ class _StartButtonState extends State<StartButton>
}
handleSwitchStart() {
if (isStart == globalState.appState.isStart) {
isStart = !isStart;
updateController();
globalState.appController.updateStatus(isStart);
}
isStart = !isStart;
updateController();
debouncer.call(
DebounceTag.updateStatus,
() {
globalState.appController.updateStatus(isStart);
},
duration: moreDuration,
);
}
updateController() {
@@ -126,9 +130,11 @@ class _StartButtonState extends State<StartButton>
final text = utils.getTimeText(runTime);
return Text(
text,
style: Theme.of(context).textTheme.titleMedium?.toSoftBold.copyWith(
color: context.colorScheme.onPrimaryContainer
),
style: Theme.of(context)
.textTheme
.titleMedium
?.toSoftBold
.copyWith(color: context.colorScheme.onPrimaryContainer),
);
},
),

View File

@@ -0,0 +1,120 @@
import 'package:fl_clash/clash/core.dart';
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/config.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';
import '../providers/app.dart';
class DeveloperView extends ConsumerWidget {
const DeveloperView({super.key});
Widget _getDeveloperList(BuildContext context, WidgetRef ref) {
return generateSectionV2(
title: appLocalizations.options,
items: [
ListItem(
title: Text(appLocalizations.messageTest),
onTap: () {
context.showNotifier(
appLocalizations.messageTestTip,
);
},
),
ListItem(
title: Text(appLocalizations.logsTest),
onTap: () {
for (int i = 0; i < 1000; i++) {
ref.read(requestsProvider.notifier).addRequest(Connection(
id: utils.id,
start: DateTime.now(),
metadata: Metadata(
uid: i * i,
network: utils.generateRandomString(
maxLength: 1000,
minLength: 20,
),
sourceIP: '',
sourcePort: '',
destinationIP: '',
destinationPort: '',
host: '',
process: '',
remoteDestination: "",
),
chains: ["chains"],
));
globalState.appController.addLog(
Log.app(
utils.generateRandomString(
maxLength: 200,
minLength: 20,
),
),
);
}
},
),
ListItem(
title: Text(appLocalizations.crashTest),
onTap: () {
clashCore.clashInterface.crash();
},
),
ListItem(
title: Text(appLocalizations.clearData),
onTap: () async {
await globalState.appController.handleClear();
},
)
],
);
}
@override
Widget build(BuildContext context, ref) {
final enable = ref.watch(
appSettingProvider.select(
(state) => state.developerMode,
),
);
return SingleChildScrollView(
padding: baseInfoEdgeInsets,
child: Column(
children: [
CommonCard(
type: CommonCardType.filled,
radius: 18,
child: ListItem.switchItem(
padding: const EdgeInsets.only(
left: 16,
right: 16,
top: 4,
bottom: 4,
),
title: Text(appLocalizations.developerMode),
delegate: SwitchDelegate(
value: enable,
onChanged: (value) {
ref.read(appSettingProvider.notifier).updateState(
(state) => state.copyWith(
developerMode: value,
),
);
},
),
),
),
SizedBox(
height: 16,
),
_getDeveloperList(context, ref)
],
),
);
}
}

View File

@@ -11,3 +11,4 @@ export 'backup_and_recovery.dart';
export 'resources.dart';
export 'connection/requests.dart';
export 'connection/connections.dart';
export 'developer.dart';

View File

@@ -1,3 +1,5 @@
import 'dart:math';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/providers/providers.dart';
@@ -16,36 +18,27 @@ class LogsFragment extends ConsumerStatefulWidget {
}
class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
final _logsStateNotifier = ValueNotifier<LogsState>(LogsState());
final _cacheKey = ValueKey("logs_list");
final _logsStateNotifier = ValueNotifier<LogsState>(
LogsState(loading: true),
);
late ScrollController _scrollController;
double _currentMaxWidth = 0;
final GlobalKey<CacheItemExtentListViewState> _key = GlobalKey();
final _tag = CacheTag.rules;
bool _isLoad = false;
List<Log> _logs = [];
@override
void initState() {
super.initState();
final preOffset = globalState.cacheScrollPosition[_cacheKey] ?? -1;
final position = globalState.cacheScrollPosition[_tag] ?? -1;
_scrollController = ScrollController(
initialScrollOffset: preOffset > 0 ? preOffset : double.maxFinite,
initialScrollOffset: position > 0 ? position : double.maxFinite,
);
_logs = globalState.appState.logs.list;
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
logs: globalState.appState.logs.list,
);
ref.listenManual(
logsProvider.select((state) => state.list),
(prev, next) {
if (prev != next) {
final isEquality = logListEquality.equals(prev, next);
if (!isEquality) {
_logs = next;
updateLogsThrottler();
}
}
},
fireImmediately: true,
logs: _logs,
);
ref.listenManual(
isCurrentPageProvider(
@@ -60,6 +53,18 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
},
fireImmediately: true,
);
ref.listenManual(
logsProvider.select((state) => state.list),
(prev, next) {
if (prev != next) {
final isEquality = logListEquality.equals(prev, next);
if (!isEquality) {
_logs = next;
updateLogsThrottler();
}
}
},
);
}
@override
@@ -94,13 +99,6 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
super.dispose();
}
_handleTryClearCache(double maxWidth) {
if (_currentMaxWidth != maxWidth) {
_currentMaxWidth = maxWidth;
_key.currentState?.clearCache();
}
}
_handleExport() async {
final commonScaffoldState = context.commonScaffoldState;
final res = await commonScaffoldState?.loadingRun<bool>(
@@ -123,13 +121,13 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
final height = globalState.measure
.computeTextSize(
Text(
log.payload ?? "",
style: globalState.appController.context.textTheme.bodyLarge,
log.payload,
style: context.textTheme.bodyLarge,
),
maxWidth: _currentMaxWidth,
)
.height;
return height + bodySmallHeight + 8 + bodyMediumHeight + 40;
return height + bodySmallHeight + 8 + bodyMediumHeight + 40 + 8;
}
updateLogsThrottler() {
@@ -142,82 +140,123 @@ class _LogsFragmentState extends ConsumerState<LogsFragment> with PageMixin {
return;
}
WidgetsBinding.instance.addPostFrameCallback((_) {
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
logs: _logs,
);
if (mounted) {
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
logs: _logs,
);
}
});
}, duration: commonDuration);
}
_preLoad() {
if (_isLoad == true) {
return;
}
_isLoad = true;
WidgetsBinding.instance.addPostFrameCallback((_) async {
if (!mounted) {
return;
}
final isMobileView = ref.read(isMobileViewProvider);
if (isMobileView) {
await Future.delayed(Duration(milliseconds: 300));
}
final parts = _logs.batch(10);
globalState.cacheHeightMap[_tag] ??= FixedMap(
_logs.length,
);
for (int i = 0; i < parts.length; i++) {
final part = parts[i];
await Future(
() {
for (final log in part) {
globalState.cacheHeightMap[_tag]?.updateCacheValue(
log.payload,
() => _getItemHeight(log),
);
}
},
);
}
_logsStateNotifier.value = _logsStateNotifier.value.copyWith(
loading: false,
);
});
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (_, constraints) {
_handleTryClearCache(constraints.maxWidth - 40);
return Align(
alignment: Alignment.topCenter,
child: ValueListenableBuilder<LogsState>(
valueListenable: _logsStateNotifier,
builder: (_, state, __) {
final logs = state.list;
if (logs.isEmpty) {
return NullStatus(
label: appLocalizations.nullLogsDesc,
);
}
final items = logs
.map<Widget>(
(log) => LogItem(
key: Key(log.dateTime.toString()),
log: log,
onClick: (value) {
context.commonScaffoldState?.addKeyword(value);
},
),
)
.separated(
const Divider(
height: 0,
),
)
.toList();
return ScrollToEndBox<Log>(
controller: _scrollController,
cacheKey: _cacheKey,
dataSource: logs,
child: CommonScrollBar(
controller: _scrollController,
child: CacheItemExtentListView(
key: _key,
reverse: true,
shrinkWrap: true,
physics: NextClampingScrollPhysics(),
controller: _scrollController,
itemBuilder: (_, index) {
return items[index];
},
itemExtentBuilder: (index) {
final item = items[index];
if (item.runtimeType == Divider) {
return 0;
}
final log = logs[(index / 2).floor()];
return _getItemHeight(log);
},
itemCount: items.length,
keyBuilder: (int index) {
final item = items[index];
if (item.runtimeType == Divider) {
return "divider";
}
final log = logs[(index / 2).floor()];
return log.payload ?? "";
_currentMaxWidth = constraints.maxWidth - 40;
return ValueListenableBuilder<LogsState>(
valueListenable: _logsStateNotifier,
builder: (_, state, __) {
_preLoad();
final logs = state.list;
final items = logs
.map<Widget>(
(log) => LogItem(
key: Key(log.dateTime),
log: log,
onClick: (value) {
context.commonScaffoldState?.addKeyword(value);
},
),
),
);
},
),
)
.separated(
const Divider(
height: 0,
),
)
.toList();
final content = logs.isEmpty
? NullStatus(
label: appLocalizations.nullLogsDesc,
)
: Align(
alignment: Alignment.topCenter,
child: CommonScrollBar(
controller: _scrollController,
child: ScrollToEndBox(
controller: _scrollController,
tag: _tag,
dataSource: logs,
child: CacheItemExtentListView(
tag: _tag,
reverse: true,
shrinkWrap: true,
physics: NextClampingScrollPhysics(),
controller: _scrollController,
itemBuilder: (_, index) {
return items[index];
},
itemExtentBuilder: (index) {
if (index.isOdd) {
return 0;
}
return _getItemHeight(logs[index ~/ 2]);
},
itemCount: items.length,
keyBuilder: (int index) {
if (index.isOdd) {
return "divider";
}
return logs[index ~/ 2].payload;
},
),
),
),
);
return FadeBox(
child: state.loading
? Center(
child: CircularProgressIndicator(),
)
: content,
);
},
);
},
);
@@ -242,14 +281,14 @@ class LogItem extends StatelessWidget {
vertical: 4,
),
title: SelectableText(
log.payload ?? '',
log.payload,
style: context.textTheme.bodyLarge,
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
"${log.dateTime}",
log.dateTime,
style: context.textTheme.bodySmall?.copyWith(
color: context.colorScheme.primary,
),

View File

@@ -104,8 +104,13 @@ class _URLFormDialogState extends State<URLFormDialog> {
runSpacing: 16,
children: [
TextField(
maxLines: 5,
keyboardType: TextInputType.url,
minLines: 1,
maxLines: 5,
onSubmitted: (_) {
_handleAddProfileFormURL();
},
onEditingComplete: _handleAddProfileFormURL,
controller: urlController,
decoration: InputDecoration(
border: const OutlineInputBorder(),

View File

@@ -214,6 +214,7 @@ class _EditProfileState extends State<EditProfile> {
final items = [
ListItem(
title: TextFormField(
textInputAction: TextInputAction.next,
controller: labelController,
decoration: InputDecoration(
border: const OutlineInputBorder(),
@@ -230,6 +231,8 @@ class _EditProfileState extends State<EditProfile> {
if (widget.profile.type == ProfileType.url) ...[
ListItem(
title: TextFormField(
textInputAction: TextInputAction.next,
keyboardType: TextInputType.url,
controller: urlController,
maxLines: 5,
minLines: 1,
@@ -258,6 +261,7 @@ class _EditProfileState extends State<EditProfile> {
if (autoUpdate)
ListItem(
title: TextFormField(
textInputAction: TextInputAction.next,
controller: autoUpdateDurationController,
decoration: InputDecoration(
border: const OutlineInputBorder(),

View File

@@ -23,7 +23,6 @@ class OverrideProfile extends StatefulWidget {
}
class _OverrideProfileState extends State<OverrideProfile> {
final GlobalKey<CacheItemExtentListViewState> _ruleListKey = GlobalKey();
final _controller = ScrollController();
double _currentMaxWidth = 0;
@@ -86,13 +85,6 @@ class _OverrideProfileState extends State<OverrideProfile> {
);
}
_handleTryClearCache(double maxWidth) {
if (_currentMaxWidth != maxWidth) {
_currentMaxWidth = maxWidth;
_ruleListKey.currentState?.clearCache();
}
}
_buildContent() {
return Consumer(
builder: (_, ref, child) {
@@ -116,7 +108,7 @@ class _OverrideProfileState extends State<OverrideProfile> {
},
child: LayoutBuilder(
builder: (_, constraints) {
_handleTryClearCache(constraints.maxWidth - 104);
_currentMaxWidth = constraints.maxWidth - 104;
return CommonAutoHiddenScrollBar(
controller: _controller,
child: CustomScrollView(
@@ -148,7 +140,6 @@ class _OverrideProfileState extends State<OverrideProfile> {
padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 0),
sliver: RuleContent(
maxWidth: _currentMaxWidth,
ruleListKey: _ruleListKey,
),
),
SliverToBoxAdapter(
@@ -228,7 +219,7 @@ class _OverrideProfileState extends State<OverrideProfile> {
message: TextSpan(
text: appLocalizations.saveTip,
),
confirmText: appLocalizations.tip,
confirmText: appLocalizations.save,
);
if (res != true) {
return;
@@ -449,12 +440,10 @@ class RuleTitle extends ConsumerWidget {
}
class RuleContent extends ConsumerWidget {
final Key ruleListKey;
final double maxWidth;
const RuleContent({
super.key,
required this.ruleListKey,
required this.maxWidth,
});
@@ -602,7 +591,7 @@ class RuleContent extends ConsumerWidget {
);
}
return CacheItemExtentSliverReorderableList(
key: ruleListKey,
tag: CacheTag.rules,
itemBuilder: (context, index) {
final rule = rules[index];
return GestureDetector(
@@ -873,6 +862,8 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
builder: (filed) {
return DropdownMenu(
width: 200,
enableFilter: false,
enableSearch: false,
controller: _subRuleController,
label: Text(appLocalizations.subRule),
menuHeight: 250,
@@ -890,11 +881,11 @@ class _AddRuleDialogState extends State<AddRuleDialog> {
builder: (filed) {
return DropdownMenu(
controller: _ruleTargetController,
initialSelection: filed.value,
label: Text(appLocalizations.ruleTarget),
width: 200,
menuHeight: 250,
enableFilter: true,
enableFilter: false,
enableSearch: false,
dropdownMenuEntries: _targetItems,
errorText: filed.errorText,
);

View File

@@ -370,7 +370,7 @@ class ProfileItem extends StatelessWidget {
),
),
title: Container(
padding: const EdgeInsets.symmetric(vertical: 8),
padding: const EdgeInsets.symmetric(vertical: 4),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,

View File

@@ -6,7 +6,7 @@ import 'package:fl_clash/state.dart';
double get listHeaderHeight {
final measure = globalState.measure;
return 24 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;
return 28 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;
}
double getItemHeight(ProxyCardType proxyCardType) {

View File

@@ -414,7 +414,10 @@ class _ListHeaderState extends State<ListHeader>
return Consumer(
builder: (_, ref, child) {
final iconStyle = ref.watch(
proxiesStyleSettingProvider.select((state) => state.iconStyle));
proxiesStyleSettingProvider.select(
(state) => state.iconStyle,
),
);
final icon = ref.watch(proxiesStyleSettingProvider.select((state) {
final iconMapEntryList = state.iconMap.entries.toList();
final index = iconMapEntryList.indexWhere((item) {
@@ -430,30 +433,44 @@ class _ListHeaderState extends State<ListHeader>
return this.icon;
}));
return switch (iconStyle) {
ProxiesIconStyle.standard => Container(
height: 48,
width: 48,
margin: const EdgeInsets.only(
right: 16,
),
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: context.colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(12),
),
clipBehavior: Clip.antiAlias,
child: CommonTargetIcon(
src: icon,
size: 32,
),
ProxiesIconStyle.standard => LayoutBuilder(
builder: (_, constraints) {
return Container(
margin: const EdgeInsets.only(
right: 16,
),
child: AspectRatio(
aspectRatio: 1,
child: Container(
height: constraints.maxHeight,
width: constraints.maxWidth,
alignment: Alignment.center,
padding: EdgeInsets.all(6.ap),
decoration: BoxDecoration(
color: context.colorScheme.secondaryContainer,
borderRadius: BorderRadius.circular(12),
),
clipBehavior: Clip.antiAlias,
child: CommonTargetIcon(
src: icon,
size: constraints.maxHeight - 12.ap,
),
),
),
);
},
),
ProxiesIconStyle.icon => Container(
margin: const EdgeInsets.only(
right: 16,
),
child: CommonTargetIcon(
src: icon,
size: 42,
child: LayoutBuilder(
builder: (_, constraints) {
return CommonTargetIcon(
src: icon,
size: constraints.maxHeight - 8,
);
},
),
),
ProxiesIconStyle.none => Container(),

View File

@@ -78,7 +78,7 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
return AdaptiveSheetScaffold(
type: type,
body: const ProxiesSetting(),
title: appLocalizations.proxiesSetting,
title: appLocalizations.settings,
);
},
);
@@ -123,8 +123,13 @@ class _ProxiesFragmentState extends ConsumerState<ProxiesFragment>
@override
Widget build(BuildContext context) {
final proxiesType =
ref.watch(proxiesStyleSettingProvider.select((state) => state.type));
final proxiesType = ref.watch(
proxiesStyleSettingProvider.select(
(state) => state.type,
),
);
ref.watch(themeSettingProvider.select((state) => state.textScale));
return switch (proxiesType) {
ProxiesType.tab => ProxiesTabFragment(
key: _proxiesTabKey,

View File

@@ -147,22 +147,21 @@ class _GeoDataListItemState extends State<GeoDataListItem> {
FutureBuilder<FileInfo>(
future: _getGeoFileLastModified(geoItem.fileName),
builder: (_, snapshot) {
final height = globalState.measure.bodyMediumHeight;
return SizedBox(
height: 24,
child: FadeThroughBox(
key: Key("fade_box_${geoItem.label}"),
child: snapshot.data == null
? const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
),
)
: Text(
snapshot.data!.desc,
height: height,
child: snapshot.data == null
? SizedBox(
width: height,
height: height,
child: CircularProgressIndicator(
strokeWidth: 2,
),
),
)
: Text(
snapshot.data!.desc,
style: context.textTheme.bodyMedium,
),
);
},
),

View File

@@ -1,3 +1,5 @@
// ignore_for_file: deprecated_member_use
import 'dart:math';
import 'dart:ui' as ui;
@@ -39,7 +41,20 @@ class ThemeFragment extends StatelessWidget {
@override
Widget build(BuildContext context) {
return SingleChildScrollView(child: ThemeColorsBox());
return SingleChildScrollView(
child: Column(
spacing: 24,
children: [
_ThemeModeItem(),
_PrimaryColorItem(),
_PrueBlackItem(),
_TextScaleFactorItem(),
const SizedBox(
height: 64,
),
],
),
);
}
}
@@ -57,42 +72,14 @@ class ItemCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(
top: 16,
),
child: Wrap(
runSpacing: 16,
children: [
InfoHeader(
info: info,
actions: actions,
),
child,
],
),
);
}
}
class ThemeColorsBox extends ConsumerStatefulWidget {
const ThemeColorsBox({super.key});
@override
ConsumerState<ThemeColorsBox> createState() => _ThemeColorsBoxState();
}
class _ThemeColorsBoxState extends ConsumerState<ThemeColorsBox> {
@override
Widget build(BuildContext context) {
return Column(
return Wrap(
runSpacing: 16,
children: [
_ThemeModeItem(),
_PrimaryColorItem(),
_PrueBlackItem(),
const SizedBox(
height: 64,
InfoHeader(
info: info,
actions: actions,
),
child,
],
);
}
@@ -296,137 +283,153 @@ class _PrimaryColorItemState extends ConsumerState<_PrimaryColorItem> {
@override
Widget build(BuildContext context) {
final vm3 = ref.watch(
final vm4 = ref.watch(
themeSettingProvider.select(
(state) => VM3(
(state) => VM4(
a: state.primaryColor,
b: state.primaryColors,
c: state.schemeVariant,
d: state.primaryColor == defaultPrimaryColor &&
intListEquality.equals(state.primaryColors, defaultPrimaryColors),
),
),
);
final primaryColor = vm3.a;
final primaryColors = [null, ...vm3.b];
final schemeVariant = vm3.c;
final primaryColor = vm4.a;
final primaryColors = [null, ...vm4.b];
final schemeVariant = vm4.c;
final isEquals = vm4.d;
return ItemCard(
info: Info(
label: appLocalizations.themeColor,
iconData: Icons.palette,
),
actions: genActions(
[
if (_removablePrimaryColor == null)
FilledButton(
style: ButtonStyle(
visualDensity: VisualDensity.compact,
),
onPressed: _handleChangeSchemeVariant,
child: Text(Intl.message("${schemeVariant.name}Scheme")),
),
_removablePrimaryColor != null
? FilledButton(
style: ButtonStyle(
visualDensity: VisualDensity.compact,
),
onPressed: () {
setState(() {
_removablePrimaryColor = null;
});
},
child: Text(appLocalizations.cancel),
)
: IconButton.filledTonal(
iconSize: 20,
padding: EdgeInsets.all(4),
visualDensity: VisualDensity.compact,
onPressed: _handleReset,
icon: Icon(Icons.replay),
)
],
space: 8,
),
child: Container(
margin: const EdgeInsets.only(
left: 16,
right: 16,
bottom: 16,
return CommonPopScope(
onPop: () {
if (_removablePrimaryColor != null) {
setState(() {
_removablePrimaryColor = null;
});
return false;
}
return true;
},
child: ItemCard(
info: Info(
label: appLocalizations.themeColor,
iconData: Icons.palette,
),
child: LayoutBuilder(
builder: (_, constraints) {
final columns = _calcColumns(constraints.maxWidth);
final itemWidth =
(constraints.maxWidth - (columns - 1) * 16) / columns;
return Wrap(
spacing: 16,
runSpacing: 16,
children: [
for (final color in primaryColors)
Container(
clipBehavior: Clip.none,
width: itemWidth,
height: itemWidth,
child: Stack(
alignment: Alignment.center,
actions: genActions(
[
if (_removablePrimaryColor == null)
FilledButton(
style: ButtonStyle(
visualDensity: VisualDensity.compact,
),
onPressed: _handleChangeSchemeVariant,
child: Text(Intl.message("${schemeVariant.name}Scheme")),
),
if (_removablePrimaryColor != null)
FilledButton(
style: ButtonStyle(
visualDensity: VisualDensity.compact,
),
onPressed: () {
setState(() {
_removablePrimaryColor = null;
});
},
child: Text(appLocalizations.cancel),
),
if (_removablePrimaryColor == null && !isEquals)
IconButton.filledTonal(
iconSize: 20,
padding: EdgeInsets.all(4),
visualDensity: VisualDensity.compact,
onPressed: _handleReset,
icon: Icon(Icons.replay),
)
],
space: 8,
),
child: Container(
margin: const EdgeInsets.symmetric(
horizontal: 16,
),
child: LayoutBuilder(
builder: (_, constraints) {
final columns = _calcColumns(constraints.maxWidth);
final itemWidth =
(constraints.maxWidth - (columns - 1) * 16) / columns;
return Wrap(
spacing: 16,
runSpacing: 16,
children: [
for (final color in primaryColors)
Container(
clipBehavior: Clip.none,
children: [
EffectGestureDetector(
child: ColorSchemeBox(
isSelected: color == primaryColor,
primaryColor: color != null ? Color(color) : null,
onPressed: () {
ref
.read(themeSettingProvider.notifier)
.updateState(
(state) => state.copyWith(
primaryColor: color,
),
);
width: itemWidth,
height: itemWidth,
child: Stack(
alignment: Alignment.center,
clipBehavior: Clip.none,
children: [
EffectGestureDetector(
child: ColorSchemeBox(
isSelected: color == primaryColor,
primaryColor: color != null ? Color(color) : null,
onPressed: () {
setState(() {
_removablePrimaryColor = null;
});
ref
.read(themeSettingProvider.notifier)
.updateState(
(state) => state.copyWith(
primaryColor: color,
),
);
},
),
onLongPress: () {
setState(() {
_removablePrimaryColor = color;
});
},
),
onLongPress: () {
setState(() {
_removablePrimaryColor = color;
});
},
),
if (_removablePrimaryColor != null &&
_removablePrimaryColor == color)
Container(
color: Colors.white.opacity0,
padding: EdgeInsets.all(8),
child: IconButton.filledTonal(
onPressed: _handleDel,
padding: EdgeInsets.all(12),
iconSize: 30,
icon: Icon(
color: context.colorScheme.primary,
Icons.delete,
if (_removablePrimaryColor != null &&
_removablePrimaryColor == color)
Container(
color: Colors.white.opacity0,
padding: EdgeInsets.all(8),
child: IconButton.filledTonal(
onPressed: _handleDel,
padding: EdgeInsets.all(12),
iconSize: 30,
icon: Icon(
color: context.colorScheme.primary,
Icons.delete,
),
),
),
),
],
),
),
if (_removablePrimaryColor == null)
Container(
width: itemWidth,
height: itemWidth,
padding: EdgeInsets.all(
4,
),
child: IconButton.filledTonal(
onPressed: _handleAdd,
iconSize: 32,
icon: Icon(
color: context.colorScheme.primary,
Icons.add,
],
),
),
)
],
);
},
if (_removablePrimaryColor == null)
Container(
width: itemWidth,
height: itemWidth,
padding: EdgeInsets.all(
4,
),
child: IconButton.filledTonal(
onPressed: _handleAdd,
iconSize: 32,
icon: Icon(
color: context.colorScheme.primary,
Icons.add,
),
),
)
],
);
},
),
),
),
);
@@ -438,33 +441,118 @@ class _PrueBlackItem extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final prueBlack =
ref.watch(themeSettingProvider.select((state) => state.pureBlack));
return Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: ListItem.switchItem(
leading: Icon(
Icons.contrast,
),
horizontalTitleGap: 12,
title: Text(
appLocalizations.pureBlackMode,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: context.colorScheme.onSurfaceVariant,
),
),
delegate: SwitchDelegate(
value: prueBlack,
onChanged: (value) {
ref.read(themeSettingProvider.notifier).updateState(
(state) => state.copyWith(
pureBlack: value,
),
);
},
),
final prueBlack = ref.watch(
themeSettingProvider.select(
(state) => state.pureBlack,
),
);
return ListItem.switchItem(
leading: Icon(
Icons.contrast,
),
horizontalTitleGap: 12,
title: Text(
appLocalizations.pureBlackMode,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: context.colorScheme.onSurfaceVariant,
),
),
delegate: SwitchDelegate(
value: prueBlack,
onChanged: (value) {
ref.read(themeSettingProvider.notifier).updateState(
(state) => state.copyWith(
pureBlack: value,
),
);
},
),
);
}
}
class _TextScaleFactorItem extends ConsumerWidget {
const _TextScaleFactorItem();
@override
Widget build(BuildContext context, WidgetRef ref) {
final textScale = ref.watch(
themeSettingProvider.select(
(state) => state.textScale,
),
);
final String process = "${((textScale.scale * 100) as double).round()}%";
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: EdgeInsets.only(bottom: 8),
child: ListItem.switchItem(
leading: Icon(
Icons.text_fields,
),
horizontalTitleGap: 12,
title: Text(
appLocalizations.textScale,
style: Theme.of(context).textTheme.titleSmall?.copyWith(
color: context.colorScheme.onSurfaceVariant,
),
),
delegate: SwitchDelegate(
value: textScale.enable,
onChanged: (value) {
ref.read(themeSettingProvider.notifier).updateState(
(state) => state.copyWith.textScale(
enable: value,
),
);
},
),
),
),
Padding(
padding: EdgeInsets.symmetric(horizontal: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
spacing: 32,
children: [
Expanded(
child: DisabledMask(
status: !textScale.enable,
child: ActivateBox(
active: textScale.enable,
child: SliderTheme(
data: _SliderDefaultsM3(context),
child: Slider(
padding: EdgeInsets.zero,
min: minTextScale,
max: maxTextScale,
value: textScale.scale,
onChanged: (value) {
ref.read(themeSettingProvider.notifier).updateState(
(state) => state.copyWith.textScale(
scale: value,
),
);
},
),
),
),
),
),
Padding(
padding: EdgeInsets.only(right: 4),
child: Text(
process,
style: context.textTheme.titleMedium,
),
),
],
),
),
],
);
}
}
@@ -530,3 +618,112 @@ class _PaletteDialogState extends State<_PaletteDialog> {
);
}
}
class _SliderDefaultsM3 extends SliderThemeData {
_SliderDefaultsM3(this.context) : super(trackHeight: 16.0);
final BuildContext context;
late final ColorScheme _colors = Theme.of(context).colorScheme;
@override
Color? get activeTrackColor => _colors.primary;
@override
Color? get inactiveTrackColor => _colors.secondaryContainer;
@override
Color? get secondaryActiveTrackColor => _colors.primary.withOpacity(0.54);
@override
Color? get disabledActiveTrackColor => _colors.onSurface.withOpacity(0.38);
@override
Color? get disabledInactiveTrackColor => _colors.onSurface.withOpacity(0.12);
@override
Color? get disabledSecondaryActiveTrackColor =>
_colors.onSurface.withOpacity(0.38);
@override
Color? get activeTickMarkColor => _colors.onPrimary.withOpacity(1.0);
@override
Color? get inactiveTickMarkColor =>
_colors.onSecondaryContainer.withOpacity(1.0);
@override
Color? get disabledActiveTickMarkColor => _colors.onInverseSurface;
@override
Color? get disabledInactiveTickMarkColor => _colors.onSurface;
@override
Color? get thumbColor => _colors.primary;
@override
Color? get disabledThumbColor => _colors.onSurface.withOpacity(0.38);
@override
Color? get overlayColor =>
WidgetStateColor.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.dragged)) {
return _colors.primary.withOpacity(0.1);
}
if (states.contains(WidgetState.hovered)) {
return _colors.primary.withOpacity(0.08);
}
if (states.contains(WidgetState.focused)) {
return _colors.primary.withOpacity(0.1);
}
return Colors.transparent;
});
@override
TextStyle? get valueIndicatorTextStyle =>
Theme.of(context).textTheme.labelLarge!.copyWith(
color: _colors.onInverseSurface,
);
@override
Color? get valueIndicatorColor => _colors.inverseSurface;
@override
SliderComponentShape? get valueIndicatorShape =>
const RoundedRectSliderValueIndicatorShape();
@override
SliderComponentShape? get thumbShape => const HandleThumbShape();
@override
SliderTrackShape? get trackShape => const GappedSliderTrackShape();
@override
SliderComponentShape? get overlayShape => const RoundSliderOverlayShape();
@override
SliderTickMarkShape? get tickMarkShape =>
const RoundSliderTickMarkShape(tickMarkRadius: 4.0 / 2);
@override
WidgetStateProperty<Size?>? get thumbSize {
return WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.disabled)) {
return const Size(4.0, 44.0);
}
if (states.contains(WidgetState.hovered)) {
return const Size(4.0, 44.0);
}
if (states.contains(WidgetState.focused)) {
return const Size(2.0, 44.0);
}
if (states.contains(WidgetState.pressed)) {
return const Size(2.0, 44.0);
}
return const Size(4.0, 44.0);
});
}
@override
double? get trackGap => 6.0;
}

View File

@@ -14,6 +14,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:intl/intl.dart';
import 'backup_and_recovery.dart';
import 'developer.dart';
import 'theme.dart';
import 'package:path/path.dart' show dirname, join;
@@ -54,11 +55,12 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
);
}
List<Widget> _getOtherList() {
List<Widget> _getOtherList(bool enableDeveloperMode) {
return generateSection(
title: appLocalizations.other,
items: [
_DisclaimerItem(),
if (enableDeveloperMode) _DeveloperItem(),
_InfoItem(),
],
);
@@ -82,7 +84,11 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
@override
Widget build(BuildContext context) {
ref.watch(appSettingProvider.select((state) => state.locale));
final vm2 = ref.watch(
appSettingProvider.select(
(state) => VM2(a: state.locale, b: state.developerMode),
),
);
final items = [
Consumer(
builder: (_, ref, __) {
@@ -99,7 +105,7 @@ class _ToolboxFragmentState extends ConsumerState<ToolsFragment> {
},
),
..._getSettingList(),
..._getOtherList(),
..._getOtherList(vm2.b),
];
return ListView.builder(
itemCount: items.length,
@@ -297,3 +303,19 @@ class _InfoItem extends StatelessWidget {
);
}
}
class _DeveloperItem extends StatelessWidget {
const _DeveloperItem();
@override
Widget build(BuildContext context) {
return ListItem.open(
leading: const Icon(Icons.developer_board),
title: Text(appLocalizations.developerMode),
delegate: OpenDelegate(
title: appLocalizations.developerMode,
widget: const DeveloperView(),
),
);
}
}

View File

@@ -1,389 +0,0 @@
{
"rule": "Rule",
"global": "Global",
"direct": "Direct",
"dashboard": "Dashboard",
"proxies": "Proxies",
"profile": "Profile",
"profiles": "Profiles",
"tools": "Tools",
"logs": "Logs",
"logsDesc": "Log capture records",
"resources": "Resources",
"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",
"upload": "Upload",
"download": "Download",
"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",
"more": "More",
"other": "Other",
"about": "About",
"en": "English",
"ja": "Japanese",
"ru": "Russian",
"zh_CN": "Simplified Chinese",
"theme": "Theme",
"themeDesc": "Set dark mode,adjust the color",
"override": "Override",
"overrideDesc": "Override Proxy related config",
"allowLan": "AllowLan",
"allowLanDesc": "Allow access proxy through the LAN",
"tun": "TUN",
"tunDesc": "only effective in administrator mode",
"minimizeOnExit": "Minimize on exit",
"minimizeOnExitDesc": "Modify the default system exit event",
"autoLaunch": "Auto launch",
"autoLaunchDesc": "Follow the system self startup",
"silentLaunch": "SilentLaunch",
"silentLaunchDesc": "Start in the background",
"autoRun": "AutoRun",
"autoRunDesc": "Auto run when the application is opened",
"logcat": "Logcat",
"logcatDesc": "Disabling will hide the log entry",
"autoCheckUpdate": "Auto check updates",
"autoCheckUpdateDesc": "Auto check for updates when the app starts",
"accessControl": "AccessControl",
"accessControlDesc": "Configure application access proxy",
"application": "Application",
"applicationDesc": "Modify application related settings",
"edit": "Edit",
"confirm": "Confirm",
"update": "Update",
"add": "Add",
"save": "Save",
"delete": "Delete",
"years": "Years",
"months": "Months",
"hours": "Hours",
"days": "Days",
"minutes": "Minutes",
"seconds": "Seconds",
"ago": " Ago",
"just": "Just",
"qrcode": "QR code",
"qrcodeDesc": "Scan QR code to obtain profile",
"url": "URL",
"urlDesc": "Obtain profile through URL",
"file": "File",
"fileDesc": "Directly upload profile",
"name": "Name",
"profileNameNullValidationDesc": "Please input the profile name",
"profileUrlNullValidationDesc": "Please input the profile URL",
"profileUrlInvalidValidationDesc": "Please input a valid profile URL",
"autoUpdate": "Auto update",
"autoUpdateInterval": "Auto update interval (minutes)",
"profileAutoUpdateIntervalNullValidationDesc": "Please enter the auto update interval time",
"profileAutoUpdateIntervalInvalidValidationDesc": "Please input a valid interval time format",
"themeMode": "Theme mode",
"themeColor": "Theme color",
"preview": "Preview",
"auto": "Auto",
"light": "Light",
"dark": "Dark",
"importFromURL": "Import from URL",
"submit": "Submit",
"doYouWantToPass": "Do you want to pass",
"create": "Create",
"defaultSort": "Sort by default",
"delaySort": "Sort by delay",
"nameSort": "Sort by name",
"pleaseUploadFile": "Please upload file",
"pleaseUploadValidQrcode": "Please upload a valid QR code",
"blacklistMode": "Blacklist mode",
"whitelistMode": "Whitelist mode",
"filterSystemApp": "Filter system app",
"cancelFilterSystemApp": "Cancel filter system app",
"selectAll": "Select all",
"cancelSelectAll": "Cancel select all",
"appAccessControl": "App access control",
"accessControlAllowDesc": "Only allow selected app to enter VPN",
"accessControlNotAllowDesc": "The selected application will be excluded from VPN",
"selected": "Selected",
"unableToUpdateCurrentProfileDesc": "unable to update current profile",
"noMoreInfoDesc": "No more info",
"profileParseErrorDesc": "profile parse error",
"proxyPort": "ProxyPort",
"proxyPortDesc": "Set the Clash listening port",
"port": "Port",
"logLevel": "LogLevel",
"show": "Show",
"exit": "Exit",
"systemProxy": "System proxy",
"project": "Project",
"core": "Core",
"tabAnimation": "Tab animation",
"desc": "A multi-platform proxy client based on ClashMeta, simple and easy to use, open-source and ad-free.",
"startVpn": "Starting VPN...",
"stopVpn": "Stopping VPN...",
"discovery": "Discovery a new version",
"compatible": "Compatibility mode",
"compatibleDesc": "Opening it will lose part of its application ability and gain the support of full amount of Clash.",
"notSelectedTip": "The current proxy group cannot be selected.",
"tip": "tip",
"backupAndRecovery": "Backup and Recovery",
"backupAndRecoveryDesc": "Sync data via WebDAV or file",
"account": "Account",
"backup": "Backup",
"recovery": "Recovery",
"recoveryProfiles": "Only recovery profiles",
"recoveryAll": "Recovery all data",
"recoverySuccess": "Recovery success",
"backupSuccess": "Backup success",
"noInfo": "No info",
"pleaseBindWebDAV": "Please bind WebDAV",
"bind": "Bind",
"connectivity": "Connectivity",
"webDAVConfiguration": "WebDAV configuration",
"address": "Address",
"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",
"goDownload": "Go to download",
"unknown": "Unknown",
"geoData": "GeoData",
"externalResources": "External resources",
"checking": "Checking...",
"country": "Country",
"checkError": "Check error",
"search": "Search",
"allowBypass": "Allow applications to bypass VPN",
"allowBypassDesc": "Some apps can bypass VPN when turned on",
"externalController": "ExternalController",
"externalControllerDesc": "Once enabled, the Clash kernel can be controlled on port 9090",
"ipv6Desc": "When turned on it will be able to receive IPv6 traffic",
"app": "App",
"general": "General",
"vpnSystemProxyDesc": "Attach HTTP proxy to VpnService",
"systemProxyDesc": "Attach HTTP proxy to VpnService",
"unifiedDelay": "Unified delay",
"unifiedDelayDesc": "Remove extra delays such as handshaking",
"tcpConcurrent": "TCP concurrent",
"tcpConcurrentDesc": "Enabling it will allow TCP concurrency",
"geodataLoader": "Geo Low Memory Mode",
"geodataLoaderDesc": "Enabling will use the Geo low memory loader",
"requests": "Requests",
"requestsDesc": "View recently request records",
"findProcessMode": "Find process",
"init": "Init",
"infiniteTime": "Long term effective",
"expirationTime": "Expiration time",
"connections": "Connections",
"connectionsDesc": "View current connections data",
"nullRequestsDesc": "No requests",
"nullConnectionsDesc": "No connections",
"intranetIP": "Intranet IP",
"view": "View",
"cut": "Cut",
"copy": "Copy",
"paste": "Paste",
"testUrl": "Test url",
"sync": "Sync",
"exclude": "Hidden from recent tasks",
"excludeDesc": "When the app is in the background, the app is hidden from the recent task",
"oneColumn": "One column",
"twoColumns": "Two columns",
"threeColumns": "Three columns",
"fourColumns": "Four columns",
"expand": "Standard",
"shrink": "Shrink",
"min": "Min",
"tab": "Tab",
"list": "List",
"delay": "Delay",
"style": "Style",
"size": "Size",
"sort": "Sort",
"columns": "Columns",
"proxiesSetting": "Proxies setting",
"proxyGroup": "Proxy group",
"go": "Go",
"externalLink": "External link",
"otherContributors": "Other contributors",
"autoCloseConnections": "Auto close connections",
"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",
"local": "Local",
"remote": "Remote",
"remoteBackupDesc": "Backup local data to WebDAV",
"remoteRecoveryDesc": "Recovery data from WebDAV",
"localBackupDesc": "Backup local data to local",
"localRecoveryDesc": "Recovery data from file",
"mode": "Mode",
"time": "Time",
"source": "Source",
"allApps": "All apps",
"onlyOtherApps": "Only third-party apps",
"action": "Action",
"intelligentSelected": "Intelligent selection",
"clipboardImport": "Clipboard import",
"clipboardExport": "Export clipboard",
"layout": "Layout",
"tight": "Tight",
"standard": "Standard",
"loose": "Loose",
"profilesSort": "Profiles sort",
"start": "Start",
"stop": "Stop",
"appDesc": "Processing app related settings",
"vpnDesc": "Modify VPN related settings",
"dnsDesc": "Update DNS related settings",
"key": "Key",
"value": "Value",
"notEmpty": "Cannot be empty",
"hostsDesc": "Add Hosts",
"vpnTip": "Changes take effect after restarting the VPN",
"vpnEnableDesc": "Auto routes all system traffic through VpnService",
"options": "Options",
"loopback": "Loopback unlock tool",
"loopbackDesc": "Used for UWP loopback unlocking",
"providers": "Providers",
"proxyProviders": "Proxy providers",
"ruleProviders": "Rule providers",
"overrideDns": "Override Dns",
"overrideDnsDesc": "Turning it on will override the DNS options in the profile",
"status": "Status",
"statusDesc": "System DNS will be used when turned off",
"preferH3Desc": "Prioritize the use of DOH's http/3",
"respectRules": "Respect rules",
"respectRulesDesc": "DNS connection following rules, need to configure proxy-server-nameserver",
"dnsMode": "DNS mode",
"fakeipRange": "Fakeip range",
"fakeipFilter": "Fakeip filter",
"defaultNameserver": "Default nameserver",
"defaultNameserverDesc": "For resolving DNS server",
"nameserver": "Nameserver",
"nameserverDesc": "For resolving domain",
"useHosts": "Use hosts",
"useSystemHosts": "Use system hosts",
"nameserverPolicy": "Nameserver policy",
"nameserverPolicyDesc": "Specify the corresponding nameserver policy",
"proxyNameserver": "Proxy nameserver",
"proxyNameserverDesc": "Domain for resolving proxy nodes",
"fallback": "Fallback",
"fallbackDesc": "Generally use offshore DNS",
"fallbackFilter": "Fallback filter",
"geoipCode": "Geoip code",
"ipcidr": "Ipcidr",
"domain": "Domain",
"reset": "Reset",
"action_view": "Show/Hide",
"action_start": "Start/Stop",
"action_mode": "Switch mode",
"action_proxy": "System proxy",
"action_tun": "TUN",
"disclaimer": "Disclaimer",
"disclaimerDesc": "This software is only used for non-commercial purposes such as learning exchanges and scientific research. It is strictly prohibited to use this software for commercial purposes. Any commercial activity, if any, has nothing to do with this software.",
"agree": "Agree",
"hotkeyManagement": "Hotkey Management",
"hotkeyManagementDesc": "Use keyboard to control applications",
"pressKeyboard": "Please press the keyboard.",
"inputCorrectHotkey": "Please enter the correct hotkey",
"hotkeyConflict": "Hotkey conflict",
"remove": "Remove",
"noHotKey": "No HotKey",
"noNetwork": "No network",
"ipv6InboundDesc": "Allow IPv6 inbound",
"exportLogs": "Export logs",
"exportSuccess": "Export Success",
"iconStyle": "Icon style",
"onlyIcon": "Icon",
"noIcon": "None",
"stackMode": "Stack mode",
"network": "Network",
"networkDesc": "Modify network-related settings",
"bypassDomain": "Bypass domain",
"bypassDomainDesc": "Only takes effect when the system proxy is enabled",
"resetTip": "Make sure to reset",
"regExp": "RegExp",
"icon": "Icon",
"iconConfiguration": "Icon configuration",
"noData": "No data",
"adminAutoLaunch": "Admin auto launch",
"adminAutoLaunchDesc": "Boot up by using admin mode",
"fontFamily": "FontFamily",
"systemFont": "System font",
"toggle": "Toggle",
"system": "System",
"routeMode": "Route mode",
"routeMode_bypassPrivate": "Bypass private route address",
"routeMode_config": "Use config",
"routeAddress": "Route address",
"routeAddressDesc": "Config listen route address",
"pleaseInputAdminPassword": "Please enter the admin password",
"copyEnvVar": "Copying environment variables",
"memoryInfo": "Memory info",
"cancel": "Cancel",
"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",
"basicConfig": "Basic configuration",
"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",
"fidelityScheme": "Fidelity",
"monochromeScheme": "Monochrome",
"neutralScheme": "Neutral",
"vibrantScheme": "Vibrant",
"expressiveScheme": "Expressive",
"contentScheme": "Content",
"rainbowScheme": "Rainbow",
"fruitSaladScheme": "FruitSalad"
}

View File

@@ -1,389 +0,0 @@
{
"rule": "ルール",
"global": "グローバル",
"direct": "ダイレクト",
"dashboard": "ダッシュボード",
"proxies": "プロキシ",
"profile": "プロファイル",
"profiles": "プロファイル一覧",
"tools": "ツール",
"logs": "ログ",
"logsDesc": "ログキャプチャ記録",
"resources": "リソース",
"resourcesDesc": "外部リソース関連情報",
"trafficUsage": "トラフィック使用量",
"coreInfo": "コア情報",
"nullCoreInfoDesc": "コア情報を取得できません",
"networkSpeed": "ネットワーク速度",
"outboundMode": "アウトバウンドモード",
"networkDetection": "ネットワーク検出",
"upload": "アップロード",
"download": "ダウンロード",
"noProxy": "プロキシなし",
"noProxyDesc": "プロファイルを作成するか、有効なプロファイルを追加してください",
"nullProfileDesc": "プロファイルがありません。追加してください",
"nullLogsDesc": "ログがありません",
"settings": "設定",
"language": "言語",
"defaultText": "デフォルト",
"more": "詳細",
"other": "その他",
"about": "について",
"en": "英語",
"ja": "日本語",
"ru": "ロシア語",
"zh_CN": "簡体字中国語",
"theme": "テーマ",
"themeDesc": "ダークモードの設定、色の調整",
"override": "上書き",
"overrideDesc": "プロキシ関連設定を上書き",
"allowLan": "LANを許可",
"allowLanDesc": "LAN経由でのプロキシアクセスを許可",
"tun": "TUN",
"tunDesc": "管理者モードでのみ有効",
"minimizeOnExit": "終了時に最小化",
"minimizeOnExitDesc": "システムの終了イベントを変更",
"autoLaunch": "自動起動",
"autoLaunchDesc": "システムの自動起動に従う",
"silentLaunch": "バックグラウンド起動",
"silentLaunchDesc": "バックグラウンドで起動",
"autoRun": "自動実行",
"autoRunDesc": "アプリ起動時に自動実行",
"logcat": "ログキャット",
"logcatDesc": "無効化するとログエントリを非表示",
"autoCheckUpdate": "自動更新チェック",
"autoCheckUpdateDesc": "起動時に更新を自動チェック",
"accessControl": "アクセス制御",
"accessControlDesc": "アプリケーションのプロキシアクセスを設定",
"application": "アプリケーション",
"applicationDesc": "アプリ関連設定を変更",
"edit": "編集",
"confirm": "確認",
"update": "更新",
"add": "追加",
"save": "保存",
"delete": "削除",
"years": "年",
"months": "月",
"hours": "時間",
"days": "日",
"minutes": "分",
"seconds": "秒",
"ago": "前",
"just": "たった今",
"qrcode": "QRコード",
"qrcodeDesc": "QRコードをスキャンしてプロファイルを取得",
"url": "URL",
"urlDesc": "URL経由でプロファイルを取得",
"file": "ファイル",
"fileDesc": "プロファイルを直接アップロード",
"name": "名前",
"profileNameNullValidationDesc": "プロファイル名を入力してください",
"profileUrlNullValidationDesc": "プロファイルURLを入力してください",
"profileUrlInvalidValidationDesc": "有効なプロファイルURLを入力してください",
"autoUpdate": "自動更新",
"autoUpdateInterval": "自動更新間隔(分)",
"profileAutoUpdateIntervalNullValidationDesc": "自動更新間隔を入力してください",
"profileAutoUpdateIntervalInvalidValidationDesc": "有効な間隔形式を入力してください",
"themeMode": "テーマモード",
"themeColor": "テーマカラー",
"preview": "プレビュー",
"auto": "自動",
"light": "ライト",
"dark": "ダーク",
"importFromURL": "URLからインポート",
"submit": "送信",
"doYouWantToPass": "通過させますか?",
"create": "作成",
"defaultSort": "デフォルト順",
"delaySort": "遅延順",
"nameSort": "名前順",
"pleaseUploadFile": "ファイルをアップロードしてください",
"pleaseUploadValidQrcode": "有効なQRコードをアップロードしてください",
"blacklistMode": "ブラックリストモード",
"whitelistMode": "ホワイトリストモード",
"filterSystemApp": "システムアプリを除外",
"cancelFilterSystemApp": "システムアプリの除外を解除",
"selectAll": "すべて選択",
"cancelSelectAll": "全選択解除",
"appAccessControl": "アプリアクセス制御",
"accessControlAllowDesc": "選択したアプリのみVPNを許可",
"accessControlNotAllowDesc": "選択したアプリをVPNから除外",
"selected": "選択済み",
"unableToUpdateCurrentProfileDesc": "現在のプロファイルを更新できません",
"noMoreInfoDesc": "追加情報なし",
"profileParseErrorDesc": "プロファイル解析エラー",
"proxyPort": "プロキシポート",
"proxyPortDesc": "Clashのリスニングポートを設定",
"port": "ポート",
"logLevel": "ログレベル",
"show": "表示",
"exit": "終了",
"systemProxy": "システムプロキシ",
"project": "プロジェクト",
"core": "コア",
"tabAnimation": "タブアニメーション",
"desc": "ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
"startVpn": "VPNを開始中...",
"stopVpn": "VPNを停止中...",
"discovery": "新しいバージョンを発見",
"compatible": "互換モード",
"compatibleDesc": "有効化すると一部機能を失いますが、Clashの完全サポートを獲得",
"notSelectedTip": "現在のプロキシグループは選択できません",
"tip": "ヒント",
"backupAndRecovery": "バックアップと復元",
"backupAndRecoveryDesc": "WebDAVまたはファイルでデータを同期",
"account": "アカウント",
"backup": "バックアップ",
"recovery": "復元",
"recoveryProfiles": "プロファイルのみ復元",
"recoveryAll": "全データ復元",
"recoverySuccess": "復元成功",
"backupSuccess": "バックアップ成功",
"noInfo": "情報なし",
"pleaseBindWebDAV": "WebDAVをバインドしてください",
"bind": "バインド",
"connectivity": "接続性:",
"webDAVConfiguration": "WebDAV設定",
"address": "アドレス",
"addressHelp": "WebDAVサーバーアドレス",
"addressTip": "有効なWebDAVアドレスを入力",
"password": "パスワード",
"passwordTip": "パスワードは必須です",
"accountTip": "アカウントは必須です",
"checkUpdate": "更新を確認",
"discoverNewVersion": "新バージョンを発見",
"checkUpdateError": "アプリは最新版です",
"goDownload": "ダウンロードへ",
"unknown": "不明",
"geoData": "地域データ",
"externalResources": "外部リソース",
"checking": "確認中...",
"country": "国",
"checkError": "確認エラー",
"search": "検索",
"allowBypass": "アプリがVPNをバイパスすることを許可",
"allowBypassDesc": "有効化すると一部アプリがVPNをバイパス",
"externalController": "外部コントローラー",
"externalControllerDesc": "有効化するとClashコアをポート9090で制御可能",
"ipv6Desc": "有効化するとIPv6トラフィックを受信可能",
"app": "アプリ",
"general": "一般",
"vpnSystemProxyDesc": "HTTPプロキシをVpnServiceに接続",
"systemProxyDesc": "HTTPプロキシをVpnServiceに接続",
"unifiedDelay": "統一遅延",
"unifiedDelayDesc": "ハンドシェイクなどの余分な遅延を削除",
"tcpConcurrent": "TCP並列処理",
"tcpConcurrentDesc": "TCP並列処理を許可",
"geodataLoader": "Geo低メモリモード",
"geodataLoaderDesc": "有効化するとGeo低メモリローダーを使用",
"requests": "リクエスト",
"requestsDesc": "最近のリクエスト記録を表示",
"findProcessMode": "プロセス検出",
"init": "初期化",
"infiniteTime": "長期有効",
"expirationTime": "有効期限",
"connections": "接続",
"connectionsDesc": "現在の接続データを表示",
"nullRequestsDesc": "リクエストなし",
"nullConnectionsDesc": "接続なし",
"intranetIP": "イントラネットIP",
"view": "表示",
"cut": "切り取り",
"copy": "コピー",
"paste": "貼り付け",
"testUrl": "URLテスト",
"sync": "同期",
"exclude": "最近のタスクから非表示",
"excludeDesc": "アプリがバックグラウンド時に最近のタスクから非表示",
"oneColumn": "1列",
"twoColumns": "2列",
"threeColumns": "3列",
"fourColumns": "4列",
"expand": "標準",
"shrink": "縮小",
"min": "最小化",
"tab": "タブ",
"list": "リスト",
"delay": "遅延",
"style": "スタイル",
"size": "サイズ",
"sort": "並び替え",
"columns": "列",
"proxiesSetting": "プロキシ設定",
"proxyGroup": "プロキシグループ",
"go": "移動",
"externalLink": "外部リンク",
"otherContributors": "その他の貢献者",
"autoCloseConnections": "接続を自動閉じる",
"autoCloseConnectionsDesc": "ノード変更後に接続を自動閉じる",
"onlyStatisticsProxy": "プロキシのみ統計",
"onlyStatisticsProxyDesc": "有効化するとプロキシトラフィックのみ統計",
"deleteProfileTip": "現在のプロファイルを削除しますか?",
"pureBlackMode": "純黒モード",
"keepAliveIntervalDesc": "TCPキープアライブ間隔",
"entries": " エントリ",
"local": "ローカル",
"remote": "リモート",
"remoteBackupDesc": "WebDAVにデータをバックアップ",
"remoteRecoveryDesc": "WebDAVからデータを復元",
"localBackupDesc": "ローカルにデータをバックアップ",
"localRecoveryDesc": "ファイルからデータを復元",
"mode": "モード",
"time": "時間",
"source": "ソース",
"allApps": "全アプリ",
"onlyOtherApps": "サードパーティアプリのみ",
"action": "アクション",
"intelligentSelected": "インテリジェント選択",
"clipboardImport": "クリップボードからインポート",
"clipboardExport": "クリップボードにエクスポート",
"layout": "レイアウト",
"tight": "密",
"standard": "標準",
"loose": "疎",
"profilesSort": "プロファイルの並び替え",
"start": "開始",
"stop": "停止",
"appDesc": "アプリ関連設定の処理",
"vpnDesc": "VPN関連設定の変更",
"dnsDesc": "DNS関連設定の更新",
"key": "キー",
"value": "値",
"notEmpty": "空欄不可",
"hostsDesc": "ホストを追加",
"vpnTip": "変更はVPN再起動後に有効",
"vpnEnableDesc": "VpnService経由で全システムトラフィックをルーティング",
"options": "オプション",
"loopback": "ループバック解除ツール",
"loopbackDesc": "UWPループバック解除用",
"providers": "プロバイダー",
"proxyProviders": "プロキシプロバイダー",
"ruleProviders": "ルールプロバイダー",
"overrideDns": "DNS上書き",
"overrideDnsDesc": "有効化するとプロファイルのDNS設定を上書き",
"status": "ステータス",
"statusDesc": "無効時はシステムDNSを使用",
"preferH3Desc": "DOHのHTTP/3を優先使用",
"respectRules": "ルール尊重",
"respectRulesDesc": "DNS接続がルールに従うproxy-server-nameserverの設定が必要",
"dnsMode": "DNSモード",
"fakeipRange": "Fakeip範囲",
"fakeipFilter": "Fakeipフィルター",
"defaultNameserver": "デフォルトネームサーバー",
"defaultNameserverDesc": "DNSサーバーの解決用",
"nameserver": "ネームサーバー",
"nameserverDesc": "ドメイン解決用",
"useHosts": "ホストを使用",
"useSystemHosts": "システムホストを使用",
"nameserverPolicy": "ネームサーバーポリシー",
"nameserverPolicyDesc": "対応するネームサーバーポリシーを指定",
"proxyNameserver": "プロキシネームサーバー",
"proxyNameserverDesc": "プロキシノード解決用ドメイン",
"fallback": "フォールバック",
"fallbackDesc": "通常はオフショアDNSを使用",
"fallbackFilter": "フォールバックフィルター",
"geoipCode": "GeoIPコード",
"ipcidr": "IPCIDR",
"domain": "ドメイン",
"reset": "リセット",
"action_view": "表示/非表示",
"action_start": "開始/停止",
"action_mode": "モード切替",
"action_proxy": "システムプロキシ",
"action_tun": "TUN",
"disclaimer": "免責事項",
"disclaimerDesc": "本ソフトウェアは学習交流や科学研究などの非営利目的でのみ使用されます。商用利用は厳禁です。いかなる商用活動も本ソフトウェアとは無関係です。",
"agree": "同意",
"hotkeyManagement": "ホットキー管理",
"hotkeyManagementDesc": "キーボードでアプリを制御",
"pressKeyboard": "キーボードを押してください",
"inputCorrectHotkey": "正しいホットキーを入力",
"hotkeyConflict": "ホットキー競合",
"remove": "削除",
"noHotKey": "ホットキーなし",
"noNetwork": "ネットワークなし",
"ipv6InboundDesc": "IPv6インバウンドを許可",
"exportLogs": "ログをエクスポート",
"exportSuccess": "エクスポート成功",
"iconStyle": "アイコンスタイル",
"onlyIcon": "アイコンのみ",
"noIcon": "なし",
"stackMode": "スタックモード",
"network": "ネットワーク",
"networkDesc": "ネットワーク関連設定の変更",
"bypassDomain": "バイパスドメイン",
"bypassDomainDesc": "システムプロキシ有効時のみ適用",
"resetTip": "リセットを確定",
"regExp": "正規表現",
"icon": "アイコン",
"iconConfiguration": "アイコン設定",
"noData": "データなし",
"adminAutoLaunch": "管理者自動起動",
"adminAutoLaunchDesc": "管理者モードで起動",
"fontFamily": "フォントファミリー",
"systemFont": "システムフォント",
"toggle": "トグル",
"system": "システム",
"routeMode": "ルートモード",
"routeMode_bypassPrivate": "プライベートルートをバイパス",
"routeMode_config": "設定を使用",
"routeAddress": "ルートアドレス",
"routeAddressDesc": "ルートアドレスを設定",
"pleaseInputAdminPassword": "管理者パスワードを入力",
"copyEnvVar": "環境変数をコピー",
"memoryInfo": "メモリ情報",
"cancel": "キャンセル",
"fileIsUpdate": "ファイルが変更されました。保存しますか?",
"profileHasUpdate": "プロファイルが変更されました。自動更新を無効化しますか?",
"hasCacheChange": "変更をキャッシュしますか?",
"nullProxies": "プロキシなし",
"copySuccess": "コピー成功",
"copyLink": "リンクをコピー",
"exportFile": "ファイルをエクスポート",
"cacheCorrupt": "キャッシュが破損しています。クリアしますか?",
"detectionTip": "サードパーティAPIに依存参考値",
"listen": "リスン",
"keyExists": "現在のキーは既に存在します",
"valueExists": "現在の値は既に存在します",
"undo": "元に戻す",
"redo": "やり直す",
"none": "なし",
"basicConfig": "基本設定",
"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": "トーンスポット",
"fidelityScheme": "ハイファイデリティー",
"monochromeScheme": "モノクローム",
"neutralScheme": "ニュートラル",
"vibrantScheme": "ビブラント",
"expressiveScheme": "エクスプレッシブ",
"contentScheme": "コンテンツテーマ",
"rainbowScheme": "レインボー",
"fruitSaladScheme": "フルーツサラダ"
}

View File

@@ -1,389 +0,0 @@
{
"rule": "Правило",
"global": "Глобальный",
"direct": "Прямой",
"dashboard": "Панель управления",
"proxies": "Прокси",
"profile": "Профиль",
"profiles": "Профили",
"tools": "Инструменты",
"logs": "Логи",
"logsDesc": "Записи захвата логов",
"resources": "Ресурсы",
"resourcesDesc": "Информация, связанная с внешними ресурсами",
"trafficUsage": "Использование трафика",
"coreInfo": "Информация о ядре",
"nullCoreInfoDesc": "Не удалось получить информацию о ядре",
"networkSpeed": "Скорость сети",
"outboundMode": "Режим исходящего трафика",
"networkDetection": "Обнаружение сети",
"upload": "Загрузка",
"download": "Скачивание",
"noProxy": "Нет прокси",
"noProxyDesc": "Пожалуйста, создайте профиль или добавьте действительный профиль",
"nullProfileDesc": "Нет профиля, пожалуйста, добавьте профиль",
"nullLogsDesc": "Нет логов",
"settings": "Настройки",
"language": "Язык",
"defaultText": "По умолчанию",
"more": "Еще",
"other": "Другое",
"about": "О программе",
"en": "Английский",
"ja": "Японский",
"ru": "Русский",
"zh_CN": "Упрощенный китайский",
"theme": "Тема",
"themeDesc": "Установить темный режим, настроить цвет",
"override": "Переопределить",
"overrideDesc": "Переопределить конфигурацию, связанную с прокси",
"allowLan": "Разрешить LAN",
"allowLanDesc": "Разрешить доступ к прокси через локальную сеть",
"tun": "TUN",
"tunDesc": "действительно только в режиме администратора",
"minimizeOnExit": "Свернуть при выходе",
"minimizeOnExitDesc": "Изменить стандартное событие выхода из системы",
"autoLaunch": "Автозапуск",
"autoLaunchDesc": "Следовать автозапуску системы",
"silentLaunch": "Тихий запуск",
"silentLaunchDesc": "Запуск в фоновом режиме",
"autoRun": "Автозапуск",
"autoRunDesc": "Автоматический запуск при открытии приложения",
"logcat": "Logcat",
"logcatDesc": "Отключение скроет запись логов",
"autoCheckUpdate": "Автопроверка обновлений",
"autoCheckUpdateDesc": "Автоматически проверять обновления при запуске приложения",
"accessControl": "Контроль доступа",
"accessControlDesc": "Настройка доступа приложений к прокси",
"application": "Приложение",
"applicationDesc": "Изменение настроек, связанных с приложением",
"edit": "Редактировать",
"confirm": "Подтвердить",
"update": "Обновить",
"add": "Добавить",
"save": "Сохранить",
"delete": "Удалить",
"years": "Лет",
"months": "Месяцев",
"hours": "Часов",
"days": "Дней",
"minutes": "Минут",
"seconds": "Секунд",
"ago": " назад",
"just": "Только что",
"qrcode": "QR-код",
"qrcodeDesc": "Сканируйте QR-код для получения профиля",
"url": "URL",
"urlDesc": "Получить профиль через URL",
"file": "Файл",
"fileDesc": "Прямая загрузка профиля",
"name": "Имя",
"profileNameNullValidationDesc": "Пожалуйста, введите имя профиля",
"profileUrlNullValidationDesc": "Пожалуйста, введите URL профиля",
"profileUrlInvalidValidationDesc": "Пожалуйста, введите действительный URL профиля",
"autoUpdate": "Автообновление",
"autoUpdateInterval": "Интервал автообновления (минуты)",
"profileAutoUpdateIntervalNullValidationDesc": "Пожалуйста, введите интервал времени для автообновления",
"profileAutoUpdateIntervalInvalidValidationDesc": "Пожалуйста, введите действительный формат интервала времени",
"themeMode": "Режим темы",
"themeColor": "Цвет темы",
"preview": "Предпросмотр",
"auto": "Авто",
"light": "Светлый",
"dark": "Темный",
"importFromURL": "Импорт из URL",
"submit": "Отправить",
"doYouWantToPass": "Вы хотите пропустить",
"create": "Создать",
"defaultSort": "Сортировка по умолчанию",
"delaySort": "Сортировка по задержке",
"nameSort": "Сортировка по имени",
"pleaseUploadFile": "Пожалуйста, загрузите файл",
"pleaseUploadValidQrcode": "Пожалуйста, загрузите действительный QR-код",
"blacklistMode": "Режим черного списка",
"whitelistMode": "Режим белого списка",
"filterSystemApp": "Фильтровать системные приложения",
"cancelFilterSystemApp": "Отменить фильтрацию системных приложений",
"selectAll": "Выбрать все",
"cancelSelectAll": "Отменить выбор всего",
"appAccessControl": "Контроль доступа приложений",
"accessControlAllowDesc": "Разрешить только выбранным приложениям доступ к VPN",
"accessControlNotAllowDesc": "Выбранные приложения будут исключены из VPN",
"selected": "Выбрано",
"unableToUpdateCurrentProfileDesc": "невозможно обновить текущий профиль",
"noMoreInfoDesc": "Нет дополнительной информации",
"profileParseErrorDesc": "ошибка разбора профиля",
"proxyPort": "Порт прокси",
"proxyPortDesc": "Установить порт прослушивания Clash",
"port": "Порт",
"logLevel": "Уровень логов",
"show": "Показать",
"exit": "Выход",
"systemProxy": "Системный прокси",
"project": "Проект",
"core": "Ядро",
"tabAnimation": "Анимация вкладок",
"desc": "Многоплатформенный прокси-клиент на основе ClashMeta, простой и удобный в использовании, с открытым исходным кодом и без рекламы.",
"startVpn": "Запуск VPN...",
"stopVpn": "Остановка VPN...",
"discovery": "Обнаружена новая версия",
"compatible": "Режим совместимости",
"compatibleDesc": "Включение приведет к потере части функциональности приложения, но обеспечит полную поддержку Clash.",
"notSelectedTip": "Текущая группа прокси не может быть выбрана.",
"tip": "подсказка",
"backupAndRecovery": "Резервное копирование и восстановление",
"backupAndRecoveryDesc": "Синхронизация данных через WebDAV или файл",
"account": "Аккаунт",
"backup": "Резервное копирование",
"recovery": "Восстановление",
"recoveryProfiles": "Только восстановление профилей",
"recoveryAll": "Восстановить все данные",
"recoverySuccess": "Восстановление успешно",
"backupSuccess": "Резервное копирование успешно",
"noInfo": "Нет информации",
"pleaseBindWebDAV": "Пожалуйста, привяжите WebDAV",
"bind": "Привязать",
"connectivity": "Связь:",
"webDAVConfiguration": "Конфигурация WebDAV",
"address": "Адрес",
"addressHelp": "Адрес сервера WebDAV",
"addressTip": "Пожалуйста, введите действительный адрес WebDAV",
"password": "Пароль",
"passwordTip": "Пароль не может быть пустым",
"accountTip": "Аккаунт не может быть пустым",
"checkUpdate": "Проверить обновления",
"discoverNewVersion": "Обнаружена новая версия",
"checkUpdateError": "Текущее приложение уже является последней версией",
"goDownload": "Перейти к загрузке",
"unknown": "Неизвестно",
"geoData": "Геоданные",
"externalResources": "Внешние ресурсы",
"checking": "Проверка...",
"country": "Страна",
"checkError": "Ошибка проверки",
"search": "Поиск",
"allowBypass": "Разрешить приложениям обходить VPN",
"allowBypassDesc": "Некоторые приложения могут обходить VPN при включении",
"externalController": "Внешний контроллер",
"externalControllerDesc": "При включении ядро Clash можно контролировать на порту 9090",
"ipv6Desc": "При включении будет возможно получать IPv6 трафик",
"app": "Приложение",
"general": "Общие",
"vpnSystemProxyDesc": "Прикрепить HTTP-прокси к VpnService",
"systemProxyDesc": "Прикрепить HTTP-прокси к VpnService",
"unifiedDelay": "Унифицированная задержка",
"unifiedDelayDesc": "Убрать дополнительные задержки, такие как рукопожатие",
"tcpConcurrent": "TCP параллелизм",
"tcpConcurrentDesc": "Включение позволит использовать параллелизм TCP",
"geodataLoader": "Режим низкого потребления памяти для геоданных",
"geodataLoaderDesc": "Включение будет использовать загрузчик геоданных с низким потреблением памяти",
"requests": "Запросы",
"requestsDesc": "Просмотр последних записей запросов",
"findProcessMode": "Режим поиска процесса",
"init": "Инициализация",
"infiniteTime": "Долгосрочное действие",
"expirationTime": "Время истечения",
"connections": "Соединения",
"connectionsDesc": "Просмотр текущих данных о соединениях",
"nullRequestsDesc": "Нет запросов",
"nullConnectionsDesc": "Нет соединений",
"intranetIP": "Внутренний IP",
"view": "Просмотр",
"cut": "Вырезать",
"copy": "Копировать",
"paste": "Вставить",
"testUrl": "Тест URL",
"sync": "Синхронизация",
"exclude": "Скрыть из последних задач",
"excludeDesc": "Когда приложение находится в фоновом режиме, оно скрыто из последних задач",
"oneColumn": "Один столбец",
"twoColumns": "Два столбца",
"threeColumns": "Три столбца",
"fourColumns": "Четыре столбца",
"expand": "Стандартный",
"shrink": "Сжать",
"min": "Мин",
"tab": "Вкладка",
"list": "Список",
"delay": "Задержка",
"style": "Стиль",
"size": "Размер",
"sort": "Сортировка",
"columns": "Столбцы",
"proxiesSetting": "Настройка прокси",
"proxyGroup": "Группа прокси",
"go": "Перейти",
"externalLink": "Внешняя ссылка",
"otherContributors": "Другие участники",
"autoCloseConnections": "Автоматическое закрытие соединений",
"autoCloseConnectionsDesc": "Автоматически закрывать соединения после смены узла",
"onlyStatisticsProxy": "Только статистика прокси",
"onlyStatisticsProxyDesc": "При включении будет учитываться только трафик прокси",
"deleteProfileTip": "Вы уверены, что хотите удалить текущий профиль?",
"pureBlackMode": "Чисто черный режим",
"keepAliveIntervalDesc": "Интервал поддержания TCP-соединения",
"entries": " записей",
"local": "Локальный",
"remote": "Удаленный",
"remoteBackupDesc": "Резервное копирование локальных данных на WebDAV",
"remoteRecoveryDesc": "Восстановление данных с WebDAV",
"localBackupDesc": "Резервное копирование локальных данных на локальный диск",
"localRecoveryDesc": "Восстановление данных из файла",
"mode": "Режим",
"time": "Время",
"source": "Источник",
"allApps": "Все приложения",
"onlyOtherApps": "Только сторонние приложения",
"action": "Действие",
"intelligentSelected": "Интеллектуальный выбор",
"clipboardImport": "Импорт из буфера обмена",
"clipboardExport": "Экспорт в буфер обмена",
"layout": "Макет",
"tight": "Плотный",
"standard": "Стандартный",
"loose": "Свободный",
"profilesSort": "Сортировка профилей",
"start": "Старт",
"stop": "Стоп",
"appDesc": "Обработка настроек, связанных с приложением",
"vpnDesc": "Изменение настроек, связанных с VPN",
"dnsDesc": "Обновление настроек, связанных с DNS",
"key": "Ключ",
"value": "Значение",
"notEmpty": "Не может быть пустым",
"hostsDesc": "Добавить Hosts",
"vpnTip": "Изменения вступят в силу после перезапуска VPN",
"vpnEnableDesc": "Автоматически направляет весь системный трафик через VpnService",
"options": "Опции",
"loopback": "Инструмент разблокировки Loopback",
"loopbackDesc": "Используется для разблокировки Loopback UWP",
"providers": "Провайдеры",
"proxyProviders": "Провайдеры прокси",
"ruleProviders": "Провайдеры правил",
"overrideDns": "Переопределить DNS",
"overrideDnsDesc": "Включение переопределит настройки DNS в профиле",
"status": "Статус",
"statusDesc": "Системный DNS будет использоваться при выключении",
"preferH3Desc": "Приоритетное использование HTTP/3 для DOH",
"respectRules": "Соблюдение правил",
"respectRulesDesc": "DNS-соединение следует правилам, необходимо настроить proxy-server-nameserver",
"dnsMode": "Режим DNS",
"fakeipRange": "Диапазон Fakeip",
"fakeipFilter": "Фильтр Fakeip",
"defaultNameserver": "Сервер имен по умолчанию",
"defaultNameserverDesc": "Для разрешения DNS-сервера",
"nameserver": "Сервер имен",
"nameserverDesc": "Для разрешения домена",
"useHosts": "Использовать hosts",
"useSystemHosts": "Использовать системные hosts",
"nameserverPolicy": "Политика сервера имен",
"nameserverPolicyDesc": "Указать соответствующую политику сервера имен",
"proxyNameserver": "Прокси-сервер имен",
"proxyNameserverDesc": "Домен для разрешения прокси-узлов",
"fallback": "Резервный",
"fallbackDesc": "Обычно используется оффшорный DNS",
"fallbackFilter": "Фильтр резервного DNS",
"geoipCode": "Код Geoip",
"ipcidr": "IPCIDR",
"domain": "Домен",
"reset": "Сброс",
"action_view": "Показать/Скрыть",
"action_start": "Старт/Стоп",
"action_mode": "Переключить режим",
"action_proxy": "Системный прокси",
"action_tun": "TUN",
"disclaimer": "Отказ от ответственности",
"disclaimerDesc": "Это программное обеспечение используется только в некоммерческих целях, таких как учебные обмены и научные исследования. Запрещено использовать это программное обеспечение в коммерческих целях. Любая коммерческая деятельность, если таковая имеется, не имеет отношения к этому программному обеспечению.",
"agree": "Согласен",
"hotkeyManagement": "Управление горячими клавишами",
"hotkeyManagementDesc": "Использование клавиатуры для управления приложением",
"pressKeyboard": "Пожалуйста, нажмите клавишу.",
"inputCorrectHotkey": "Пожалуйста, введите правильную горячую клавишу",
"hotkeyConflict": "Конфликт горячих клавиш",
"remove": "Удалить",
"noHotKey": "Нет горячей клавиши",
"noNetwork": "Нет сети",
"ipv6InboundDesc": "Разрешить входящий IPv6",
"exportLogs": "Экспорт логов",
"exportSuccess": "Экспорт успешен",
"iconStyle": "Стиль иконки",
"onlyIcon": "Только иконка",
"noIcon": "Нет иконки",
"stackMode": "Режим стека",
"network": "Сеть",
"networkDesc": "Изменение настроек, связанных с сетью",
"bypassDomain": "Обход домена",
"bypassDomainDesc": "Действует только при включенном системном прокси",
"resetTip": "Убедитесь, что хотите сбросить",
"regExp": "Регулярное выражение",
"icon": "Иконка",
"iconConfiguration": "Конфигурация иконки",
"noData": "Нет данных",
"adminAutoLaunch": "Автозапуск с правами администратора",
"adminAutoLaunchDesc": "Запуск с правами администратора при загрузке системы",
"fontFamily": "Семейство шрифтов",
"systemFont": "Системный шрифт",
"toggle": "Переключить",
"system": "Система",
"routeMode": "Режим маршрутизации",
"routeMode_bypassPrivate": "Обход частных адресов маршрутизации",
"routeMode_config": "Использовать конфигурацию",
"routeAddress": "Адрес маршрутизации",
"routeAddressDesc": "Настройка адреса прослушивания маршрутизации",
"pleaseInputAdminPassword": "Пожалуйста, введите пароль администратора",
"copyEnvVar": "Копирование переменных окружения",
"memoryInfo": "Информация о памяти",
"cancel": "Отмена",
"fileIsUpdate": "Файл был изменен. Хотите сохранить изменения?",
"profileHasUpdate": "Профиль был изменен. Хотите отключить автообновление?",
"hasCacheChange": "Хотите сохранить изменения в кэше?",
"nullProxies": "Нет прокси",
"copySuccess": "Копирование успешно",
"copyLink": "Копировать ссылку",
"exportFile": "Экспорт файла",
"cacheCorrupt": "Кэш поврежден. Хотите очистить его?",
"detectionTip": "Опирается на сторонний API, только для справки",
"listen": "Слушать",
"keyExists": "Текущий ключ уже существует",
"valueExists": "Текущее значение уже существует",
"undo": "Отменить",
"redo": "Повторить",
"none": "Нет",
"basicConfig": "Базовая конфигурация",
"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": "Тональный акцент",
"fidelityScheme": "Точная передача",
"monochromeScheme": "Монохром",
"neutralScheme": "Нейтральные",
"vibrantScheme": "Яркие",
"expressiveScheme": "Экспрессивные",
"contentScheme": "Контентная тема",
"rainbowScheme": "Радужные",
"fruitSaladScheme": "Фруктовый микс"
}

View File

@@ -1,389 +0,0 @@
{
"rule": "规则",
"global": "全局",
"direct": "直连",
"dashboard": "仪表盘",
"proxies": "代理",
"profile": "配置",
"profiles": "配置",
"tools": "工具",
"logs": "日志",
"logsDesc": "日志捕获记录",
"resources": "资源",
"resourcesDesc": "外部资源相关信息",
"trafficUsage": "流量统计",
"coreInfo": "内核信息",
"nullCoreInfoDesc": "无法获取内核信息",
"networkSpeed": "网络速度",
"outboundMode": "出站模式",
"networkDetection": "网络检测",
"upload": "上传",
"download": "下载",
"noProxy": "暂无代理",
"noProxyDesc": "请创建配置文件或者添加有效配置文件",
"nullProfileDesc": "没有配置文件,请先添加配置文件",
"nullLogsDesc": "暂无日志",
"settings": "设置",
"language": "语言",
"defaultText": "默认",
"more": "更多",
"other": "其他",
"about": "关于",
"en": "英语",
"ja": "日语",
"ru": "俄语",
"zh_CN": "中文简体",
"theme": "主题",
"themeDesc": "设置深色模式,调整色彩",
"override": "覆写",
"overrideDesc": "覆写代理相关配置",
"allowLan": "局域网代理",
"allowLanDesc": "允许通过局域网访问代理",
"tun": "虚拟网卡",
"tunDesc": "仅在管理员模式生效",
"minimizeOnExit": "退出时最小化",
"minimizeOnExitDesc": "修改系统默认退出事件",
"autoLaunch": "自启动",
"autoLaunchDesc": "跟随系统自启动",
"silentLaunch": "静默启动",
"silentLaunchDesc": "后台启动",
"autoRun": "自动运行",
"autoRunDesc": "应用打开时自动运行",
"logcat": "日志捕获",
"logcatDesc": "禁用将会隐藏日志入口",
"autoCheckUpdate": "自动检查更新",
"autoCheckUpdateDesc": "应用启动时自动检查更新",
"accessControl": "访问控制",
"accessControlDesc": "配置应用访问代理",
"application": "应用程序",
"applicationDesc": "修改应用程序相关设置",
"edit": "编辑",
"confirm": "确定",
"update": "更新",
"add": "添加",
"save": "保存",
"delete": "删除",
"years": "年",
"months": "月",
"hours": "小时",
"days": "天",
"minutes": "分钟",
"seconds": "秒",
"ago": "前",
"just": "刚刚",
"qrcode": "二维码",
"qrcodeDesc": "扫描二维码获取配置文件",
"url": "URL",
"urlDesc": "通过URL获取配置文件",
"file": "文件",
"fileDesc": "直接上传配置文件",
"name": "名称",
"profileNameNullValidationDesc": "请输入配置名称",
"profileUrlNullValidationDesc": "请输入配置URL",
"profileUrlInvalidValidationDesc": "请输入有效配置URL",
"autoUpdate": "自动更新",
"autoUpdateInterval": "自动更新间隔(分钟)",
"profileAutoUpdateIntervalNullValidationDesc": "请输入自动更新间隔时间",
"profileAutoUpdateIntervalInvalidValidationDesc": "请输入有效间隔时间格式",
"themeMode": "主题模式",
"themeColor": "主题色彩",
"preview": "预览",
"auto": "自动",
"light": "浅色",
"dark": "深色",
"importFromURL": "从URL导入",
"submit": "提交",
"doYouWantToPass": "是否要通过",
"create": "创建",
"defaultSort": "按默认排序",
"delaySort": "按延迟排序",
"nameSort": "按名称排序",
"pleaseUploadFile": "请上传文件",
"pleaseUploadValidQrcode": "请上传有效的二维码",
"blacklistMode": "黑名单模式",
"whitelistMode": "白名单模式",
"filterSystemApp": "过滤系统应用",
"cancelFilterSystemApp": "取消过滤系统应用",
"selectAll": "全选",
"cancelSelectAll": "取消全选",
"appAccessControl": "应用访问控制",
"accessControlAllowDesc": "只允许选中应用进入VPN",
"accessControlNotAllowDesc": "选中应用将会被排除在VPN之外",
"selected": "已选择",
"unableToUpdateCurrentProfileDesc": "无法更新当前配置文件",
"noMoreInfoDesc": "暂无更多信息",
"profileParseErrorDesc": "配置文件解析错误",
"proxyPort": "代理端口",
"proxyPortDesc": "设置Clash监听端口",
"port": "端口",
"logLevel": "日志等级",
"show": "显示",
"exit": "退出",
"systemProxy": "系统代理",
"project": "项目",
"core": "内核",
"tabAnimation": "选项卡动画",
"desc": "基于ClashMeta的多平台代理客户端简单易用开源无广告。",
"startVpn": "正在启动VPN...",
"stopVpn": "正在停止VPN...",
"discovery": "发现新版本",
"compatible": "兼容模式",
"compatibleDesc": "开启将失去部分应用能力获得全量的Clash的支持",
"notSelectedTip": "当前代理组无法选中",
"tip": "提示",
"backupAndRecovery": "备份与恢复",
"backupAndRecoveryDesc": "通过WebDAV或者文件同步数据",
"account": "账号",
"backup": "备份",
"recovery": "恢复",
"recoveryProfiles": "仅恢复配置文件",
"recoveryAll": "恢复所有数据",
"recoverySuccess": "恢复成功",
"backupSuccess": "备份成功",
"noInfo": "暂无信息",
"pleaseBindWebDAV": "请绑定WebDAV",
"bind": "绑定",
"connectivity": "连通性:",
"webDAVConfiguration": "WebDAV配置",
"address": "地址",
"addressHelp": "WebDAV服务器地址",
"addressTip": "请输入有效的WebDAV地址",
"password": "密码",
"passwordTip": "密码不能为空",
"accountTip": "账号不能为空",
"checkUpdate": "检查更新",
"discoverNewVersion": "发现新版本",
"checkUpdateError": "当前应用已经是最新版了",
"goDownload": "前往下载",
"unknown": "未知",
"geoData": "地理数据",
"externalResources": "外部资源",
"checking": "检测中...",
"country": "区域",
"checkError": "检测失败",
"search": "搜索",
"allowBypass": "允许应用绕过VPN",
"allowBypassDesc": "开启后部分应用可绕过VPN",
"externalController": "外部控制器",
"externalControllerDesc": "开启后将可以通过9090端口控制Clash内核",
"ipv6Desc": "开启后将可以接收IPv6流量",
"app": "应用",
"general": "常规",
"vpnSystemProxyDesc": "为VpnService附加HTTP代理",
"systemProxyDesc": "设置系统代理",
"unifiedDelay": "统一延迟",
"unifiedDelayDesc": "去除握手等额外延迟",
"tcpConcurrent": "TCP并发",
"tcpConcurrentDesc": "开启后允许TCP并发",
"geodataLoader": "Geo低内存模式",
"geodataLoaderDesc": "开启将使用Geo低内存加载器",
"requests": "请求",
"requestsDesc": "查看最近请求记录",
"findProcessMode": "查找进程",
"init": "初始化",
"infiniteTime": "长期有效",
"expirationTime": "到期时间",
"connections": "连接",
"connectionsDesc": "查看当前连接数据",
"nullRequestsDesc": "暂无请求",
"nullConnectionsDesc": "暂无连接",
"intranetIP": "内网 IP",
"view": "查看",
"cut": "剪切",
"copy": "复制",
"paste": "粘贴",
"testUrl": "测速链接",
"sync": "同步",
"exclude": "从最近任务中隐藏",
"excludeDesc": "应用在后台时,从最近任务中隐藏应用",
"oneColumn": "一列",
"twoColumns": "两列",
"threeColumns": "三列",
"fourColumns": "四列",
"expand": "标准",
"shrink": "紧凑",
"min": "最小",
"tab": "标签页",
"list": "列表",
"delay": "延迟",
"style": "风格",
"size": "尺寸",
"sort": "排序",
"columns": "列数",
"proxiesSetting": "代理设置",
"proxyGroup": "代理组",
"go": "前往",
"externalLink": "外部链接",
"otherContributors": "其他贡献者",
"autoCloseConnections": "自动关闭连接",
"autoCloseConnectionsDesc": "切换节点后自动关闭连接",
"onlyStatisticsProxy": "仅统计代理",
"onlyStatisticsProxyDesc": "开启后,将只统计代理流量",
"deleteProfileTip": "确定要删除当前配置吗?",
"pureBlackMode": "纯黑模式",
"keepAliveIntervalDesc": "TCP保持活动间隔",
"entries": "个条目",
"local": "本地",
"remote": "远程",
"remoteBackupDesc": "备份数据到WebDAV",
"remoteRecoveryDesc": "通过WebDAV恢复数据",
"localBackupDesc": "备份数据到本地",
"localRecoveryDesc": "通过文件恢复数据",
"mode": "模式",
"time": "时间",
"source": "来源",
"allApps": "所有应用",
"onlyOtherApps": "仅第三方应用",
"action": "操作",
"intelligentSelected": "智能选择",
"clipboardImport": "剪贴板导入",
"clipboardExport": "导出剪贴板",
"layout": "布局",
"tight": "紧凑",
"standard": "标准",
"loose": "宽松",
"profilesSort": "配置排序",
"start": "启动",
"stop": "暂停",
"appDesc": "处理应用相关设置",
"vpnDesc": "修改VPN相关设置",
"dnsDesc": "更新DNS相关设置",
"key": "键",
"value": "值",
"notEmpty": "不能为空",
"hostsDesc": "追加Hosts",
"vpnTip": "重启VPN后改变生效",
"vpnEnableDesc": "通过VpnService自动路由系统所有流量",
"options": "选项",
"loopback": "回环解锁工具",
"loopbackDesc": "用于UWP回环解锁",
"providers": "提供者",
"proxyProviders": "代理提供者",
"ruleProviders": "规则提供者",
"overrideDns": "覆写DNS",
"overrideDnsDesc": "开启后将覆盖配置中的DNS选项",
"status": "状态",
"statusDesc": "关闭后将使用系统DNS",
"preferH3Desc": "优先使用DOH的http/3",
"respectRules": "遵守规则",
"respectRulesDesc": "DNS连接跟随rules,需配置proxy-server-nameserver",
"dnsMode": "DNS模式",
"fakeipRange": "Fakeip范围",
"fakeipFilter": "Fakeip过滤",
"defaultNameserver": "默认域名服务器",
"defaultNameserverDesc": "用于解析DNS服务器",
"nameserver": "域名服务器",
"nameserverDesc": "用于解析域名",
"useHosts": "使用Hosts",
"useSystemHosts": "使用系统Hosts",
"nameserverPolicy": "域名服务器策略",
"nameserverPolicyDesc": "指定对应域名服务器策略",
"proxyNameserver": "代理域名服务器",
"proxyNameserverDesc": "用于解析代理节点的域名",
"fallback": "Fallback",
"fallbackDesc": "一般情况下使用境外DNS",
"fallbackFilter": "Fallback过滤",
"geoipCode": "Geoip代码",
"ipcidr": "IP/掩码",
"domain": "域名",
"reset": "重置",
"action_view": "显示/隐藏",
"action_start": "启动/停止",
"action_mode": "切换模式",
"action_proxy": "系统代理",
"action_tun": "虚拟网卡",
"disclaimer": "免责声明",
"disclaimerDesc": "本软件仅供学习交流、科研等非商业性质的用途,严禁将本软件用于商业目的。如有任何商业行为,均与本软件无关。",
"agree": "同意",
"hotkeyManagement": "快捷键管理",
"hotkeyManagementDesc": "使用键盘控制应用程序",
"pressKeyboard": "请按下按键",
"inputCorrectHotkey": "请输入正确的快捷键",
"hotkeyConflict": "快捷键冲突",
"remove": "移除",
"noHotKey": "暂无快捷键",
"noNetwork": "无网络",
"ipv6InboundDesc": "允许IPv6入站",
"exportLogs": "导出日志",
"exportSuccess": "导出成功",
"iconStyle": "图标样式",
"onlyIcon": "仅图标",
"noIcon": "无图标",
"stackMode": "栈模式",
"network": "网络",
"networkDesc": "修改网络相关设置",
"bypassDomain": "排除域名",
"bypassDomainDesc": "仅在系统代理启用时生效",
"resetTip": "确定要重置吗?",
"regExp": "正则",
"icon": "图片",
"iconConfiguration": "图片配置",
"noData": "暂无数据",
"adminAutoLaunch": "管理员自启动",
"adminAutoLaunchDesc": "使用管理员模式开机自启动",
"fontFamily": "字体",
"systemFont": "系统字体",
"toggle": "切换",
"system": "系统",
"routeMode": "路由模式",
"routeMode_bypassPrivate": "绕过私有路由地址",
"routeMode_config": "使用配置",
"routeAddress": "路由地址",
"routeAddressDesc": "配置监听路由地址",
"pleaseInputAdminPassword": "请输入管理员密码",
"copyEnvVar": "复制环境变量",
"memoryInfo": "内存信息",
"cancel": "取消",
"fileIsUpdate": "文件有修改,是否保存修改",
"profileHasUpdate": "配置文件已经修改,是否关闭自动更新 ",
"hasCacheChange": "是否缓存修改",
"nullProxies": "暂无代理",
"copySuccess": "复制成功",
"copyLink": "复制链接",
"exportFile": "导出文件",
"cacheCorrupt": "缓存已损坏,是否清空?",
"detectionTip": "依赖第三方api仅供参考",
"listen": "监听",
"keyExists": "当前键已存在",
"valueExists": "当前值已存在",
"undo": "撤销",
"redo": "重做",
"none": "无",
"basicConfig": "基本配置",
"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": "调性点缀",
"fidelityScheme": "高保真",
"monochromeScheme": "单色",
"neutralScheme": "中性",
"vibrantScheme": "活力",
"expressiveScheme": "表现力",
"contentScheme": "内容主题",
"rainbowScheme": "彩虹",
"fruitSaladScheme": "果缤纷"
}

View File

@@ -146,6 +146,7 @@ class MessageLookup extends MessageLookupByLibrary {
"The current application is already the latest version",
),
"checking": MessageLookupByLibrary.simpleMessage("Checking..."),
"clearData": MessageLookupByLibrary.simpleMessage("Clear Data"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("Export clipboard"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("Clipboard import"),
"colorExists": MessageLookupByLibrary.simpleMessage(
@@ -163,6 +164,7 @@ class MessageLookup extends MessageLookupByLibrary {
"View current connections data",
),
"connectivity": MessageLookupByLibrary.simpleMessage("Connectivity"),
"contactMe": MessageLookupByLibrary.simpleMessage("Contact me"),
"content": MessageLookupByLibrary.simpleMessage("Content"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
"Content cannot be empty",
@@ -177,6 +179,7 @@ class MessageLookup extends MessageLookupByLibrary {
"core": MessageLookupByLibrary.simpleMessage("Core"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Core info"),
"country": MessageLookupByLibrary.simpleMessage("Country"),
"crashTest": MessageLookupByLibrary.simpleMessage("Crash test"),
"create": MessageLookupByLibrary.simpleMessage("Create"),
"cut": MessageLookupByLibrary.simpleMessage("Cut"),
"dark": MessageLookupByLibrary.simpleMessage("Dark"),
@@ -208,6 +211,10 @@ class MessageLookup extends MessageLookupByLibrary {
"detectionTip": MessageLookupByLibrary.simpleMessage(
"Relying on third-party api is for reference only",
),
"developerMode": MessageLookupByLibrary.simpleMessage("Developer mode"),
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
"Developer mode is enabled.",
),
"direct": MessageLookupByLibrary.simpleMessage("Direct"),
"disclaimer": MessageLookupByLibrary.simpleMessage("Disclaimer"),
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
@@ -320,6 +327,7 @@ class MessageLookup extends MessageLookupByLibrary {
"intelligentSelected": MessageLookupByLibrary.simpleMessage(
"Intelligent selection",
),
"internet": MessageLookupByLibrary.simpleMessage("Internet"),
"intranetIP": MessageLookupByLibrary.simpleMessage("Intranet IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("Ipcidr"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
@@ -356,12 +364,17 @@ class MessageLookup extends MessageLookupByLibrary {
),
"logs": MessageLookupByLibrary.simpleMessage("Logs"),
"logsDesc": MessageLookupByLibrary.simpleMessage("Log capture records"),
"logsTest": MessageLookupByLibrary.simpleMessage("Logs test"),
"loopback": MessageLookupByLibrary.simpleMessage("Loopback unlock tool"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage(
"Used for UWP loopback unlocking",
),
"loose": MessageLookupByLibrary.simpleMessage("Loose"),
"memoryInfo": MessageLookupByLibrary.simpleMessage("Memory info"),
"messageTest": MessageLookupByLibrary.simpleMessage("Message test"),
"messageTestTip": MessageLookupByLibrary.simpleMessage(
"This is a message.",
),
"min": MessageLookupByLibrary.simpleMessage("Min"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("Minimize on exit"),
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
@@ -399,6 +412,7 @@ class MessageLookup extends MessageLookupByLibrary {
"noInfo": MessageLookupByLibrary.simpleMessage("No info"),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("No more info"),
"noNetwork": MessageLookupByLibrary.simpleMessage("No network"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("No network APP"),
"noProxy": MessageLookupByLibrary.simpleMessage("No proxy"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"Please create a profile or add a valid profile",
@@ -526,6 +540,15 @@ class MessageLookup extends MessageLookupByLibrary {
"recoveryProfiles": MessageLookupByLibrary.simpleMessage(
"Only recovery profiles",
),
"recoveryStrategy": MessageLookupByLibrary.simpleMessage(
"Recovery strategy",
),
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage(
"Compatible",
),
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage(
"Override",
),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("Recovery success"),
"redo": MessageLookupByLibrary.simpleMessage("redo"),
"regExp": MessageLookupByLibrary.simpleMessage("RegExp"),
@@ -612,6 +635,7 @@ class MessageLookup extends MessageLookupByLibrary {
"submit": MessageLookupByLibrary.simpleMessage("Submit"),
"sync": MessageLookupByLibrary.simpleMessage("Sync"),
"system": MessageLookupByLibrary.simpleMessage("System"),
"systemApp": MessageLookupByLibrary.simpleMessage("System APP"),
"systemFont": MessageLookupByLibrary.simpleMessage("System font"),
"systemProxy": MessageLookupByLibrary.simpleMessage("System proxy"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
@@ -627,6 +651,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Enabling it will allow TCP concurrency",
),
"testUrl": MessageLookupByLibrary.simpleMessage("Test url"),
"textScale": MessageLookupByLibrary.simpleMessage("Text Scaling"),
"theme": MessageLookupByLibrary.simpleMessage("Theme"),
"themeColor": MessageLookupByLibrary.simpleMessage("Theme color"),
"themeDesc": MessageLookupByLibrary.simpleMessage(

View File

@@ -104,6 +104,7 @@ class MessageLookup extends MessageLookupByLibrary {
"checkUpdate": MessageLookupByLibrary.simpleMessage("更新を確認"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("アプリは最新版です"),
"checking": MessageLookupByLibrary.simpleMessage("確認中..."),
"clearData": MessageLookupByLibrary.simpleMessage("データを消去"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("クリップボードにエクスポート"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("クリップボードからインポート"),
"colorExists": MessageLookupByLibrary.simpleMessage("この色は既に存在します"),
@@ -117,6 +118,7 @@ class MessageLookup extends MessageLookupByLibrary {
"connections": MessageLookupByLibrary.simpleMessage("接続"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("現在の接続データを表示"),
"connectivity": MessageLookupByLibrary.simpleMessage("接続性:"),
"contactMe": MessageLookupByLibrary.simpleMessage("連絡する"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容は必須です"),
"contentScheme": MessageLookupByLibrary.simpleMessage("コンテンツテーマ"),
@@ -127,6 +129,7 @@ class MessageLookup extends MessageLookupByLibrary {
"core": MessageLookupByLibrary.simpleMessage("コア"),
"coreInfo": MessageLookupByLibrary.simpleMessage("コア情報"),
"country": MessageLookupByLibrary.simpleMessage(""),
"crashTest": MessageLookupByLibrary.simpleMessage("クラッシュテスト"),
"create": MessageLookupByLibrary.simpleMessage("作成"),
"cut": MessageLookupByLibrary.simpleMessage("切り取り"),
"dark": MessageLookupByLibrary.simpleMessage("ダーク"),
@@ -150,6 +153,10 @@ class MessageLookup extends MessageLookupByLibrary {
"ClashMetaベースのマルチプラットフォームプロキシクライアント。シンプルで使いやすく、オープンソースで広告なし。",
),
"detectionTip": MessageLookupByLibrary.simpleMessage("サードパーティAPIに依存参考値"),
"developerMode": MessageLookupByLibrary.simpleMessage("デベロッパーモード"),
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
"デベロッパーモードが有効になりました。",
),
"direct": MessageLookupByLibrary.simpleMessage("ダイレクト"),
"disclaimer": MessageLookupByLibrary.simpleMessage("免責事項"),
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
@@ -230,6 +237,7 @@ class MessageLookup extends MessageLookupByLibrary {
"init": MessageLookupByLibrary.simpleMessage("初期化"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("正しいホットキーを入力"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("インテリジェント選択"),
"internet": MessageLookupByLibrary.simpleMessage("インターネット"),
"intranetIP": MessageLookupByLibrary.simpleMessage("イントラネットIP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("有効化するとIPv6トラフィックを受信可能"),
@@ -254,10 +262,13 @@ class MessageLookup extends MessageLookupByLibrary {
"logcatDesc": MessageLookupByLibrary.simpleMessage("無効化するとログエントリを非表示"),
"logs": MessageLookupByLibrary.simpleMessage("ログ"),
"logsDesc": MessageLookupByLibrary.simpleMessage("ログキャプチャ記録"),
"logsTest": MessageLookupByLibrary.simpleMessage("ログテスト"),
"loopback": MessageLookupByLibrary.simpleMessage("ループバック解除ツール"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage("UWPループバック解除用"),
"loose": MessageLookupByLibrary.simpleMessage(""),
"memoryInfo": MessageLookupByLibrary.simpleMessage("メモリ情報"),
"messageTest": MessageLookupByLibrary.simpleMessage("メッセージテスト"),
"messageTestTip": MessageLookupByLibrary.simpleMessage("これはメッセージです。"),
"min": MessageLookupByLibrary.simpleMessage("最小化"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("終了時に最小化"),
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage(
@@ -287,6 +298,7 @@ class MessageLookup extends MessageLookupByLibrary {
"noInfo": MessageLookupByLibrary.simpleMessage("情報なし"),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("追加情報なし"),
"noNetwork": MessageLookupByLibrary.simpleMessage("ネットワークなし"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("ネットワークなしアプリ"),
"noProxy": MessageLookupByLibrary.simpleMessage("プロキシなし"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"プロファイルを作成するか、有効なプロファイルを追加してください",
@@ -384,6 +396,11 @@ class MessageLookup extends MessageLookupByLibrary {
"recovery": MessageLookupByLibrary.simpleMessage("復元"),
"recoveryAll": MessageLookupByLibrary.simpleMessage("全データ復元"),
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("プロファイルのみ復元"),
"recoveryStrategy": MessageLookupByLibrary.simpleMessage("リカバリー戦略"),
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage("互換性"),
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage(
"オーバーライド",
),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("復元成功"),
"redo": MessageLookupByLibrary.simpleMessage("やり直す"),
"regExp": MessageLookupByLibrary.simpleMessage("正規表現"),
@@ -452,6 +469,7 @@ class MessageLookup extends MessageLookupByLibrary {
"submit": MessageLookupByLibrary.simpleMessage("送信"),
"sync": MessageLookupByLibrary.simpleMessage("同期"),
"system": MessageLookupByLibrary.simpleMessage("システム"),
"systemApp": MessageLookupByLibrary.simpleMessage("システムアプリ"),
"systemFont": MessageLookupByLibrary.simpleMessage("システムフォント"),
"systemProxy": MessageLookupByLibrary.simpleMessage("システムプロキシ"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
@@ -463,6 +481,7 @@ class MessageLookup extends MessageLookupByLibrary {
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP並列処理"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("TCP並列処理を許可"),
"testUrl": MessageLookupByLibrary.simpleMessage("URLテスト"),
"textScale": MessageLookupByLibrary.simpleMessage("テキストスケーリング"),
"theme": MessageLookupByLibrary.simpleMessage("テーマ"),
"themeColor": MessageLookupByLibrary.simpleMessage("テーマカラー"),
"themeDesc": MessageLookupByLibrary.simpleMessage("ダークモードの設定、色の調整"),

View File

@@ -148,6 +148,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Текущее приложение уже является последней версией",
),
"checking": MessageLookupByLibrary.simpleMessage("Проверка..."),
"clearData": MessageLookupByLibrary.simpleMessage("Очистить данные"),
"clipboardExport": MessageLookupByLibrary.simpleMessage(
"Экспорт в буфер обмена",
),
@@ -169,6 +170,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Просмотр текущих данных о соединениях",
),
"connectivity": MessageLookupByLibrary.simpleMessage("Связь:"),
"contactMe": MessageLookupByLibrary.simpleMessage("Свяжитесь со мной"),
"content": MessageLookupByLibrary.simpleMessage("Содержание"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage(
"Содержание не может быть пустым",
@@ -183,6 +185,7 @@ class MessageLookup extends MessageLookupByLibrary {
"core": MessageLookupByLibrary.simpleMessage("Ядро"),
"coreInfo": MessageLookupByLibrary.simpleMessage("Информация о ядре"),
"country": MessageLookupByLibrary.simpleMessage("Страна"),
"crashTest": MessageLookupByLibrary.simpleMessage("Тест на сбои"),
"create": MessageLookupByLibrary.simpleMessage("Создать"),
"cut": MessageLookupByLibrary.simpleMessage("Вырезать"),
"dark": MessageLookupByLibrary.simpleMessage("Темный"),
@@ -216,6 +219,10 @@ class MessageLookup extends MessageLookupByLibrary {
"detectionTip": MessageLookupByLibrary.simpleMessage(
"Опирается на сторонний API, только для справки",
),
"developerMode": MessageLookupByLibrary.simpleMessage("Режим разработчика"),
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage(
"Режим разработчика активирован.",
),
"direct": MessageLookupByLibrary.simpleMessage("Прямой"),
"disclaimer": MessageLookupByLibrary.simpleMessage(
"Отказ от ответственности",
@@ -342,6 +349,7 @@ class MessageLookup extends MessageLookupByLibrary {
"intelligentSelected": MessageLookupByLibrary.simpleMessage(
"Интеллектуальный выбор",
),
"internet": MessageLookupByLibrary.simpleMessage("Интернет"),
"intranetIP": MessageLookupByLibrary.simpleMessage("Внутренний IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IPCIDR"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage(
@@ -378,6 +386,7 @@ class MessageLookup extends MessageLookupByLibrary {
),
"logs": MessageLookupByLibrary.simpleMessage("Логи"),
"logsDesc": MessageLookupByLibrary.simpleMessage("Записи захвата логов"),
"logsTest": MessageLookupByLibrary.simpleMessage("Тест журналов"),
"loopback": MessageLookupByLibrary.simpleMessage(
"Инструмент разблокировки Loopback",
),
@@ -386,6 +395,10 @@ class MessageLookup extends MessageLookupByLibrary {
),
"loose": MessageLookupByLibrary.simpleMessage("Свободный"),
"memoryInfo": MessageLookupByLibrary.simpleMessage("Информация о памяти"),
"messageTest": MessageLookupByLibrary.simpleMessage(
"Тестирование сообщения",
),
"messageTestTip": MessageLookupByLibrary.simpleMessage("Это сообщение."),
"min": MessageLookupByLibrary.simpleMessage("Мин"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage(
"Свернуть при выходе",
@@ -427,6 +440,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Нет дополнительной информации",
),
"noNetwork": MessageLookupByLibrary.simpleMessage("Нет сети"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("Приложение без сети"),
"noProxy": MessageLookupByLibrary.simpleMessage("Нет прокси"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage(
"Пожалуйста, создайте профиль или добавьте действительный профиль",
@@ -560,6 +574,15 @@ class MessageLookup extends MessageLookupByLibrary {
"recoveryProfiles": MessageLookupByLibrary.simpleMessage(
"Только восстановление профилей",
),
"recoveryStrategy": MessageLookupByLibrary.simpleMessage(
"Стратегия восстановления",
),
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage(
"Совместимый",
),
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage(
"Переопределение",
),
"recoverySuccess": MessageLookupByLibrary.simpleMessage(
"Восстановление успешно",
),
@@ -650,6 +673,7 @@ class MessageLookup extends MessageLookupByLibrary {
"submit": MessageLookupByLibrary.simpleMessage("Отправить"),
"sync": MessageLookupByLibrary.simpleMessage("Синхронизация"),
"system": MessageLookupByLibrary.simpleMessage("Система"),
"systemApp": MessageLookupByLibrary.simpleMessage("Системное приложение"),
"systemFont": MessageLookupByLibrary.simpleMessage("Системный шрифт"),
"systemProxy": MessageLookupByLibrary.simpleMessage("Системный прокси"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage(
@@ -665,6 +689,7 @@ class MessageLookup extends MessageLookupByLibrary {
"Включение позволит использовать параллелизм TCP",
),
"testUrl": MessageLookupByLibrary.simpleMessage("Тест URL"),
"textScale": MessageLookupByLibrary.simpleMessage("Масштабирование текста"),
"theme": MessageLookupByLibrary.simpleMessage("Тема"),
"themeColor": MessageLookupByLibrary.simpleMessage("Цвет темы"),
"themeDesc": MessageLookupByLibrary.simpleMessage(

View File

@@ -94,6 +94,7 @@ class MessageLookup extends MessageLookupByLibrary {
"checkUpdate": MessageLookupByLibrary.simpleMessage("检查更新"),
"checkUpdateError": MessageLookupByLibrary.simpleMessage("当前应用已经是最新版了"),
"checking": MessageLookupByLibrary.simpleMessage("检测中..."),
"clearData": MessageLookupByLibrary.simpleMessage("清除数据"),
"clipboardExport": MessageLookupByLibrary.simpleMessage("导出剪贴板"),
"clipboardImport": MessageLookupByLibrary.simpleMessage("剪贴板导入"),
"colorExists": MessageLookupByLibrary.simpleMessage("该颜色已存在"),
@@ -107,6 +108,7 @@ class MessageLookup extends MessageLookupByLibrary {
"connections": MessageLookupByLibrary.simpleMessage("连接"),
"connectionsDesc": MessageLookupByLibrary.simpleMessage("查看当前连接数据"),
"connectivity": MessageLookupByLibrary.simpleMessage("连通性:"),
"contactMe": MessageLookupByLibrary.simpleMessage("联系我"),
"content": MessageLookupByLibrary.simpleMessage("内容"),
"contentEmptyTip": MessageLookupByLibrary.simpleMessage("内容不能为空"),
"contentScheme": MessageLookupByLibrary.simpleMessage("内容主题"),
@@ -117,6 +119,7 @@ class MessageLookup extends MessageLookupByLibrary {
"core": MessageLookupByLibrary.simpleMessage("内核"),
"coreInfo": MessageLookupByLibrary.simpleMessage("内核信息"),
"country": MessageLookupByLibrary.simpleMessage("区域"),
"crashTest": MessageLookupByLibrary.simpleMessage("崩溃测试"),
"create": MessageLookupByLibrary.simpleMessage("创建"),
"cut": MessageLookupByLibrary.simpleMessage("剪切"),
"dark": MessageLookupByLibrary.simpleMessage("深色"),
@@ -136,6 +139,8 @@ class MessageLookup extends MessageLookupByLibrary {
"基于ClashMeta的多平台代理客户端简单易用开源无广告。",
),
"detectionTip": MessageLookupByLibrary.simpleMessage("依赖第三方api仅供参考"),
"developerMode": MessageLookupByLibrary.simpleMessage("开发者模式"),
"developerModeEnableTip": MessageLookupByLibrary.simpleMessage("开发者模式已启用。"),
"direct": MessageLookupByLibrary.simpleMessage("直连"),
"disclaimer": MessageLookupByLibrary.simpleMessage("免责声明"),
"disclaimerDesc": MessageLookupByLibrary.simpleMessage(
@@ -206,6 +211,7 @@ class MessageLookup extends MessageLookupByLibrary {
"init": MessageLookupByLibrary.simpleMessage("初始化"),
"inputCorrectHotkey": MessageLookupByLibrary.simpleMessage("请输入正确的快捷键"),
"intelligentSelected": MessageLookupByLibrary.simpleMessage("智能选择"),
"internet": MessageLookupByLibrary.simpleMessage("互联网"),
"intranetIP": MessageLookupByLibrary.simpleMessage("内网 IP"),
"ipcidr": MessageLookupByLibrary.simpleMessage("IP/掩码"),
"ipv6Desc": MessageLookupByLibrary.simpleMessage("开启后将可以接收IPv6流量"),
@@ -228,10 +234,13 @@ class MessageLookup extends MessageLookupByLibrary {
"logcatDesc": MessageLookupByLibrary.simpleMessage("禁用将会隐藏日志入口"),
"logs": MessageLookupByLibrary.simpleMessage("日志"),
"logsDesc": MessageLookupByLibrary.simpleMessage("日志捕获记录"),
"logsTest": MessageLookupByLibrary.simpleMessage("日志测试"),
"loopback": MessageLookupByLibrary.simpleMessage("回环解锁工具"),
"loopbackDesc": MessageLookupByLibrary.simpleMessage("用于UWP回环解锁"),
"loose": MessageLookupByLibrary.simpleMessage("宽松"),
"memoryInfo": MessageLookupByLibrary.simpleMessage("内存信息"),
"messageTest": MessageLookupByLibrary.simpleMessage("消息测试"),
"messageTestTip": MessageLookupByLibrary.simpleMessage("这是一条消息。"),
"min": MessageLookupByLibrary.simpleMessage("最小"),
"minimizeOnExit": MessageLookupByLibrary.simpleMessage("退出时最小化"),
"minimizeOnExitDesc": MessageLookupByLibrary.simpleMessage("修改系统默认退出事件"),
@@ -257,6 +266,7 @@ class MessageLookup extends MessageLookupByLibrary {
"noInfo": MessageLookupByLibrary.simpleMessage("暂无信息"),
"noMoreInfoDesc": MessageLookupByLibrary.simpleMessage("暂无更多信息"),
"noNetwork": MessageLookupByLibrary.simpleMessage("无网络"),
"noNetworkApp": MessageLookupByLibrary.simpleMessage("无网络应用"),
"noProxy": MessageLookupByLibrary.simpleMessage("暂无代理"),
"noProxyDesc": MessageLookupByLibrary.simpleMessage("请创建配置文件或者添加有效配置文件"),
"noResolve": MessageLookupByLibrary.simpleMessage("不解析IP"),
@@ -338,6 +348,9 @@ class MessageLookup extends MessageLookupByLibrary {
"recovery": MessageLookupByLibrary.simpleMessage("恢复"),
"recoveryAll": MessageLookupByLibrary.simpleMessage("恢复所有数据"),
"recoveryProfiles": MessageLookupByLibrary.simpleMessage("仅恢复配置文件"),
"recoveryStrategy": MessageLookupByLibrary.simpleMessage("恢复策略"),
"recoveryStrategy_compatible": MessageLookupByLibrary.simpleMessage("兼容"),
"recoveryStrategy_override": MessageLookupByLibrary.simpleMessage("覆盖"),
"recoverySuccess": MessageLookupByLibrary.simpleMessage("恢复成功"),
"redo": MessageLookupByLibrary.simpleMessage("重做"),
"regExp": MessageLookupByLibrary.simpleMessage("正则"),
@@ -398,6 +411,7 @@ class MessageLookup extends MessageLookupByLibrary {
"submit": MessageLookupByLibrary.simpleMessage("提交"),
"sync": MessageLookupByLibrary.simpleMessage("同步"),
"system": MessageLookupByLibrary.simpleMessage("系统"),
"systemApp": MessageLookupByLibrary.simpleMessage("系统应用"),
"systemFont": MessageLookupByLibrary.simpleMessage("系统字体"),
"systemProxy": MessageLookupByLibrary.simpleMessage("系统代理"),
"systemProxyDesc": MessageLookupByLibrary.simpleMessage("设置系统代理"),
@@ -407,6 +421,7 @@ class MessageLookup extends MessageLookupByLibrary {
"tcpConcurrent": MessageLookupByLibrary.simpleMessage("TCP并发"),
"tcpConcurrentDesc": MessageLookupByLibrary.simpleMessage("开启后允许TCP并发"),
"testUrl": MessageLookupByLibrary.simpleMessage("测速链接"),
"textScale": MessageLookupByLibrary.simpleMessage("文本缩放"),
"theme": MessageLookupByLibrary.simpleMessage("主题"),
"themeColor": MessageLookupByLibrary.simpleMessage("主题色彩"),
"themeDesc": MessageLookupByLibrary.simpleMessage("设置深色模式,调整色彩"),

View File

@@ -3004,6 +3004,121 @@ class AppLocalizations {
args: [],
);
}
/// `Developer mode`
String get developerMode {
return Intl.message(
'Developer mode',
name: 'developerMode',
desc: '',
args: [],
);
}
/// `Developer mode is enabled.`
String get developerModeEnableTip {
return Intl.message(
'Developer mode is enabled.',
name: 'developerModeEnableTip',
desc: '',
args: [],
);
}
/// `Message test`
String get messageTest {
return Intl.message(
'Message test',
name: 'messageTest',
desc: '',
args: [],
);
}
/// `This is a message.`
String get messageTestTip {
return Intl.message(
'This is a message.',
name: 'messageTestTip',
desc: '',
args: [],
);
}
/// `Crash test`
String get crashTest {
return Intl.message('Crash test', name: 'crashTest', desc: '', args: []);
}
/// `Clear Data`
String get clearData {
return Intl.message('Clear Data', name: 'clearData', desc: '', args: []);
}
/// `Text Scaling`
String get textScale {
return Intl.message('Text Scaling', name: 'textScale', desc: '', args: []);
}
/// `Internet`
String get internet {
return Intl.message('Internet', name: 'internet', desc: '', args: []);
}
/// `System APP`
String get systemApp {
return Intl.message('System APP', name: 'systemApp', desc: '', args: []);
}
/// `No network APP`
String get noNetworkApp {
return Intl.message(
'No network APP',
name: 'noNetworkApp',
desc: '',
args: [],
);
}
/// `Contact me`
String get contactMe {
return Intl.message('Contact me', name: 'contactMe', desc: '', args: []);
}
/// `Recovery strategy`
String get recoveryStrategy {
return Intl.message(
'Recovery strategy',
name: 'recoveryStrategy',
desc: '',
args: [],
);
}
/// `Override`
String get recoveryStrategy_override {
return Intl.message(
'Override',
name: 'recoveryStrategy_override',
desc: '',
args: [],
);
}
/// `Compatible`
String get recoveryStrategy_compatible {
return Intl.message(
'Compatible',
name: 'recoveryStrategy_compatible',
desc: '',
args: [],
);
}
/// `Logs test`
String get logsTest {
return Intl.message('Logs test', name: 'logsTest', desc: '', args: []);
}
}
class AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {

View File

@@ -21,12 +21,16 @@ import 'common/common.dart';
Future<void> main() async {
globalState.isService = false;
WidgetsFlutterBinding.ensureInitialized();
FlutterError.onError = (details) {
commonPrint.log(details.stack.toString());
};
final version = await system.version;
await clashCore.preload();
await globalState.initApp(version);
await android?.init();
await window?.init(version);
globalState.isPre = const String.fromEnvironment("APP_ENV") != 'stable';
globalState.coreSHA256 = const String.fromEnvironment("CORE_SHA256");
HttpOverrides.global = FlClashHttpOverrides();
runApp(ProviderScope(
child: const Application(),

View File

@@ -1,11 +1,13 @@
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/providers/providers.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AppStateManager extends StatefulWidget {
class AppStateManager extends ConsumerStatefulWidget {
final Widget child;
const AppStateManager({
@@ -14,15 +16,22 @@ class AppStateManager extends StatefulWidget {
});
@override
State<AppStateManager> createState() => _AppStateManagerState();
ConsumerState<AppStateManager> createState() => _AppStateManagerState();
}
class _AppStateManagerState extends State<AppStateManager>
class _AppStateManagerState extends ConsumerState<AppStateManager>
with WidgetsBindingObserver {
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
ref.listenManual(layoutChangeProvider, (prev, next) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (prev != next) {
globalState.cacheHeightMap = {};
}
});
});
}
@override

View File

@@ -71,22 +71,22 @@ class _ClashContainerState extends ConsumerState<ClashManager>
@override
void onLog(Log log) {
ref.watch(logsProvider.notifier).addLog(log);
ref.read(logsProvider.notifier).addLog(log);
if (log.logLevel == LogLevel.error) {
globalState.showNotifier(log.payload ?? '');
globalState.showNotifier(log.payload);
}
super.onLog(log);
}
@override
void onRequest(Connection connection) async {
ref.watch(requestsProvider.notifier).addRequest(connection);
ref.read(requestsProvider.notifier).addRequest(connection);
super.onRequest(connection);
}
@override
Future<void> onLoaded(String providerName) async {
ref.watch(providersProvider.notifier).setProvider(
ref.read(providersProvider.notifier).setProvider(
await clashCore.getExternalProvider(
providerName,
),

View File

@@ -21,7 +21,7 @@ class MessageManager extends StatefulWidget {
class MessageManagerState extends State<MessageManager> {
final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);
final List<CommonMessage> _bufferMessages = [];
Completer<bool>? _messageIngCompleter;
bool _pushing = false;
@override
void initState() {
@@ -40,26 +40,27 @@ class MessageManagerState extends State<MessageManager> {
text: text,
);
_bufferMessages.add(commonMessage);
_showMessage();
await _showMessage();
}
_showMessage() async {
if (_messageIngCompleter?.isCompleted == false) {
if (_pushing == true) {
return;
}
_pushing = true;
while (_bufferMessages.isNotEmpty) {
final commonMessage = _bufferMessages.removeAt(0);
_messagesNotifier.value = List.from(_messagesNotifier.value)
..add(
commonMessage,
);
_messageIngCompleter = Completer();
await Future.delayed(Duration(seconds: 1));
Future.delayed(commonMessage.duration, () {
_handleRemove(commonMessage);
});
_messageIngCompleter?.complete(true);
if (_bufferMessages.isEmpty) {
_pushing = false;
}
}
}
@@ -77,41 +78,41 @@ class MessageManagerState extends State<MessageManager> {
valueListenable: _messagesNotifier,
builder: (_, messages, __) {
return FadeThroughBox(
margin: EdgeInsets.only(
top: kToolbarHeight + 8,
left: 12,
right: 12,
),
alignment: Alignment.topRight,
child: messages.isEmpty
? SizedBox()
: LayoutBuilder(
key: Key(messages.last.id),
builder: (_, constraints) {
return Card(
shape: const RoundedRectangleBorder(
borderRadius: BorderRadius.all(
Radius.circular(12.0),
),
),
elevation: 10,
margin: EdgeInsets.only(
top: kToolbarHeight + 8,
left: 12,
right: 12,
),
color: context.colorScheme.surfaceContainerHigh,
child: Container(
width: min(
constraints.maxWidth,
400,
),
padding: EdgeInsets.symmetric(
horizontal: 12,
vertical: 16,
),
child: Text(
messages.last.text,
),
),
);
},
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,
),
),
);
},
),
);
},
),

View File

@@ -1,10 +1,13 @@
import 'dart:math';
import 'package:fl_clash/common/constant.dart';
import 'package:fl_clash/common/measure.dart';
import 'package:fl_clash/common/theme.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class ThemeManager extends StatelessWidget {
class ThemeManager extends ConsumerWidget {
final Widget child;
const ThemeManager({
@@ -13,14 +16,30 @@ class ThemeManager extends StatelessWidget {
});
@override
Widget build(BuildContext context) {
globalState.measure = Measure.of(context);
globalState.theme = CommonTheme.of(context);
Widget build(BuildContext context, ref) {
final textScale = ref.read(
themeSettingProvider.select((state) => state.textScale),
);
final double textScaleFactor = max(
min(
textScale.enable ? textScale.scale : defaultTextScaleFactor,
maxTextScale,
),
minTextScale,
);
globalState.measure = Measure.of(context, textScaleFactor);
globalState.theme = CommonTheme.of(context, textScaleFactor);
final padding = MediaQuery.of(context).padding;
final height = MediaQuery.of(context).size.height;
return MediaQuery(
data: MediaQuery.of(context).copyWith(
textScaler: TextScaler.linear(
textScaleFactor,
),
padding: padding.copyWith(
top: padding.top > height * 0.3 ? 0.0 : padding.top,
),
),
child: LayoutBuilder(
builder: (_, container) {

View File

@@ -25,7 +25,6 @@ class WindowManager extends ConsumerStatefulWidget {
class _WindowContainerState extends ConsumerState<WindowManager>
with WindowListener, WindowExtListener {
@override
Widget build(BuildContext context) {
return widget.child;
@@ -183,19 +182,23 @@ class _WindowHeaderState extends State<WindowHeader> {
super.dispose();
}
_updateMaximized() {
isMaximizedNotifier.value = !isMaximizedNotifier.value;
switch (isMaximizedNotifier.value) {
_updateMaximized() async {
final isMaximized = await windowManager.isMaximized();
switch (isMaximized) {
case true:
windowManager.maximize();
await windowManager.unmaximize();
break;
case false:
windowManager.unmaximize();
await windowManager.maximize();
break;
}
isMaximizedNotifier.value = await windowManager.isMaximized();
}
_updatePin() {
isPinNotifier.value = !isPinNotifier.value;
windowManager.setAlwaysOnTop(isPinNotifier.value);
_updatePin() async {
final isAlwaysOnTop = await windowManager.isAlwaysOnTop();
await windowManager.setAlwaysOnTop(!isAlwaysOnTop);
isPinNotifier.value = await windowManager.isAlwaysOnTop();
}
_buildActions() {

View File

@@ -16,7 +16,6 @@ class AppState with _$AppState {
@Default(false) bool isInit,
@Default(PageLabel.dashboard) PageLabel pageLabel,
@Default([]) List<Package> packages,
@Default(ColorSchemes()) ColorSchemes colorSchemes,
@Default(0) int sortNum,
required Size viewSize,
@Default({}) DelayMap delayMap,

View File

@@ -147,7 +147,8 @@ class Tun with _$Tun {
const factory Tun({
@Default(false) bool enable,
@Default(appName) String device,
@Default(TunStack.gvisor) TunStack stack,
@JsonKey(name: "auto-route") @Default(false) bool autoRoute,
@Default(TunStack.mixed) TunStack stack,
@JsonKey(name: "dns-hijack") @Default(["any:53"]) List<String> dnsHijack,
@JsonKey(name: "route-address") @Default([]) List<String> routeAddress,
}) = _Tun;

View File

@@ -30,7 +30,8 @@ class Package with _$Package {
const factory Package({
required String packageName,
required String label,
required bool isSystem,
required bool system,
required bool internet,
required int lastUpdateTime,
}) = _Package;
@@ -84,33 +85,33 @@ extension ConnectionExt on Connection {
}
}
@JsonSerializable()
class Log {
@JsonKey(name: "LogLevel")
LogLevel logLevel;
@JsonKey(name: "Payload")
String? payload;
DateTime _dateTime;
String _logDateTime(_) {
return DateTime.now().toString();
}
Log({
required this.logLevel,
this.payload,
}) : _dateTime = DateTime.now();
// String _logId(_) {
// return utils.id;
// }
DateTime get dateTime => _dateTime;
@freezed
class Log with _$Log {
const factory Log({
@JsonKey(name: "LogLevel") @Default(LogLevel.app) LogLevel logLevel,
@JsonKey(name: "Payload") @Default("") String payload,
@JsonKey(fromJson: _logDateTime) required String dateTime,
}) = _Log;
factory Log.fromJson(Map<String, dynamic> json) {
return _$LogFromJson(json);
factory Log.app(
String payload,
) {
return Log(
payload: payload,
dateTime: _logDateTime(null),
// id: _logId(null),
);
}
Map<String, dynamic> toJson() {
return _$LogToJson(this);
}
@override
String toString() {
return 'Log{logLevel: $logLevel, payload: $payload, dateTime: $dateTime}';
}
factory Log.fromJson(Map<String, Object?> json) => _$LogFromJson(json);
}
@freezed
@@ -119,6 +120,7 @@ class LogsState with _$LogsState {
@Default([]) List<Log> logs,
@Default([]) List<String> keywords,
@Default("") String query,
@Default(false) bool loading,
}) = _LogsState;
}
@@ -127,11 +129,10 @@ extension LogsStateExt on LogsState {
final lowQuery = query.toLowerCase();
return logs.where(
(log) {
final payload = log.payload?.toLowerCase();
final payload = log.payload.toLowerCase();
final logLevelName = log.logLevel.name;
return {logLevelName}.containsAll(keywords) &&
((payload?.contains(lowQuery) ?? false) ||
logLevelName.contains(lowQuery));
((payload.contains(lowQuery)) || logLevelName.contains(lowQuery));
},
).toList();
}
@@ -143,6 +144,7 @@ class ConnectionsState with _$ConnectionsState {
@Default([]) List<Connection> connections,
@Default([]) List<String> keywords,
@Default("") String query,
@Default(false) bool loading,
}) = _ConnectionsState;
}
@@ -512,3 +514,17 @@ class PopupMenuItemData {
final IconData? icon;
final PopupMenuItemType? type;
}
@freezed
class TextPainterParams with _$TextPainterParams {
const factory TextPainterParams({
required String? text,
required double? fontSize,
required double textScaleFactor,
@Default(double.infinity) double maxWidth,
int? maxLines,
}) = _TextPainterParams;
factory TextPainterParams.fromJson(Map<String, Object?> json) =>
_$TextPainterParamsFromJson(json);
}

View File

@@ -37,7 +37,9 @@ const defaultNetworkProps = NetworkProps();
const defaultProxiesStyle = ProxiesStyle();
const defaultWindowProps = WindowProps();
const defaultAccessControl = AccessControl();
const defaultThemeProps = ThemeProps();
final defaultThemeProps = ThemeProps(
primaryColor: defaultPrimaryColor,
);
const List<DashboardWidget> defaultDashboardWidgets = [
DashboardWidget.networkSpeed,
@@ -73,7 +75,7 @@ class AppSettingProps with _$AppSettingProps {
@Default(false) bool autoLaunch,
@Default(false) bool silentLaunch,
@Default(false) bool autoRun,
@Default(true) bool openLogs,
@Default(false) bool openLogs,
@Default(true) bool closeConnections,
@Default(defaultTestUrl) String testUrl,
@Default(true) bool isAnimateToPage,
@@ -82,6 +84,8 @@ class AppSettingProps with _$AppSettingProps {
@Default(false) bool disclaimerAccepted,
@Default(true) bool minimizeOnExit,
@Default(false) bool hidden,
@Default(false) bool developerMode,
@Default(RecoveryStrategy.compatible) RecoveryStrategy recoveryStrategy,
}) = _AppSettingProps;
factory AppSettingProps.fromJson(Map<String, Object?> json) =>
@@ -103,6 +107,7 @@ class AccessControl with _$AccessControl {
@Default([]) List<String> rejectList,
@Default(AccessSortType.none) AccessSortType sort,
@Default(true) bool isFilterSystemApp,
@Default(true) bool isFilterNonInternetApp,
}) = _AccessControl;
factory AccessControl.fromJson(Map<String, Object?> json) =>
@@ -170,18 +175,41 @@ class ProxiesStyle with _$ProxiesStyle {
json == null ? defaultProxiesStyle : _$ProxiesStyleFromJson(json);
}
@freezed
class TextScale with _$TextScale {
const factory TextScale({
@Default(false) enable,
@Default(1.0) scale,
}) = _TextScale;
factory TextScale.fromJson(Map<String, Object?> json) =>
_$TextScaleFromJson(json);
}
@freezed
class ThemeProps with _$ThemeProps {
const factory ThemeProps({
@Default(defaultPrimaryColor) int? primaryColor,
int? primaryColor,
@Default(defaultPrimaryColors) List<int> primaryColors,
@Default(ThemeMode.dark) ThemeMode themeMode,
@Default(DynamicSchemeVariant.tonalSpot) DynamicSchemeVariant schemeVariant,
@Default(DynamicSchemeVariant.content) DynamicSchemeVariant schemeVariant,
@Default(false) bool pureBlack,
@Default(TextScale()) TextScale textScale,
}) = _ThemeProps;
factory ThemeProps.fromJson(Map<String, Object?>? json) =>
json == null ? defaultThemeProps : _$ThemePropsFromJson(json);
factory ThemeProps.fromJson(Map<String, Object?> json) =>
_$ThemePropsFromJson(json);
factory ThemeProps.safeFromJson(Map<String, Object?>? json) {
if (json == null) {
return defaultThemeProps;
}
try {
return ThemeProps.fromJson(json);
} catch (_) {
return defaultThemeProps;
}
}
}
@freezed
@@ -197,7 +225,7 @@ class Config with _$Config {
DAV? dav,
@Default(defaultNetworkProps) NetworkProps networkProps,
@Default(defaultVpnProps) VpnProps vpnProps,
@Default(defaultThemeProps) ThemeProps themeProps,
@JsonKey(fromJson: ThemeProps.safeFromJson) required ThemeProps themeProps,
@Default(defaultProxiesStyle) ProxiesStyle proxiesStyle,
@Default(defaultWindowProps) WindowProps windowProps,
@Default(defaultClashConfig) ClashConfig patchClashConfig,

View File

@@ -19,7 +19,6 @@ mixin _$AppState {
bool get isInit => throw _privateConstructorUsedError;
PageLabel get pageLabel => throw _privateConstructorUsedError;
List<Package> get packages => throw _privateConstructorUsedError;
ColorSchemes get colorSchemes => throw _privateConstructorUsedError;
int get sortNum => throw _privateConstructorUsedError;
Size get viewSize => throw _privateConstructorUsedError;
Map<String, Map<String, int?>> get delayMap =>
@@ -53,7 +52,6 @@ abstract class $AppStateCopyWith<$Res> {
{bool isInit,
PageLabel pageLabel,
List<Package> packages,
ColorSchemes colorSchemes,
int sortNum,
Size viewSize,
Map<String, Map<String, int?>> delayMap,
@@ -69,8 +67,6 @@ abstract class $AppStateCopyWith<$Res> {
FixedList<Traffic> traffics,
Traffic totalTraffic,
bool needApply});
$ColorSchemesCopyWith<$Res> get colorSchemes;
}
/// @nodoc
@@ -91,7 +87,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
Object? isInit = null,
Object? pageLabel = null,
Object? packages = null,
Object? colorSchemes = null,
Object? sortNum = null,
Object? viewSize = null,
Object? delayMap = null,
@@ -121,10 +116,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
? _value.packages
: packages // ignore: cast_nullable_to_non_nullable
as List<Package>,
colorSchemes: null == colorSchemes
? _value.colorSchemes
: colorSchemes // ignore: cast_nullable_to_non_nullable
as ColorSchemes,
sortNum: null == sortNum
? _value.sortNum
: sortNum // ignore: cast_nullable_to_non_nullable
@@ -187,16 +178,6 @@ class _$AppStateCopyWithImpl<$Res, $Val extends AppState>
as bool,
) as $Val);
}
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ColorSchemesCopyWith<$Res> get colorSchemes {
return $ColorSchemesCopyWith<$Res>(_value.colorSchemes, (value) {
return _then(_value.copyWith(colorSchemes: value) as $Val);
});
}
}
/// @nodoc
@@ -211,7 +192,6 @@ abstract class _$$AppStateImplCopyWith<$Res>
{bool isInit,
PageLabel pageLabel,
List<Package> packages,
ColorSchemes colorSchemes,
int sortNum,
Size viewSize,
Map<String, Map<String, int?>> delayMap,
@@ -227,9 +207,6 @@ abstract class _$$AppStateImplCopyWith<$Res>
FixedList<Traffic> traffics,
Traffic totalTraffic,
bool needApply});
@override
$ColorSchemesCopyWith<$Res> get colorSchemes;
}
/// @nodoc
@@ -248,7 +225,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
Object? isInit = null,
Object? pageLabel = null,
Object? packages = null,
Object? colorSchemes = null,
Object? sortNum = null,
Object? viewSize = null,
Object? delayMap = null,
@@ -278,10 +254,6 @@ class __$$AppStateImplCopyWithImpl<$Res>
? _value._packages
: packages // ignore: cast_nullable_to_non_nullable
as List<Package>,
colorSchemes: null == colorSchemes
? _value.colorSchemes
: colorSchemes // ignore: cast_nullable_to_non_nullable
as ColorSchemes,
sortNum: null == sortNum
? _value.sortNum
: sortNum // ignore: cast_nullable_to_non_nullable
@@ -353,7 +325,6 @@ class _$AppStateImpl implements _AppState {
{this.isInit = false,
this.pageLabel = PageLabel.dashboard,
final List<Package> packages = const [],
this.colorSchemes = const ColorSchemes(),
this.sortNum = 0,
required this.viewSize,
final Map<String, Map<String, int?>> delayMap = const {},
@@ -389,9 +360,6 @@ class _$AppStateImpl implements _AppState {
return EqualUnmodifiableListView(_packages);
}
@override
@JsonKey()
final ColorSchemes colorSchemes;
@override
@JsonKey()
final int sortNum;
@@ -449,7 +417,7 @@ class _$AppStateImpl implements _AppState {
@override
String toString() {
return 'AppState(isInit: $isInit, pageLabel: $pageLabel, packages: $packages, colorSchemes: $colorSchemes, 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, needApply: $needApply)';
return 'AppState(isInit: $isInit, 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, needApply: $needApply)';
}
@override
@@ -461,8 +429,6 @@ class _$AppStateImpl implements _AppState {
(identical(other.pageLabel, pageLabel) ||
other.pageLabel == pageLabel) &&
const DeepCollectionEquality().equals(other._packages, _packages) &&
(identical(other.colorSchemes, colorSchemes) ||
other.colorSchemes == colorSchemes) &&
(identical(other.sortNum, sortNum) || other.sortNum == sortNum) &&
(identical(other.viewSize, viewSize) ||
other.viewSize == viewSize) &&
@@ -489,28 +455,26 @@ class _$AppStateImpl implements _AppState {
}
@override
int get hashCode => Object.hashAll([
runtimeType,
isInit,
pageLabel,
const DeepCollectionEquality().hash(_packages),
colorSchemes,
sortNum,
viewSize,
const DeepCollectionEquality().hash(_delayMap),
const DeepCollectionEquality().hash(_groups),
checkIpNum,
brightness,
runTime,
const DeepCollectionEquality().hash(_providers),
localIp,
requests,
version,
logs,
traffics,
totalTraffic,
needApply
]);
int get hashCode => Object.hash(
runtimeType,
isInit,
pageLabel,
const DeepCollectionEquality().hash(_packages),
sortNum,
viewSize,
const DeepCollectionEquality().hash(_delayMap),
const DeepCollectionEquality().hash(_groups),
checkIpNum,
brightness,
runTime,
const DeepCollectionEquality().hash(_providers),
localIp,
requests,
version,
logs,
traffics,
totalTraffic,
needApply);
/// Create a copy of AppState
/// with the given fields replaced by the non-null parameter values.
@@ -526,7 +490,6 @@ abstract class _AppState implements AppState {
{final bool isInit,
final PageLabel pageLabel,
final List<Package> packages,
final ColorSchemes colorSchemes,
final int sortNum,
required final Size viewSize,
final Map<String, Map<String, int?>> delayMap,
@@ -550,8 +513,6 @@ abstract class _AppState implements AppState {
@override
List<Package> get packages;
@override
ColorSchemes get colorSchemes;
@override
int get sortNum;
@override
Size get viewSize;

View File

@@ -660,6 +660,8 @@ Tun _$TunFromJson(Map<String, dynamic> json) {
mixin _$Tun {
bool get enable => throw _privateConstructorUsedError;
String get device => throw _privateConstructorUsedError;
@JsonKey(name: "auto-route")
bool get autoRoute => throw _privateConstructorUsedError;
TunStack get stack => throw _privateConstructorUsedError;
@JsonKey(name: "dns-hijack")
List<String> get dnsHijack => throw _privateConstructorUsedError;
@@ -683,6 +685,7 @@ abstract class $TunCopyWith<$Res> {
$Res call(
{bool enable,
String device,
@JsonKey(name: "auto-route") bool autoRoute,
TunStack stack,
@JsonKey(name: "dns-hijack") List<String> dnsHijack,
@JsonKey(name: "route-address") List<String> routeAddress});
@@ -704,6 +707,7 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> {
$Res call({
Object? enable = null,
Object? device = null,
Object? autoRoute = null,
Object? stack = null,
Object? dnsHijack = null,
Object? routeAddress = null,
@@ -717,6 +721,10 @@ class _$TunCopyWithImpl<$Res, $Val extends Tun> implements $TunCopyWith<$Res> {
? _value.device
: device // ignore: cast_nullable_to_non_nullable
as String,
autoRoute: null == autoRoute
? _value.autoRoute
: autoRoute // ignore: cast_nullable_to_non_nullable
as bool,
stack: null == stack
? _value.stack
: stack // ignore: cast_nullable_to_non_nullable
@@ -742,6 +750,7 @@ abstract class _$$TunImplCopyWith<$Res> implements $TunCopyWith<$Res> {
$Res call(
{bool enable,
String device,
@JsonKey(name: "auto-route") bool autoRoute,
TunStack stack,
@JsonKey(name: "dns-hijack") List<String> dnsHijack,
@JsonKey(name: "route-address") List<String> routeAddress});
@@ -760,6 +769,7 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl>
$Res call({
Object? enable = null,
Object? device = null,
Object? autoRoute = null,
Object? stack = null,
Object? dnsHijack = null,
Object? routeAddress = null,
@@ -773,6 +783,10 @@ class __$$TunImplCopyWithImpl<$Res> extends _$TunCopyWithImpl<$Res, _$TunImpl>
? _value.device
: device // ignore: cast_nullable_to_non_nullable
as String,
autoRoute: null == autoRoute
? _value.autoRoute
: autoRoute // ignore: cast_nullable_to_non_nullable
as bool,
stack: null == stack
? _value.stack
: stack // ignore: cast_nullable_to_non_nullable
@@ -795,7 +809,8 @@ class _$TunImpl implements _Tun {
const _$TunImpl(
{this.enable = false,
this.device = appName,
this.stack = TunStack.gvisor,
@JsonKey(name: "auto-route") this.autoRoute = false,
this.stack = TunStack.mixed,
@JsonKey(name: "dns-hijack")
final List<String> dnsHijack = const ["any:53"],
@JsonKey(name: "route-address")
@@ -813,6 +828,9 @@ class _$TunImpl implements _Tun {
@JsonKey()
final String device;
@override
@JsonKey(name: "auto-route")
final bool autoRoute;
@override
@JsonKey()
final TunStack stack;
final List<String> _dnsHijack;
@@ -835,7 +853,7 @@ class _$TunImpl implements _Tun {
@override
String toString() {
return 'Tun(enable: $enable, device: $device, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress)';
return 'Tun(enable: $enable, device: $device, autoRoute: $autoRoute, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress)';
}
@override
@@ -845,6 +863,8 @@ class _$TunImpl implements _Tun {
other is _$TunImpl &&
(identical(other.enable, enable) || other.enable == enable) &&
(identical(other.device, device) || other.device == device) &&
(identical(other.autoRoute, autoRoute) ||
other.autoRoute == autoRoute) &&
(identical(other.stack, stack) || other.stack == stack) &&
const DeepCollectionEquality()
.equals(other._dnsHijack, _dnsHijack) &&
@@ -858,6 +878,7 @@ class _$TunImpl implements _Tun {
runtimeType,
enable,
device,
autoRoute,
stack,
const DeepCollectionEquality().hash(_dnsHijack),
const DeepCollectionEquality().hash(_routeAddress));
@@ -882,6 +903,7 @@ abstract class _Tun implements Tun {
const factory _Tun(
{final bool enable,
final String device,
@JsonKey(name: "auto-route") final bool autoRoute,
final TunStack stack,
@JsonKey(name: "dns-hijack") final List<String> dnsHijack,
@JsonKey(name: "route-address") final List<String> routeAddress}) =
@@ -894,6 +916,9 @@ abstract class _Tun implements Tun {
@override
String get device;
@override
@JsonKey(name: "auto-route")
bool get autoRoute;
@override
TunStack get stack;
@override
@JsonKey(name: "dns-hijack")

View File

@@ -66,8 +66,9 @@ Map<String, dynamic> _$$RuleProviderImplToJson(_$RuleProviderImpl instance) =>
_$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
enable: json['enable'] as bool? ?? false,
device: json['device'] as String? ?? appName,
autoRoute: json['auto-route'] as bool? ?? false,
stack: $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ??
TunStack.gvisor,
TunStack.mixed,
dnsHijack: (json['dns-hijack'] as List<dynamic>?)
?.map((e) => e as String)
.toList() ??
@@ -81,6 +82,7 @@ _$TunImpl _$$TunImplFromJson(Map<String, dynamic> json) => _$TunImpl(
Map<String, dynamic> _$$TunImplToJson(_$TunImpl instance) => <String, dynamic>{
'enable': instance.enable,
'device': instance.device,
'auto-route': instance.autoRoute,
'stack': _$TunStackEnumMap[instance.stack]!,
'dns-hijack': instance.dnsHijack,
'route-address': instance.routeAddress,
@@ -342,6 +344,7 @@ const _$LogLevelEnumMap = {
LogLevel.warning: 'warning',
LogLevel.error: 'error',
LogLevel.silent: 'silent',
LogLevel.app: 'app',
};
const _$FindProcessModeEnumMap = {

View File

@@ -289,7 +289,8 @@ Package _$PackageFromJson(Map<String, dynamic> json) {
mixin _$Package {
String get packageName => throw _privateConstructorUsedError;
String get label => throw _privateConstructorUsedError;
bool get isSystem => throw _privateConstructorUsedError;
bool get system => throw _privateConstructorUsedError;
bool get internet => throw _privateConstructorUsedError;
int get lastUpdateTime => throw _privateConstructorUsedError;
/// Serializes this Package to a JSON map.
@@ -307,7 +308,11 @@ abstract class $PackageCopyWith<$Res> {
_$PackageCopyWithImpl<$Res, Package>;
@useResult
$Res call(
{String packageName, String label, bool isSystem, int lastUpdateTime});
{String packageName,
String label,
bool system,
bool internet,
int lastUpdateTime});
}
/// @nodoc
@@ -327,7 +332,8 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package>
$Res call({
Object? packageName = null,
Object? label = null,
Object? isSystem = null,
Object? system = null,
Object? internet = null,
Object? lastUpdateTime = null,
}) {
return _then(_value.copyWith(
@@ -339,9 +345,13 @@ class _$PackageCopyWithImpl<$Res, $Val extends Package>
? _value.label
: label // ignore: cast_nullable_to_non_nullable
as String,
isSystem: null == isSystem
? _value.isSystem
: isSystem // ignore: cast_nullable_to_non_nullable
system: null == system
? _value.system
: system // ignore: cast_nullable_to_non_nullable
as bool,
internet: null == internet
? _value.internet
: internet // ignore: cast_nullable_to_non_nullable
as bool,
lastUpdateTime: null == lastUpdateTime
? _value.lastUpdateTime
@@ -359,7 +369,11 @@ abstract class _$$PackageImplCopyWith<$Res> implements $PackageCopyWith<$Res> {
@override
@useResult
$Res call(
{String packageName, String label, bool isSystem, int lastUpdateTime});
{String packageName,
String label,
bool system,
bool internet,
int lastUpdateTime});
}
/// @nodoc
@@ -377,7 +391,8 @@ class __$$PackageImplCopyWithImpl<$Res>
$Res call({
Object? packageName = null,
Object? label = null,
Object? isSystem = null,
Object? system = null,
Object? internet = null,
Object? lastUpdateTime = null,
}) {
return _then(_$PackageImpl(
@@ -389,9 +404,13 @@ class __$$PackageImplCopyWithImpl<$Res>
? _value.label
: label // ignore: cast_nullable_to_non_nullable
as String,
isSystem: null == isSystem
? _value.isSystem
: isSystem // ignore: cast_nullable_to_non_nullable
system: null == system
? _value.system
: system // ignore: cast_nullable_to_non_nullable
as bool,
internet: null == internet
? _value.internet
: internet // ignore: cast_nullable_to_non_nullable
as bool,
lastUpdateTime: null == lastUpdateTime
? _value.lastUpdateTime
@@ -407,7 +426,8 @@ class _$PackageImpl implements _Package {
const _$PackageImpl(
{required this.packageName,
required this.label,
required this.isSystem,
required this.system,
required this.internet,
required this.lastUpdateTime});
factory _$PackageImpl.fromJson(Map<String, dynamic> json) =>
@@ -418,13 +438,15 @@ class _$PackageImpl implements _Package {
@override
final String label;
@override
final bool isSystem;
final bool system;
@override
final bool internet;
@override
final int lastUpdateTime;
@override
String toString() {
return 'Package(packageName: $packageName, label: $label, isSystem: $isSystem, lastUpdateTime: $lastUpdateTime)';
return 'Package(packageName: $packageName, label: $label, system: $system, internet: $internet, lastUpdateTime: $lastUpdateTime)';
}
@override
@@ -435,16 +457,17 @@ class _$PackageImpl implements _Package {
(identical(other.packageName, packageName) ||
other.packageName == packageName) &&
(identical(other.label, label) || other.label == label) &&
(identical(other.isSystem, isSystem) ||
other.isSystem == isSystem) &&
(identical(other.system, system) || other.system == system) &&
(identical(other.internet, internet) ||
other.internet == internet) &&
(identical(other.lastUpdateTime, lastUpdateTime) ||
other.lastUpdateTime == lastUpdateTime));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode =>
Object.hash(runtimeType, packageName, label, isSystem, lastUpdateTime);
int get hashCode => Object.hash(
runtimeType, packageName, label, system, internet, lastUpdateTime);
/// Create a copy of Package
/// with the given fields replaced by the non-null parameter values.
@@ -466,7 +489,8 @@ abstract class _Package implements Package {
const factory _Package(
{required final String packageName,
required final String label,
required final bool isSystem,
required final bool system,
required final bool internet,
required final int lastUpdateTime}) = _$PackageImpl;
factory _Package.fromJson(Map<String, dynamic> json) = _$PackageImpl.fromJson;
@@ -476,7 +500,9 @@ abstract class _Package implements Package {
@override
String get label;
@override
bool get isSystem;
bool get system;
@override
bool get internet;
@override
int get lastUpdateTime;
@@ -1092,11 +1118,209 @@ abstract class _Connection implements Connection {
throw _privateConstructorUsedError;
}
Log _$LogFromJson(Map<String, dynamic> json) {
return _Log.fromJson(json);
}
/// @nodoc
mixin _$Log {
@JsonKey(name: "LogLevel")
LogLevel get logLevel => throw _privateConstructorUsedError;
@JsonKey(name: "Payload")
String get payload => throw _privateConstructorUsedError;
@JsonKey(fromJson: _logDateTime)
String get dateTime => throw _privateConstructorUsedError;
/// Serializes this Log to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of Log
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LogCopyWith<Log> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LogCopyWith<$Res> {
factory $LogCopyWith(Log value, $Res Function(Log) then) =
_$LogCopyWithImpl<$Res, Log>;
@useResult
$Res call(
{@JsonKey(name: "LogLevel") LogLevel logLevel,
@JsonKey(name: "Payload") String payload,
@JsonKey(fromJson: _logDateTime) String dateTime});
}
/// @nodoc
class _$LogCopyWithImpl<$Res, $Val extends Log> implements $LogCopyWith<$Res> {
_$LogCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of Log
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? logLevel = null,
Object? payload = null,
Object? dateTime = null,
}) {
return _then(_value.copyWith(
logLevel: null == logLevel
? _value.logLevel
: logLevel // ignore: cast_nullable_to_non_nullable
as LogLevel,
payload: null == payload
? _value.payload
: payload // ignore: cast_nullable_to_non_nullable
as String,
dateTime: null == dateTime
? _value.dateTime
: dateTime // ignore: cast_nullable_to_non_nullable
as String,
) as $Val);
}
}
/// @nodoc
abstract class _$$LogImplCopyWith<$Res> implements $LogCopyWith<$Res> {
factory _$$LogImplCopyWith(_$LogImpl value, $Res Function(_$LogImpl) then) =
__$$LogImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{@JsonKey(name: "LogLevel") LogLevel logLevel,
@JsonKey(name: "Payload") String payload,
@JsonKey(fromJson: _logDateTime) String dateTime});
}
/// @nodoc
class __$$LogImplCopyWithImpl<$Res> extends _$LogCopyWithImpl<$Res, _$LogImpl>
implements _$$LogImplCopyWith<$Res> {
__$$LogImplCopyWithImpl(_$LogImpl _value, $Res Function(_$LogImpl) _then)
: super(_value, _then);
/// Create a copy of Log
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? logLevel = null,
Object? payload = null,
Object? dateTime = null,
}) {
return _then(_$LogImpl(
logLevel: null == logLevel
? _value.logLevel
: logLevel // ignore: cast_nullable_to_non_nullable
as LogLevel,
payload: null == payload
? _value.payload
: payload // ignore: cast_nullable_to_non_nullable
as String,
dateTime: null == dateTime
? _value.dateTime
: dateTime // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
@JsonSerializable()
class _$LogImpl implements _Log {
const _$LogImpl(
{@JsonKey(name: "LogLevel") this.logLevel = LogLevel.app,
@JsonKey(name: "Payload") this.payload = "",
@JsonKey(fromJson: _logDateTime) required this.dateTime});
factory _$LogImpl.fromJson(Map<String, dynamic> json) =>
_$$LogImplFromJson(json);
@override
@JsonKey(name: "LogLevel")
final LogLevel logLevel;
@override
@JsonKey(name: "Payload")
final String payload;
@override
@JsonKey(fromJson: _logDateTime)
final String dateTime;
@override
String toString() {
return 'Log(logLevel: $logLevel, payload: $payload, dateTime: $dateTime)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LogImpl &&
(identical(other.logLevel, logLevel) ||
other.logLevel == logLevel) &&
(identical(other.payload, payload) || other.payload == payload) &&
(identical(other.dateTime, dateTime) ||
other.dateTime == dateTime));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType, logLevel, payload, dateTime);
/// Create a copy of Log
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LogImplCopyWith<_$LogImpl> get copyWith =>
__$$LogImplCopyWithImpl<_$LogImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$LogImplToJson(
this,
);
}
}
abstract class _Log implements Log {
const factory _Log(
{@JsonKey(name: "LogLevel") final LogLevel logLevel,
@JsonKey(name: "Payload") final String payload,
@JsonKey(fromJson: _logDateTime) required final String dateTime}) =
_$LogImpl;
factory _Log.fromJson(Map<String, dynamic> json) = _$LogImpl.fromJson;
@override
@JsonKey(name: "LogLevel")
LogLevel get logLevel;
@override
@JsonKey(name: "Payload")
String get payload;
@override
@JsonKey(fromJson: _logDateTime)
String get dateTime;
/// Create a copy of Log
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LogImplCopyWith<_$LogImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$LogsState {
List<Log> get logs => throw _privateConstructorUsedError;
List<String> get keywords => throw _privateConstructorUsedError;
String get query => throw _privateConstructorUsedError;
bool get loading => throw _privateConstructorUsedError;
/// Create a copy of LogsState
/// with the given fields replaced by the non-null parameter values.
@@ -1110,7 +1334,8 @@ abstract class $LogsStateCopyWith<$Res> {
factory $LogsStateCopyWith(LogsState value, $Res Function(LogsState) then) =
_$LogsStateCopyWithImpl<$Res, LogsState>;
@useResult
$Res call({List<Log> logs, List<String> keywords, String query});
$Res call(
{List<Log> logs, List<String> keywords, String query, bool loading});
}
/// @nodoc
@@ -1131,6 +1356,7 @@ class _$LogsStateCopyWithImpl<$Res, $Val extends LogsState>
Object? logs = null,
Object? keywords = null,
Object? query = null,
Object? loading = null,
}) {
return _then(_value.copyWith(
logs: null == logs
@@ -1145,6 +1371,10 @@ class _$LogsStateCopyWithImpl<$Res, $Val extends LogsState>
? _value.query
: query // ignore: cast_nullable_to_non_nullable
as String,
loading: null == loading
? _value.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -1157,7 +1387,8 @@ abstract class _$$LogsStateImplCopyWith<$Res>
__$$LogsStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({List<Log> logs, List<String> keywords, String query});
$Res call(
{List<Log> logs, List<String> keywords, String query, bool loading});
}
/// @nodoc
@@ -1176,6 +1407,7 @@ class __$$LogsStateImplCopyWithImpl<$Res>
Object? logs = null,
Object? keywords = null,
Object? query = null,
Object? loading = null,
}) {
return _then(_$LogsStateImpl(
logs: null == logs
@@ -1190,6 +1422,10 @@ class __$$LogsStateImplCopyWithImpl<$Res>
? _value.query
: query // ignore: cast_nullable_to_non_nullable
as String,
loading: null == loading
? _value.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -1200,7 +1436,8 @@ class _$LogsStateImpl implements _LogsState {
const _$LogsStateImpl(
{final List<Log> logs = const [],
final List<String> keywords = const [],
this.query = ""})
this.query = "",
this.loading = false})
: _logs = logs,
_keywords = keywords;
@@ -1225,10 +1462,13 @@ class _$LogsStateImpl implements _LogsState {
@override
@JsonKey()
final String query;
@override
@JsonKey()
final bool loading;
@override
String toString() {
return 'LogsState(logs: $logs, keywords: $keywords, query: $query)';
return 'LogsState(logs: $logs, keywords: $keywords, query: $query, loading: $loading)';
}
@override
@@ -1238,7 +1478,8 @@ class _$LogsStateImpl implements _LogsState {
other is _$LogsStateImpl &&
const DeepCollectionEquality().equals(other._logs, _logs) &&
const DeepCollectionEquality().equals(other._keywords, _keywords) &&
(identical(other.query, query) || other.query == query));
(identical(other.query, query) || other.query == query) &&
(identical(other.loading, loading) || other.loading == loading));
}
@override
@@ -1246,7 +1487,8 @@ class _$LogsStateImpl implements _LogsState {
runtimeType,
const DeepCollectionEquality().hash(_logs),
const DeepCollectionEquality().hash(_keywords),
query);
query,
loading);
/// Create a copy of LogsState
/// with the given fields replaced by the non-null parameter values.
@@ -1261,7 +1503,8 @@ abstract class _LogsState implements LogsState {
const factory _LogsState(
{final List<Log> logs,
final List<String> keywords,
final String query}) = _$LogsStateImpl;
final String query,
final bool loading}) = _$LogsStateImpl;
@override
List<Log> get logs;
@@ -1269,6 +1512,8 @@ abstract class _LogsState implements LogsState {
List<String> get keywords;
@override
String get query;
@override
bool get loading;
/// Create a copy of LogsState
/// with the given fields replaced by the non-null parameter values.
@@ -1283,6 +1528,7 @@ mixin _$ConnectionsState {
List<Connection> get connections => throw _privateConstructorUsedError;
List<String> get keywords => throw _privateConstructorUsedError;
String get query => throw _privateConstructorUsedError;
bool get loading => throw _privateConstructorUsedError;
/// Create a copy of ConnectionsState
/// with the given fields replaced by the non-null parameter values.
@@ -1298,7 +1544,10 @@ abstract class $ConnectionsStateCopyWith<$Res> {
_$ConnectionsStateCopyWithImpl<$Res, ConnectionsState>;
@useResult
$Res call(
{List<Connection> connections, List<String> keywords, String query});
{List<Connection> connections,
List<String> keywords,
String query,
bool loading});
}
/// @nodoc
@@ -1319,6 +1568,7 @@ class _$ConnectionsStateCopyWithImpl<$Res, $Val extends ConnectionsState>
Object? connections = null,
Object? keywords = null,
Object? query = null,
Object? loading = null,
}) {
return _then(_value.copyWith(
connections: null == connections
@@ -1333,6 +1583,10 @@ class _$ConnectionsStateCopyWithImpl<$Res, $Val extends ConnectionsState>
? _value.query
: query // ignore: cast_nullable_to_non_nullable
as String,
loading: null == loading
? _value.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -1346,7 +1600,10 @@ abstract class _$$ConnectionsStateImplCopyWith<$Res>
@override
@useResult
$Res call(
{List<Connection> connections, List<String> keywords, String query});
{List<Connection> connections,
List<String> keywords,
String query,
bool loading});
}
/// @nodoc
@@ -1365,6 +1622,7 @@ class __$$ConnectionsStateImplCopyWithImpl<$Res>
Object? connections = null,
Object? keywords = null,
Object? query = null,
Object? loading = null,
}) {
return _then(_$ConnectionsStateImpl(
connections: null == connections
@@ -1379,6 +1637,10 @@ class __$$ConnectionsStateImplCopyWithImpl<$Res>
? _value.query
: query // ignore: cast_nullable_to_non_nullable
as String,
loading: null == loading
? _value.loading
: loading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -1389,7 +1651,8 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
const _$ConnectionsStateImpl(
{final List<Connection> connections = const [],
final List<String> keywords = const [],
this.query = ""})
this.query = "",
this.loading = false})
: _connections = connections,
_keywords = keywords;
@@ -1414,10 +1677,13 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
@override
@JsonKey()
final String query;
@override
@JsonKey()
final bool loading;
@override
String toString() {
return 'ConnectionsState(connections: $connections, keywords: $keywords, query: $query)';
return 'ConnectionsState(connections: $connections, keywords: $keywords, query: $query, loading: $loading)';
}
@override
@@ -1428,7 +1694,8 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
const DeepCollectionEquality()
.equals(other._connections, _connections) &&
const DeepCollectionEquality().equals(other._keywords, _keywords) &&
(identical(other.query, query) || other.query == query));
(identical(other.query, query) || other.query == query) &&
(identical(other.loading, loading) || other.loading == loading));
}
@override
@@ -1436,7 +1703,8 @@ class _$ConnectionsStateImpl implements _ConnectionsState {
runtimeType,
const DeepCollectionEquality().hash(_connections),
const DeepCollectionEquality().hash(_keywords),
query);
query,
loading);
/// Create a copy of ConnectionsState
/// with the given fields replaced by the non-null parameter values.
@@ -1452,7 +1720,8 @@ abstract class _ConnectionsState implements ConnectionsState {
const factory _ConnectionsState(
{final List<Connection> connections,
final List<String> keywords,
final String query}) = _$ConnectionsStateImpl;
final String query,
final bool loading}) = _$ConnectionsStateImpl;
@override
List<Connection> get connections;
@@ -1460,6 +1729,8 @@ abstract class _ConnectionsState implements ConnectionsState {
List<String> get keywords;
@override
String get query;
@override
bool get loading;
/// Create a copy of ConnectionsState
/// with the given fields replaced by the non-null parameter values.
@@ -2955,3 +3226,243 @@ abstract class _Field implements Field {
_$$FieldImplCopyWith<_$FieldImpl> get copyWith =>
throw _privateConstructorUsedError;
}
TextPainterParams _$TextPainterParamsFromJson(Map<String, dynamic> json) {
return _TextPainterParams.fromJson(json);
}
/// @nodoc
mixin _$TextPainterParams {
String? get text => throw _privateConstructorUsedError;
double? get fontSize => throw _privateConstructorUsedError;
double get textScaleFactor => throw _privateConstructorUsedError;
double get maxWidth => throw _privateConstructorUsedError;
int? get maxLines => throw _privateConstructorUsedError;
/// Serializes this TextPainterParams to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of TextPainterParams
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TextPainterParamsCopyWith<TextPainterParams> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TextPainterParamsCopyWith<$Res> {
factory $TextPainterParamsCopyWith(
TextPainterParams value, $Res Function(TextPainterParams) then) =
_$TextPainterParamsCopyWithImpl<$Res, TextPainterParams>;
@useResult
$Res call(
{String? text,
double? fontSize,
double textScaleFactor,
double maxWidth,
int? maxLines});
}
/// @nodoc
class _$TextPainterParamsCopyWithImpl<$Res, $Val extends TextPainterParams>
implements $TextPainterParamsCopyWith<$Res> {
_$TextPainterParamsCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of TextPainterParams
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? text = freezed,
Object? fontSize = freezed,
Object? textScaleFactor = null,
Object? maxWidth = null,
Object? maxLines = freezed,
}) {
return _then(_value.copyWith(
text: freezed == text
? _value.text
: text // ignore: cast_nullable_to_non_nullable
as String?,
fontSize: freezed == fontSize
? _value.fontSize
: fontSize // ignore: cast_nullable_to_non_nullable
as double?,
textScaleFactor: null == textScaleFactor
? _value.textScaleFactor
: textScaleFactor // ignore: cast_nullable_to_non_nullable
as double,
maxWidth: null == maxWidth
? _value.maxWidth
: maxWidth // ignore: cast_nullable_to_non_nullable
as double,
maxLines: freezed == maxLines
? _value.maxLines
: maxLines // ignore: cast_nullable_to_non_nullable
as int?,
) as $Val);
}
}
/// @nodoc
abstract class _$$TextPainterParamsImplCopyWith<$Res>
implements $TextPainterParamsCopyWith<$Res> {
factory _$$TextPainterParamsImplCopyWith(_$TextPainterParamsImpl value,
$Res Function(_$TextPainterParamsImpl) then) =
__$$TextPainterParamsImplCopyWithImpl<$Res>;
@override
@useResult
$Res call(
{String? text,
double? fontSize,
double textScaleFactor,
double maxWidth,
int? maxLines});
}
/// @nodoc
class __$$TextPainterParamsImplCopyWithImpl<$Res>
extends _$TextPainterParamsCopyWithImpl<$Res, _$TextPainterParamsImpl>
implements _$$TextPainterParamsImplCopyWith<$Res> {
__$$TextPainterParamsImplCopyWithImpl(_$TextPainterParamsImpl _value,
$Res Function(_$TextPainterParamsImpl) _then)
: super(_value, _then);
/// Create a copy of TextPainterParams
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? text = freezed,
Object? fontSize = freezed,
Object? textScaleFactor = null,
Object? maxWidth = null,
Object? maxLines = freezed,
}) {
return _then(_$TextPainterParamsImpl(
text: freezed == text
? _value.text
: text // ignore: cast_nullable_to_non_nullable
as String?,
fontSize: freezed == fontSize
? _value.fontSize
: fontSize // ignore: cast_nullable_to_non_nullable
as double?,
textScaleFactor: null == textScaleFactor
? _value.textScaleFactor
: textScaleFactor // ignore: cast_nullable_to_non_nullable
as double,
maxWidth: null == maxWidth
? _value.maxWidth
: maxWidth // ignore: cast_nullable_to_non_nullable
as double,
maxLines: freezed == maxLines
? _value.maxLines
: maxLines // ignore: cast_nullable_to_non_nullable
as int?,
));
}
}
/// @nodoc
@JsonSerializable()
class _$TextPainterParamsImpl implements _TextPainterParams {
const _$TextPainterParamsImpl(
{required this.text,
required this.fontSize,
required this.textScaleFactor,
this.maxWidth = double.infinity,
this.maxLines});
factory _$TextPainterParamsImpl.fromJson(Map<String, dynamic> json) =>
_$$TextPainterParamsImplFromJson(json);
@override
final String? text;
@override
final double? fontSize;
@override
final double textScaleFactor;
@override
@JsonKey()
final double maxWidth;
@override
final int? maxLines;
@override
String toString() {
return 'TextPainterParams(text: $text, fontSize: $fontSize, textScaleFactor: $textScaleFactor, maxWidth: $maxWidth, maxLines: $maxLines)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TextPainterParamsImpl &&
(identical(other.text, text) || other.text == text) &&
(identical(other.fontSize, fontSize) ||
other.fontSize == fontSize) &&
(identical(other.textScaleFactor, textScaleFactor) ||
other.textScaleFactor == textScaleFactor) &&
(identical(other.maxWidth, maxWidth) ||
other.maxWidth == maxWidth) &&
(identical(other.maxLines, maxLines) ||
other.maxLines == maxLines));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType, text, fontSize, textScaleFactor, maxWidth, maxLines);
/// Create a copy of TextPainterParams
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$TextPainterParamsImplCopyWith<_$TextPainterParamsImpl> get copyWith =>
__$$TextPainterParamsImplCopyWithImpl<_$TextPainterParamsImpl>(
this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$TextPainterParamsImplToJson(
this,
);
}
}
abstract class _TextPainterParams implements TextPainterParams {
const factory _TextPainterParams(
{required final String? text,
required final double? fontSize,
required final double textScaleFactor,
final double maxWidth,
final int? maxLines}) = _$TextPainterParamsImpl;
factory _TextPainterParams.fromJson(Map<String, dynamic> json) =
_$TextPainterParamsImpl.fromJson;
@override
String? get text;
@override
double? get fontSize;
@override
double get textScaleFactor;
@override
double get maxWidth;
@override
int? get maxLines;
/// Create a copy of TextPainterParams
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$TextPainterParamsImplCopyWith<_$TextPainterParamsImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@@ -6,29 +6,12 @@ part of '../common.dart';
// JsonSerializableGenerator
// **************************************************************************
Log _$LogFromJson(Map<String, dynamic> json) => Log(
logLevel: $enumDecode(_$LogLevelEnumMap, json['LogLevel']),
payload: json['Payload'] as String?,
);
Map<String, dynamic> _$LogToJson(Log instance) => <String, dynamic>{
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
'Payload': instance.payload,
};
const _$LogLevelEnumMap = {
LogLevel.debug: 'debug',
LogLevel.info: 'info',
LogLevel.warning: 'warning',
LogLevel.error: 'error',
LogLevel.silent: 'silent',
};
_$PackageImpl _$$PackageImplFromJson(Map<String, dynamic> json) =>
_$PackageImpl(
packageName: json['packageName'] as String,
label: json['label'] as String,
isSystem: json['isSystem'] as bool,
system: json['system'] as bool,
internet: json['internet'] as bool,
lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),
);
@@ -36,7 +19,8 @@ Map<String, dynamic> _$$PackageImplToJson(_$PackageImpl instance) =>
<String, dynamic>{
'packageName': instance.packageName,
'label': instance.label,
'isSystem': instance.isSystem,
'system': instance.system,
'internet': instance.internet,
'lastUpdateTime': instance.lastUpdateTime,
};
@@ -87,6 +71,28 @@ Map<String, dynamic> _$$ConnectionImplToJson(_$ConnectionImpl instance) =>
'chains': instance.chains,
};
_$LogImpl _$$LogImplFromJson(Map<String, dynamic> json) => _$LogImpl(
logLevel: $enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ??
LogLevel.app,
payload: json['Payload'] as String? ?? "",
dateTime: _logDateTime(json['dateTime']),
);
Map<String, dynamic> _$$LogImplToJson(_$LogImpl instance) => <String, dynamic>{
'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,
'Payload': instance.payload,
'dateTime': instance.dateTime,
};
const _$LogLevelEnumMap = {
LogLevel.debug: 'debug',
LogLevel.info: 'info',
LogLevel.warning: 'warning',
LogLevel.error: 'error',
LogLevel.silent: 'silent',
LogLevel.app: 'app',
};
_$DAVImpl _$$DAVImplFromJson(Map<String, dynamic> json) => _$DAVImpl(
uri: json['uri'] as String,
user: json['user'] as String,
@@ -192,3 +198,23 @@ const _$KeyboardModifierEnumMap = {
KeyboardModifier.meta: 'meta',
KeyboardModifier.shift: 'shift',
};
_$TextPainterParamsImpl _$$TextPainterParamsImplFromJson(
Map<String, dynamic> json) =>
_$TextPainterParamsImpl(
text: json['text'] as String?,
fontSize: (json['fontSize'] as num?)?.toDouble(),
textScaleFactor: (json['textScaleFactor'] as num).toDouble(),
maxWidth: (json['maxWidth'] as num?)?.toDouble() ?? double.infinity,
maxLines: (json['maxLines'] as num?)?.toInt(),
);
Map<String, dynamic> _$$TextPainterParamsImplToJson(
_$TextPainterParamsImpl instance) =>
<String, dynamic>{
'text': instance.text,
'fontSize': instance.fontSize,
'textScaleFactor': instance.textScaleFactor,
'maxWidth': instance.maxWidth,
'maxLines': instance.maxLines,
};

View File

@@ -37,6 +37,8 @@ mixin _$AppSettingProps {
bool get disclaimerAccepted => throw _privateConstructorUsedError;
bool get minimizeOnExit => throw _privateConstructorUsedError;
bool get hidden => throw _privateConstructorUsedError;
bool get developerMode => throw _privateConstructorUsedError;
RecoveryStrategy get recoveryStrategy => throw _privateConstructorUsedError;
/// Serializes this AppSettingProps to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -70,7 +72,9 @@ abstract class $AppSettingPropsCopyWith<$Res> {
bool showLabel,
bool disclaimerAccepted,
bool minimizeOnExit,
bool hidden});
bool hidden,
bool developerMode,
RecoveryStrategy recoveryStrategy});
}
/// @nodoc
@@ -103,6 +107,8 @@ class _$AppSettingPropsCopyWithImpl<$Res, $Val extends AppSettingProps>
Object? disclaimerAccepted = null,
Object? minimizeOnExit = null,
Object? hidden = null,
Object? developerMode = null,
Object? recoveryStrategy = null,
}) {
return _then(_value.copyWith(
locale: freezed == locale
@@ -165,6 +171,14 @@ class _$AppSettingPropsCopyWithImpl<$Res, $Val extends AppSettingProps>
? _value.hidden
: hidden // ignore: cast_nullable_to_non_nullable
as bool,
developerMode: null == developerMode
? _value.developerMode
: developerMode // ignore: cast_nullable_to_non_nullable
as bool,
recoveryStrategy: null == recoveryStrategy
? _value.recoveryStrategy
: recoveryStrategy // ignore: cast_nullable_to_non_nullable
as RecoveryStrategy,
) as $Val);
}
}
@@ -193,7 +207,9 @@ abstract class _$$AppSettingPropsImplCopyWith<$Res>
bool showLabel,
bool disclaimerAccepted,
bool minimizeOnExit,
bool hidden});
bool hidden,
bool developerMode,
RecoveryStrategy recoveryStrategy});
}
/// @nodoc
@@ -224,6 +240,8 @@ class __$$AppSettingPropsImplCopyWithImpl<$Res>
Object? disclaimerAccepted = null,
Object? minimizeOnExit = null,
Object? hidden = null,
Object? developerMode = null,
Object? recoveryStrategy = null,
}) {
return _then(_$AppSettingPropsImpl(
locale: freezed == locale
@@ -286,6 +304,14 @@ class __$$AppSettingPropsImplCopyWithImpl<$Res>
? _value.hidden
: hidden // ignore: cast_nullable_to_non_nullable
as bool,
developerMode: null == developerMode
? _value.developerMode
: developerMode // ignore: cast_nullable_to_non_nullable
as bool,
recoveryStrategy: null == recoveryStrategy
? _value.recoveryStrategy
: recoveryStrategy // ignore: cast_nullable_to_non_nullable
as RecoveryStrategy,
));
}
}
@@ -301,7 +327,7 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
this.autoLaunch = false,
this.silentLaunch = false,
this.autoRun = false,
this.openLogs = true,
this.openLogs = false,
this.closeConnections = true,
this.testUrl = defaultTestUrl,
this.isAnimateToPage = true,
@@ -309,7 +335,9 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
this.showLabel = false,
this.disclaimerAccepted = false,
this.minimizeOnExit = true,
this.hidden = false})
this.hidden = false,
this.developerMode = false,
this.recoveryStrategy = RecoveryStrategy.compatible})
: _dashboardWidgets = dashboardWidgets;
factory _$AppSettingPropsImpl.fromJson(Map<String, dynamic> json) =>
@@ -366,10 +394,16 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
@override
@JsonKey()
final bool hidden;
@override
@JsonKey()
final bool developerMode;
@override
@JsonKey()
final RecoveryStrategy recoveryStrategy;
@override
String toString() {
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden)';
return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, recoveryStrategy: $recoveryStrategy)';
}
@override
@@ -402,7 +436,11 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
other.disclaimerAccepted == disclaimerAccepted) &&
(identical(other.minimizeOnExit, minimizeOnExit) ||
other.minimizeOnExit == minimizeOnExit) &&
(identical(other.hidden, hidden) || other.hidden == hidden));
(identical(other.hidden, hidden) || other.hidden == hidden) &&
(identical(other.developerMode, developerMode) ||
other.developerMode == developerMode) &&
(identical(other.recoveryStrategy, recoveryStrategy) ||
other.recoveryStrategy == recoveryStrategy));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -423,7 +461,9 @@ class _$AppSettingPropsImpl implements _AppSettingProps {
showLabel,
disclaimerAccepted,
minimizeOnExit,
hidden);
hidden,
developerMode,
recoveryStrategy);
/// Create a copy of AppSettingProps
/// with the given fields replaced by the non-null parameter values.
@@ -459,7 +499,9 @@ abstract class _AppSettingProps implements AppSettingProps {
final bool showLabel,
final bool disclaimerAccepted,
final bool minimizeOnExit,
final bool hidden}) = _$AppSettingPropsImpl;
final bool hidden,
final bool developerMode,
final RecoveryStrategy recoveryStrategy}) = _$AppSettingPropsImpl;
factory _AppSettingProps.fromJson(Map<String, dynamic> json) =
_$AppSettingPropsImpl.fromJson;
@@ -495,6 +537,10 @@ abstract class _AppSettingProps implements AppSettingProps {
bool get minimizeOnExit;
@override
bool get hidden;
@override
bool get developerMode;
@override
RecoveryStrategy get recoveryStrategy;
/// Create a copy of AppSettingProps
/// with the given fields replaced by the non-null parameter values.
@@ -516,6 +562,7 @@ mixin _$AccessControl {
List<String> get rejectList => throw _privateConstructorUsedError;
AccessSortType get sort => throw _privateConstructorUsedError;
bool get isFilterSystemApp => throw _privateConstructorUsedError;
bool get isFilterNonInternetApp => throw _privateConstructorUsedError;
/// Serializes this AccessControl to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -539,7 +586,8 @@ abstract class $AccessControlCopyWith<$Res> {
List<String> acceptList,
List<String> rejectList,
AccessSortType sort,
bool isFilterSystemApp});
bool isFilterSystemApp,
bool isFilterNonInternetApp});
}
/// @nodoc
@@ -563,6 +611,7 @@ class _$AccessControlCopyWithImpl<$Res, $Val extends AccessControl>
Object? rejectList = null,
Object? sort = null,
Object? isFilterSystemApp = null,
Object? isFilterNonInternetApp = null,
}) {
return _then(_value.copyWith(
enable: null == enable
@@ -589,6 +638,10 @@ class _$AccessControlCopyWithImpl<$Res, $Val extends AccessControl>
? _value.isFilterSystemApp
: isFilterSystemApp // ignore: cast_nullable_to_non_nullable
as bool,
isFilterNonInternetApp: null == isFilterNonInternetApp
? _value.isFilterNonInternetApp
: isFilterNonInternetApp // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@@ -607,7 +660,8 @@ abstract class _$$AccessControlImplCopyWith<$Res>
List<String> acceptList,
List<String> rejectList,
AccessSortType sort,
bool isFilterSystemApp});
bool isFilterSystemApp,
bool isFilterNonInternetApp});
}
/// @nodoc
@@ -629,6 +683,7 @@ class __$$AccessControlImplCopyWithImpl<$Res>
Object? rejectList = null,
Object? sort = null,
Object? isFilterSystemApp = null,
Object? isFilterNonInternetApp = null,
}) {
return _then(_$AccessControlImpl(
enable: null == enable
@@ -655,6 +710,10 @@ class __$$AccessControlImplCopyWithImpl<$Res>
? _value.isFilterSystemApp
: isFilterSystemApp // ignore: cast_nullable_to_non_nullable
as bool,
isFilterNonInternetApp: null == isFilterNonInternetApp
? _value.isFilterNonInternetApp
: isFilterNonInternetApp // ignore: cast_nullable_to_non_nullable
as bool,
));
}
}
@@ -668,7 +727,8 @@ class _$AccessControlImpl implements _AccessControl {
final List<String> acceptList = const [],
final List<String> rejectList = const [],
this.sort = AccessSortType.none,
this.isFilterSystemApp = true})
this.isFilterSystemApp = true,
this.isFilterNonInternetApp = true})
: _acceptList = acceptList,
_rejectList = rejectList;
@@ -705,10 +765,13 @@ class _$AccessControlImpl implements _AccessControl {
@override
@JsonKey()
final bool isFilterSystemApp;
@override
@JsonKey()
final bool isFilterNonInternetApp;
@override
String toString() {
return 'AccessControl(enable: $enable, mode: $mode, acceptList: $acceptList, rejectList: $rejectList, sort: $sort, isFilterSystemApp: $isFilterSystemApp)';
return 'AccessControl(enable: $enable, mode: $mode, acceptList: $acceptList, rejectList: $rejectList, sort: $sort, isFilterSystemApp: $isFilterSystemApp, isFilterNonInternetApp: $isFilterNonInternetApp)';
}
@override
@@ -724,7 +787,9 @@ class _$AccessControlImpl implements _AccessControl {
.equals(other._rejectList, _rejectList) &&
(identical(other.sort, sort) || other.sort == sort) &&
(identical(other.isFilterSystemApp, isFilterSystemApp) ||
other.isFilterSystemApp == isFilterSystemApp));
other.isFilterSystemApp == isFilterSystemApp) &&
(identical(other.isFilterNonInternetApp, isFilterNonInternetApp) ||
other.isFilterNonInternetApp == isFilterNonInternetApp));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -736,7 +801,8 @@ class _$AccessControlImpl implements _AccessControl {
const DeepCollectionEquality().hash(_acceptList),
const DeepCollectionEquality().hash(_rejectList),
sort,
isFilterSystemApp);
isFilterSystemApp,
isFilterNonInternetApp);
/// Create a copy of AccessControl
/// with the given fields replaced by the non-null parameter values.
@@ -761,7 +827,8 @@ abstract class _AccessControl implements AccessControl {
final List<String> acceptList,
final List<String> rejectList,
final AccessSortType sort,
final bool isFilterSystemApp}) = _$AccessControlImpl;
final bool isFilterSystemApp,
final bool isFilterNonInternetApp}) = _$AccessControlImpl;
factory _AccessControl.fromJson(Map<String, dynamic> json) =
_$AccessControlImpl.fromJson;
@@ -778,6 +845,8 @@ abstract class _AccessControl implements AccessControl {
AccessSortType get sort;
@override
bool get isFilterSystemApp;
@override
bool get isFilterNonInternetApp;
/// Create a copy of AccessControl
/// with the given fields replaced by the non-null parameter values.
@@ -1717,6 +1786,170 @@ abstract class _ProxiesStyle implements ProxiesStyle {
throw _privateConstructorUsedError;
}
TextScale _$TextScaleFromJson(Map<String, dynamic> json) {
return _TextScale.fromJson(json);
}
/// @nodoc
mixin _$TextScale {
dynamic get enable => throw _privateConstructorUsedError;
dynamic get scale => throw _privateConstructorUsedError;
/// Serializes this TextScale to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of TextScale
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$TextScaleCopyWith<TextScale> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $TextScaleCopyWith<$Res> {
factory $TextScaleCopyWith(TextScale value, $Res Function(TextScale) then) =
_$TextScaleCopyWithImpl<$Res, TextScale>;
@useResult
$Res call({dynamic enable, dynamic scale});
}
/// @nodoc
class _$TextScaleCopyWithImpl<$Res, $Val extends TextScale>
implements $TextScaleCopyWith<$Res> {
_$TextScaleCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of TextScale
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = freezed,
Object? scale = freezed,
}) {
return _then(_value.copyWith(
enable: freezed == enable
? _value.enable
: enable // ignore: cast_nullable_to_non_nullable
as dynamic,
scale: freezed == scale
? _value.scale
: scale // ignore: cast_nullable_to_non_nullable
as dynamic,
) as $Val);
}
}
/// @nodoc
abstract class _$$TextScaleImplCopyWith<$Res>
implements $TextScaleCopyWith<$Res> {
factory _$$TextScaleImplCopyWith(
_$TextScaleImpl value, $Res Function(_$TextScaleImpl) then) =
__$$TextScaleImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({dynamic enable, dynamic scale});
}
/// @nodoc
class __$$TextScaleImplCopyWithImpl<$Res>
extends _$TextScaleCopyWithImpl<$Res, _$TextScaleImpl>
implements _$$TextScaleImplCopyWith<$Res> {
__$$TextScaleImplCopyWithImpl(
_$TextScaleImpl _value, $Res Function(_$TextScaleImpl) _then)
: super(_value, _then);
/// Create a copy of TextScale
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? enable = freezed,
Object? scale = freezed,
}) {
return _then(_$TextScaleImpl(
enable: freezed == enable ? _value.enable! : enable,
scale: freezed == scale ? _value.scale! : scale,
));
}
}
/// @nodoc
@JsonSerializable()
class _$TextScaleImpl implements _TextScale {
const _$TextScaleImpl({this.enable = false, this.scale = 1.0});
factory _$TextScaleImpl.fromJson(Map<String, dynamic> json) =>
_$$TextScaleImplFromJson(json);
@override
@JsonKey()
final dynamic enable;
@override
@JsonKey()
final dynamic scale;
@override
String toString() {
return 'TextScale(enable: $enable, scale: $scale)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$TextScaleImpl &&
const DeepCollectionEquality().equals(other.enable, enable) &&
const DeepCollectionEquality().equals(other.scale, scale));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(enable),
const DeepCollectionEquality().hash(scale));
/// Create a copy of TextScale
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$TextScaleImplCopyWith<_$TextScaleImpl> get copyWith =>
__$$TextScaleImplCopyWithImpl<_$TextScaleImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$TextScaleImplToJson(
this,
);
}
}
abstract class _TextScale implements TextScale {
const factory _TextScale({final dynamic enable, final dynamic scale}) =
_$TextScaleImpl;
factory _TextScale.fromJson(Map<String, dynamic> json) =
_$TextScaleImpl.fromJson;
@override
dynamic get enable;
@override
dynamic get scale;
/// Create a copy of TextScale
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$TextScaleImplCopyWith<_$TextScaleImpl> get copyWith =>
throw _privateConstructorUsedError;
}
ThemeProps _$ThemePropsFromJson(Map<String, dynamic> json) {
return _ThemeProps.fromJson(json);
}
@@ -1728,6 +1961,7 @@ mixin _$ThemeProps {
ThemeMode get themeMode => throw _privateConstructorUsedError;
DynamicSchemeVariant get schemeVariant => throw _privateConstructorUsedError;
bool get pureBlack => throw _privateConstructorUsedError;
TextScale get textScale => throw _privateConstructorUsedError;
/// Serializes this ThemeProps to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
@@ -1750,7 +1984,10 @@ abstract class $ThemePropsCopyWith<$Res> {
List<int> primaryColors,
ThemeMode themeMode,
DynamicSchemeVariant schemeVariant,
bool pureBlack});
bool pureBlack,
TextScale textScale});
$TextScaleCopyWith<$Res> get textScale;
}
/// @nodoc
@@ -1773,6 +2010,7 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
Object? themeMode = null,
Object? schemeVariant = null,
Object? pureBlack = null,
Object? textScale = null,
}) {
return _then(_value.copyWith(
primaryColor: freezed == primaryColor
@@ -1795,8 +2033,22 @@ class _$ThemePropsCopyWithImpl<$Res, $Val extends ThemeProps>
? _value.pureBlack
: pureBlack // ignore: cast_nullable_to_non_nullable
as bool,
textScale: null == textScale
? _value.textScale
: textScale // ignore: cast_nullable_to_non_nullable
as TextScale,
) as $Val);
}
/// Create a copy of ThemeProps
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$TextScaleCopyWith<$Res> get textScale {
return $TextScaleCopyWith<$Res>(_value.textScale, (value) {
return _then(_value.copyWith(textScale: value) as $Val);
});
}
}
/// @nodoc
@@ -1812,7 +2064,11 @@ abstract class _$$ThemePropsImplCopyWith<$Res>
List<int> primaryColors,
ThemeMode themeMode,
DynamicSchemeVariant schemeVariant,
bool pureBlack});
bool pureBlack,
TextScale textScale});
@override
$TextScaleCopyWith<$Res> get textScale;
}
/// @nodoc
@@ -1833,6 +2089,7 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
Object? themeMode = null,
Object? schemeVariant = null,
Object? pureBlack = null,
Object? textScale = null,
}) {
return _then(_$ThemePropsImpl(
primaryColor: freezed == primaryColor
@@ -1855,6 +2112,10 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
? _value.pureBlack
: pureBlack // ignore: cast_nullable_to_non_nullable
as bool,
textScale: null == textScale
? _value.textScale
: textScale // ignore: cast_nullable_to_non_nullable
as TextScale,
));
}
}
@@ -1863,18 +2124,18 @@ class __$$ThemePropsImplCopyWithImpl<$Res>
@JsonSerializable()
class _$ThemePropsImpl implements _ThemeProps {
const _$ThemePropsImpl(
{this.primaryColor = defaultPrimaryColor,
{this.primaryColor,
final List<int> primaryColors = defaultPrimaryColors,
this.themeMode = ThemeMode.dark,
this.schemeVariant = DynamicSchemeVariant.tonalSpot,
this.pureBlack = false})
this.schemeVariant = DynamicSchemeVariant.content,
this.pureBlack = false,
this.textScale = const TextScale()})
: _primaryColors = primaryColors;
factory _$ThemePropsImpl.fromJson(Map<String, dynamic> json) =>
_$$ThemePropsImplFromJson(json);
@override
@JsonKey()
final int? primaryColor;
final List<int> _primaryColors;
@override
@@ -1894,10 +2155,13 @@ class _$ThemePropsImpl implements _ThemeProps {
@override
@JsonKey()
final bool pureBlack;
@override
@JsonKey()
final TextScale textScale;
@override
String toString() {
return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack)';
return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack, textScale: $textScale)';
}
@override
@@ -1914,7 +2178,9 @@ class _$ThemePropsImpl implements _ThemeProps {
(identical(other.schemeVariant, schemeVariant) ||
other.schemeVariant == schemeVariant) &&
(identical(other.pureBlack, pureBlack) ||
other.pureBlack == pureBlack));
other.pureBlack == pureBlack) &&
(identical(other.textScale, textScale) ||
other.textScale == textScale));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -1925,7 +2191,8 @@ class _$ThemePropsImpl implements _ThemeProps {
const DeepCollectionEquality().hash(_primaryColors),
themeMode,
schemeVariant,
pureBlack);
pureBlack,
textScale);
/// Create a copy of ThemeProps
/// with the given fields replaced by the non-null parameter values.
@@ -1949,7 +2216,8 @@ abstract class _ThemeProps implements ThemeProps {
final List<int> primaryColors,
final ThemeMode themeMode,
final DynamicSchemeVariant schemeVariant,
final bool pureBlack}) = _$ThemePropsImpl;
final bool pureBlack,
final TextScale textScale}) = _$ThemePropsImpl;
factory _ThemeProps.fromJson(Map<String, dynamic> json) =
_$ThemePropsImpl.fromJson;
@@ -1964,6 +2232,8 @@ abstract class _ThemeProps implements ThemeProps {
DynamicSchemeVariant get schemeVariant;
@override
bool get pureBlack;
@override
TextScale get textScale;
/// Create a copy of ThemeProps
/// with the given fields replaced by the non-null parameter values.
@@ -1988,6 +2258,7 @@ mixin _$Config {
DAV? get dav => throw _privateConstructorUsedError;
NetworkProps get networkProps => throw _privateConstructorUsedError;
VpnProps get vpnProps => throw _privateConstructorUsedError;
@JsonKey(fromJson: ThemeProps.safeFromJson)
ThemeProps get themeProps => throw _privateConstructorUsedError;
ProxiesStyle get proxiesStyle => throw _privateConstructorUsedError;
WindowProps get windowProps => throw _privateConstructorUsedError;
@@ -2017,7 +2288,7 @@ abstract class $ConfigCopyWith<$Res> {
DAV? dav,
NetworkProps networkProps,
VpnProps vpnProps,
ThemeProps themeProps,
@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps,
ProxiesStyle proxiesStyle,
WindowProps windowProps,
ClashConfig patchClashConfig});
@@ -2214,7 +2485,7 @@ abstract class _$$ConfigImplCopyWith<$Res> implements $ConfigCopyWith<$Res> {
DAV? dav,
NetworkProps networkProps,
VpnProps vpnProps,
ThemeProps themeProps,
@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps,
ProxiesStyle proxiesStyle,
WindowProps windowProps,
ClashConfig patchClashConfig});
@@ -2329,7 +2600,7 @@ class _$ConfigImpl implements _Config {
this.dav,
this.networkProps = defaultNetworkProps,
this.vpnProps = defaultVpnProps,
this.themeProps = defaultThemeProps,
@JsonKey(fromJson: ThemeProps.safeFromJson) required this.themeProps,
this.proxiesStyle = defaultProxiesStyle,
this.windowProps = defaultWindowProps,
this.patchClashConfig = defaultClashConfig})
@@ -2374,7 +2645,7 @@ class _$ConfigImpl implements _Config {
@JsonKey()
final VpnProps vpnProps;
@override
@JsonKey()
@JsonKey(fromJson: ThemeProps.safeFromJson)
final ThemeProps themeProps;
@override
@JsonKey()
@@ -2464,7 +2735,8 @@ abstract class _Config implements Config {
final DAV? dav,
final NetworkProps networkProps,
final VpnProps vpnProps,
final ThemeProps themeProps,
@JsonKey(fromJson: ThemeProps.safeFromJson)
required final ThemeProps themeProps,
final ProxiesStyle proxiesStyle,
final WindowProps windowProps,
final ClashConfig patchClashConfig}) = _$ConfigImpl;
@@ -2489,6 +2761,7 @@ abstract class _Config implements Config {
@override
VpnProps get vpnProps;
@override
@JsonKey(fromJson: ThemeProps.safeFromJson)
ThemeProps get themeProps;
@override
ProxiesStyle get proxiesStyle;

View File

@@ -17,7 +17,7 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
autoLaunch: json['autoLaunch'] as bool? ?? false,
silentLaunch: json['silentLaunch'] as bool? ?? false,
autoRun: json['autoRun'] as bool? ?? false,
openLogs: json['openLogs'] as bool? ?? true,
openLogs: json['openLogs'] as bool? ?? false,
closeConnections: json['closeConnections'] as bool? ?? true,
testUrl: json['testUrl'] as String? ?? defaultTestUrl,
isAnimateToPage: json['isAnimateToPage'] as bool? ?? true,
@@ -26,6 +26,10 @@ _$AppSettingPropsImpl _$$AppSettingPropsImplFromJson(
disclaimerAccepted: json['disclaimerAccepted'] as bool? ?? false,
minimizeOnExit: json['minimizeOnExit'] as bool? ?? true,
hidden: json['hidden'] as bool? ?? false,
developerMode: json['developerMode'] as bool? ?? false,
recoveryStrategy: $enumDecodeNullable(
_$RecoveryStrategyEnumMap, json['recoveryStrategy']) ??
RecoveryStrategy.compatible,
);
Map<String, dynamic> _$$AppSettingPropsImplToJson(
@@ -48,14 +52,23 @@ Map<String, dynamic> _$$AppSettingPropsImplToJson(
'disclaimerAccepted': instance.disclaimerAccepted,
'minimizeOnExit': instance.minimizeOnExit,
'hidden': instance.hidden,
'developerMode': instance.developerMode,
'recoveryStrategy': _$RecoveryStrategyEnumMap[instance.recoveryStrategy]!,
};
const _$RecoveryStrategyEnumMap = {
RecoveryStrategy.compatible: 'compatible',
RecoveryStrategy.override: 'override',
};
const _$DashboardWidgetEnumMap = {
DashboardWidget.networkSpeed: 'networkSpeed',
DashboardWidget.outboundModeV2: 'outboundModeV2',
DashboardWidget.outboundMode: 'outboundMode',
DashboardWidget.trafficUsage: 'trafficUsage',
DashboardWidget.networkDetection: 'networkDetection',
DashboardWidget.tunButton: 'tunButton',
DashboardWidget.vpnButton: 'vpnButton',
DashboardWidget.systemProxyButton: 'systemProxyButton',
DashboardWidget.intranetIp: 'intranetIp',
DashboardWidget.memoryInfo: 'memoryInfo',
@@ -77,6 +90,7 @@ _$AccessControlImpl _$$AccessControlImplFromJson(Map<String, dynamic> json) =>
sort: $enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??
AccessSortType.none,
isFilterSystemApp: json['isFilterSystemApp'] as bool? ?? true,
isFilterNonInternetApp: json['isFilterNonInternetApp'] as bool? ?? true,
);
Map<String, dynamic> _$$AccessControlImplToJson(_$AccessControlImpl instance) =>
@@ -87,6 +101,7 @@ Map<String, dynamic> _$$AccessControlImplToJson(_$AccessControlImpl instance) =>
'rejectList': instance.rejectList,
'sort': _$AccessSortTypeEnumMap[instance.sort]!,
'isFilterSystemApp': instance.isFilterSystemApp,
'isFilterNonInternetApp': instance.isFilterNonInternetApp,
};
const _$AccessControlModeEnumMap = {
@@ -219,10 +234,21 @@ const _$ProxyCardTypeEnumMap = {
ProxyCardType.min: 'min',
};
_$TextScaleImpl _$$TextScaleImplFromJson(Map<String, dynamic> json) =>
_$TextScaleImpl(
enable: json['enable'] ?? false,
scale: json['scale'] ?? 1.0,
);
Map<String, dynamic> _$$TextScaleImplToJson(_$TextScaleImpl instance) =>
<String, dynamic>{
'enable': instance.enable,
'scale': instance.scale,
};
_$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
_$ThemePropsImpl(
primaryColor:
(json['primaryColor'] as num?)?.toInt() ?? defaultPrimaryColor,
primaryColor: (json['primaryColor'] as num?)?.toInt(),
primaryColors: (json['primaryColors'] as List<dynamic>?)
?.map((e) => (e as num).toInt())
.toList() ??
@@ -231,8 +257,11 @@ _$ThemePropsImpl _$$ThemePropsImplFromJson(Map<String, dynamic> json) =>
ThemeMode.dark,
schemeVariant: $enumDecodeNullable(
_$DynamicSchemeVariantEnumMap, json['schemeVariant']) ??
DynamicSchemeVariant.tonalSpot,
DynamicSchemeVariant.content,
pureBlack: json['pureBlack'] as bool? ?? false,
textScale: json['textScale'] == null
? const TextScale()
: TextScale.fromJson(json['textScale'] as Map<String, dynamic>),
);
Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
@@ -242,6 +271,7 @@ Map<String, dynamic> _$$ThemePropsImplToJson(_$ThemePropsImpl instance) =>
'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,
'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!,
'pureBlack': instance.pureBlack,
'textScale': instance.textScale,
};
const _$ThemeModeEnumMap = {
@@ -287,9 +317,8 @@ _$ConfigImpl _$$ConfigImplFromJson(Map<String, dynamic> json) => _$ConfigImpl(
vpnProps: json['vpnProps'] == null
? defaultVpnProps
: VpnProps.fromJson(json['vpnProps'] as Map<String, dynamic>?),
themeProps: json['themeProps'] == null
? defaultThemeProps
: ThemeProps.fromJson(json['themeProps'] as Map<String, dynamic>?),
themeProps:
ThemeProps.safeFromJson(json['themeProps'] as Map<String, Object?>?),
proxiesStyle: json['proxiesStyle'] == null
? defaultProxiesStyle
: ProxiesStyle.fromJson(

View File

@@ -345,6 +345,7 @@ const _$ActionMethodEnumMap = {
ActionMethod.getCountryCode: 'getCountryCode',
ActionMethod.getMemory: 'getMemory',
ActionMethod.getProfile: 'getProfile',
ActionMethod.crash: 'crash',
ActionMethod.setFdMap: 'setFdMap',
ActionMethod.setProcessMap: 'setProcessMap',
ActionMethod.setState: 'setState',

View File

@@ -327,6 +327,193 @@ abstract class _VM3<A, B, C> implements VM3<A, B, C> {
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$VM4<A, B, C, D> {
A get a => throw _privateConstructorUsedError;
B get b => throw _privateConstructorUsedError;
C get c => throw _privateConstructorUsedError;
D get d => throw _privateConstructorUsedError;
/// Create a copy of VM4
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$VM4CopyWith<A, B, C, D, VM4<A, B, C, D>> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $VM4CopyWith<A, B, C, D, $Res> {
factory $VM4CopyWith(
VM4<A, B, C, D> value, $Res Function(VM4<A, B, C, D>) then) =
_$VM4CopyWithImpl<A, B, C, D, $Res, VM4<A, B, C, D>>;
@useResult
$Res call({A a, B b, C c, D d});
}
/// @nodoc
class _$VM4CopyWithImpl<A, B, C, D, $Res, $Val extends VM4<A, B, C, D>>
implements $VM4CopyWith<A, B, C, D, $Res> {
_$VM4CopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of VM4
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? a = freezed,
Object? b = freezed,
Object? c = freezed,
Object? d = freezed,
}) {
return _then(_value.copyWith(
a: freezed == a
? _value.a
: a // ignore: cast_nullable_to_non_nullable
as A,
b: freezed == b
? _value.b
: b // ignore: cast_nullable_to_non_nullable
as B,
c: freezed == c
? _value.c
: c // ignore: cast_nullable_to_non_nullable
as C,
d: freezed == d
? _value.d
: d // ignore: cast_nullable_to_non_nullable
as D,
) as $Val);
}
}
/// @nodoc
abstract class _$$VM4ImplCopyWith<A, B, C, D, $Res>
implements $VM4CopyWith<A, B, C, D, $Res> {
factory _$$VM4ImplCopyWith(_$VM4Impl<A, B, C, D> value,
$Res Function(_$VM4Impl<A, B, C, D>) then) =
__$$VM4ImplCopyWithImpl<A, B, C, D, $Res>;
@override
@useResult
$Res call({A a, B b, C c, D d});
}
/// @nodoc
class __$$VM4ImplCopyWithImpl<A, B, C, D, $Res>
extends _$VM4CopyWithImpl<A, B, C, D, $Res, _$VM4Impl<A, B, C, D>>
implements _$$VM4ImplCopyWith<A, B, C, D, $Res> {
__$$VM4ImplCopyWithImpl(
_$VM4Impl<A, B, C, D> _value, $Res Function(_$VM4Impl<A, B, C, D>) _then)
: super(_value, _then);
/// Create a copy of VM4
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? a = freezed,
Object? b = freezed,
Object? c = freezed,
Object? d = freezed,
}) {
return _then(_$VM4Impl<A, B, C, D>(
a: freezed == a
? _value.a
: a // ignore: cast_nullable_to_non_nullable
as A,
b: freezed == b
? _value.b
: b // ignore: cast_nullable_to_non_nullable
as B,
c: freezed == c
? _value.c
: c // ignore: cast_nullable_to_non_nullable
as C,
d: freezed == d
? _value.d
: d // ignore: cast_nullable_to_non_nullable
as D,
));
}
}
/// @nodoc
class _$VM4Impl<A, B, C, D> implements _VM4<A, B, C, D> {
const _$VM4Impl(
{required this.a, required this.b, required this.c, required this.d});
@override
final A a;
@override
final B b;
@override
final C c;
@override
final D d;
@override
String toString() {
return 'VM4<$A, $B, $C, $D>(a: $a, b: $b, c: $c, d: $d)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$VM4Impl<A, B, C, D> &&
const DeepCollectionEquality().equals(other.a, a) &&
const DeepCollectionEquality().equals(other.b, b) &&
const DeepCollectionEquality().equals(other.c, c) &&
const DeepCollectionEquality().equals(other.d, d));
}
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(a),
const DeepCollectionEquality().hash(b),
const DeepCollectionEquality().hash(c),
const DeepCollectionEquality().hash(d));
/// Create a copy of VM4
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$VM4ImplCopyWith<A, B, C, D, _$VM4Impl<A, B, C, D>> get copyWith =>
__$$VM4ImplCopyWithImpl<A, B, C, D, _$VM4Impl<A, B, C, D>>(
this, _$identity);
}
abstract class _VM4<A, B, C, D> implements VM4<A, B, C, D> {
const factory _VM4(
{required final A a,
required final B b,
required final C c,
required final D d}) = _$VM4Impl<A, B, C, D>;
@override
A get a;
@override
B get b;
@override
C get c;
@override
D get d;
/// Create a copy of VM4
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$VM4ImplCopyWith<A, B, C, D, _$VM4Impl<A, B, C, D>> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$StartButtonSelectorState {
bool get isInit => throw _privateConstructorUsedError;
@@ -3335,6 +3522,7 @@ mixin _$ClashConfigState {
bool get overrideDns => throw _privateConstructorUsedError;
ClashConfig get clashConfig => throw _privateConstructorUsedError;
OverrideData get overrideData => throw _privateConstructorUsedError;
RouteMode get routeMode => throw _privateConstructorUsedError;
/// Create a copy of ClashConfigState
/// with the given fields replaced by the non-null parameter values.
@@ -3350,7 +3538,10 @@ abstract class $ClashConfigStateCopyWith<$Res> {
_$ClashConfigStateCopyWithImpl<$Res, ClashConfigState>;
@useResult
$Res call(
{bool overrideDns, ClashConfig clashConfig, OverrideData overrideData});
{bool overrideDns,
ClashConfig clashConfig,
OverrideData overrideData,
RouteMode routeMode});
$ClashConfigCopyWith<$Res> get clashConfig;
$OverrideDataCopyWith<$Res> get overrideData;
@@ -3374,6 +3565,7 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
Object? overrideDns = null,
Object? clashConfig = null,
Object? overrideData = null,
Object? routeMode = null,
}) {
return _then(_value.copyWith(
overrideDns: null == overrideDns
@@ -3388,6 +3580,10 @@ class _$ClashConfigStateCopyWithImpl<$Res, $Val extends ClashConfigState>
? _value.overrideData
: overrideData // ignore: cast_nullable_to_non_nullable
as OverrideData,
routeMode: null == routeMode
? _value.routeMode
: routeMode // ignore: cast_nullable_to_non_nullable
as RouteMode,
) as $Val);
}
@@ -3421,7 +3617,10 @@ abstract class _$$ClashConfigStateImplCopyWith<$Res>
@override
@useResult
$Res call(
{bool overrideDns, ClashConfig clashConfig, OverrideData overrideData});
{bool overrideDns,
ClashConfig clashConfig,
OverrideData overrideData,
RouteMode routeMode});
@override
$ClashConfigCopyWith<$Res> get clashConfig;
@@ -3445,6 +3644,7 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res>
Object? overrideDns = null,
Object? clashConfig = null,
Object? overrideData = null,
Object? routeMode = null,
}) {
return _then(_$ClashConfigStateImpl(
overrideDns: null == overrideDns
@@ -3459,6 +3659,10 @@ class __$$ClashConfigStateImplCopyWithImpl<$Res>
? _value.overrideData
: overrideData // ignore: cast_nullable_to_non_nullable
as OverrideData,
routeMode: null == routeMode
? _value.routeMode
: routeMode // ignore: cast_nullable_to_non_nullable
as RouteMode,
));
}
}
@@ -3469,7 +3673,8 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
const _$ClashConfigStateImpl(
{required this.overrideDns,
required this.clashConfig,
required this.overrideData});
required this.overrideData,
required this.routeMode});
@override
final bool overrideDns;
@@ -3477,10 +3682,12 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
final ClashConfig clashConfig;
@override
final OverrideData overrideData;
@override
final RouteMode routeMode;
@override
String toString() {
return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData)';
return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData, routeMode: $routeMode)';
}
@override
@@ -3493,12 +3700,14 @@ class _$ClashConfigStateImpl implements _ClashConfigState {
(identical(other.clashConfig, clashConfig) ||
other.clashConfig == clashConfig) &&
(identical(other.overrideData, overrideData) ||
other.overrideData == overrideData));
other.overrideData == overrideData) &&
(identical(other.routeMode, routeMode) ||
other.routeMode == routeMode));
}
@override
int get hashCode =>
Object.hash(runtimeType, overrideDns, clashConfig, overrideData);
int get hashCode => Object.hash(
runtimeType, overrideDns, clashConfig, overrideData, routeMode);
/// Create a copy of ClashConfigState
/// with the given fields replaced by the non-null parameter values.
@@ -3514,7 +3723,8 @@ abstract class _ClashConfigState implements ClashConfigState {
const factory _ClashConfigState(
{required final bool overrideDns,
required final ClashConfig clashConfig,
required final OverrideData overrideData}) = _$ClashConfigStateImpl;
required final OverrideData overrideData,
required final RouteMode routeMode}) = _$ClashConfigStateImpl;
@override
bool get overrideDns;
@@ -3522,6 +3732,8 @@ abstract class _ClashConfigState implements ClashConfigState {
ClashConfig get clashConfig;
@override
OverrideData get overrideData;
@override
RouteMode get routeMode;
/// Create a copy of ClashConfigState
/// with the given fields replaced by the non-null parameter values.

View File

@@ -24,6 +24,17 @@ class VM3<A, B, C> with _$VM3<A, B, C> {
}) = _VM3;
}
@freezed
class VM4<A, B, C,D> with _$VM4<A, B, C,D> {
const factory VM4({
required A a,
required B b,
required C c,
required D d,
}) = _VM4;
}
@freezed
class StartButtonSelectorState with _$StartButtonSelectorState {
const factory StartButtonSelectorState({
@@ -146,12 +157,21 @@ class PackageListSelectorState with _$PackageListSelectorState {
}
extension PackageListSelectorStateExt on PackageListSelectorState {
List<Package> getList(List<String> selectedList) {
List<Package> get list {
final isFilterSystemApp = accessControl.isFilterSystemApp;
final sort = accessControl.sort;
final isFilterNonInternetApp = accessControl.isFilterNonInternetApp;
return packages
.where((item) => isFilterSystemApp ? item.isSystem == false : true)
.sorted(
.where(
(item) =>
(isFilterSystemApp ? item.system == false : true) &&
(isFilterNonInternetApp ? item.internet == true : true),
)
.toList();
}
List<Package> getSortList(List<String> selectedList) {
final sort = accessControl.sort;
return list.sorted(
(a, b) {
return switch (sort) {
AccessSortType.none => 0,
@@ -208,6 +228,7 @@ class ClashConfigState with _$ClashConfigState {
required bool overrideDns,
required ClashConfig clashConfig,
required OverrideData overrideData,
required RouteMode routeMode,
}) = _ClashConfigState;
}

View File

@@ -127,6 +127,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
);
},
popup: CommonPopupMenu(
minWidth: 180,
items: [
PopupMenuItemData(
icon: Icons.search,
@@ -189,7 +190,7 @@ class _EditorPageState extends ConsumerState<EditorPage> {
shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),
controller: _controller,
style: CodeEditorStyle(
fontSize: 14,
fontSize: 14.ap,
fontFamily: FontFamily.jetBrainsMono.value,
codeTheme: CodeHighlightTheme(
languages: {

View File

@@ -129,6 +129,16 @@ class _HomePageViewState extends ConsumerState<_HomePageView> {
controller: _pageController,
physics: const NeverScrollableScrollPhysics(),
itemCount: navigationItems.length,
// onPageChanged: (index) {
// debouncer.call(DebounceTag.pageChange, () {
// WidgetsBinding.instance.addPostFrameCallback((_) {
// if (_pageIndex != index) {
// final pageLabel = navigationItems[index].label;
// _toPage(pageLabel, true);
// }
// });
// });
// },
itemBuilder: (_, index) {
final navigationItem = navigationItems[index];
return KeepScope(
@@ -180,43 +190,46 @@ class CommonNavigationBar extends ConsumerWidget {
child: Column(
children: [
Expanded(
child: SingleChildScrollView(
child: IntrinsicHeight(
child: NavigationRail(
backgroundColor: context.colorScheme.surfaceContainer,
selectedIconTheme: IconThemeData(
color: context.colorScheme.onSurfaceVariant,
),
unselectedIconTheme: IconThemeData(
color: context.colorScheme.onSurfaceVariant,
),
selectedLabelTextStyle:
context.textTheme.labelLarge!.copyWith(
color: context.colorScheme.onSurface,
),
unselectedLabelTextStyle:
context.textTheme.labelLarge!.copyWith(
color: context.colorScheme.onSurface,
),
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(
Intl.message(e.label.name),
child: ScrollConfiguration(
behavior: HiddenBarScrollBehavior(),
child: SingleChildScrollView(
child: IntrinsicHeight(
child: NavigationRail(
backgroundColor: context.colorScheme.surfaceContainer,
selectedIconTheme: IconThemeData(
color: context.colorScheme.onSurfaceVariant,
),
unselectedIconTheme: IconThemeData(
color: context.colorScheme.onSurfaceVariant,
),
selectedLabelTextStyle:
context.textTheme.labelLarge!.copyWith(
color: context.colorScheme.onSurface,
),
unselectedLabelTextStyle:
context.textTheme.labelLarge!.copyWith(
color: context.colorScheme.onSurface,
),
destinations: navigationItems
.map(
(e) => NavigationRailDestination(
icon: e.icon,
label: Text(
Intl.message(e.label.name),
),
),
),
)
.toList(),
onDestinationSelected: (index) {
globalState.appController
.toPage(navigationItems[index].label);
},
extended: false,
selectedIndex: currentIndex,
labelType: showLabel
? NavigationRailLabelType.all
: NavigationRailLabelType.none,
)
.toList(),
onDestinationSelected: (index) {
globalState.appController
.toPage(navigationItems[index].label);
},
extended: false,
selectedIndex: currentIndex,
labelType: showLabel
? NavigationRailLabelType.all
: NavigationRailLabelType.none,
),
),
),
),

View File

@@ -252,21 +252,6 @@ class CurrentPageLabel extends _$CurrentPageLabel
}
}
@riverpod
class AppSchemes extends _$AppSchemes with AutoDisposeNotifierMixin {
@override
ColorSchemes build() {
return globalState.appState.colorSchemes;
}
@override
onUpdate(value) {
globalState.appState = globalState.appState.copyWith(
colorSchemes: value,
);
}
}
@riverpod
class SortNum extends _$SortNum with AutoDisposeNotifierMixin {
@override

View File

@@ -247,21 +247,6 @@ final currentPageLabelProvider =
);
typedef _$CurrentPageLabel = AutoDisposeNotifier<PageLabel>;
String _$appSchemesHash() => r'748f48f23539a879a92f318a21e1266b1df56aae';
/// See also [AppSchemes].
@ProviderFor(AppSchemes)
final appSchemesProvider =
AutoDisposeNotifierProvider<AppSchemes, ColorSchemes>.internal(
AppSchemes.new,
name: r'appSchemesProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$appSchemesHash,
dependencies: null,
allTransitiveDependencies: null,
);
typedef _$AppSchemes = AutoDisposeNotifier<ColorSchemes>;
String _$sortNumHash() => r'0f85ebbc77124020eaccf988c6ac9d86a7f34d7e';
/// See also [SortNum].

View File

@@ -78,7 +78,7 @@ 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'848f6b2f734d99fb11ec05f73d614be415e9658f';
String _$clashConfigStateHash() => r'fbbcd7221b0b9b18db523e59c9021e8e56e119ca';
/// See also [clashConfigState].
@ProviderFor(clashConfigState)
@@ -1765,7 +1765,23 @@ class _GetProfileOverrideDataProviderElement
String get profileId => (origin as GetProfileOverrideDataProvider).profileId;
}
String _$genColorSchemeHash() => r'a27ccae9b5c11d47cd46804f42f8e9dc7946a6c2';
String _$layoutChangeHash() => r'f25182e1dfaf3c70000404d7635bb1e1db09efbb';
/// See also [layoutChange].
@ProviderFor(layoutChange)
final layoutChangeProvider = AutoDisposeProvider<VM2?>.internal(
layoutChange,
name: r'layoutChangeProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product') ? null : _$layoutChangeHash,
dependencies: null,
allTransitiveDependencies: null,
);
@Deprecated('Will be removed in 3.0. Use Ref instead')
// ignore: unused_element
typedef LayoutChangeRef = AutoDisposeProviderRef<VM2?>;
String _$genColorSchemeHash() => r'b18f15c938a8132ee4ed02cdfc02f3b9f01724e2';
/// See also [genColorScheme].
@ProviderFor(genColorScheme)
@@ -1780,12 +1796,12 @@ class GenColorSchemeFamily extends Family<ColorScheme> {
GenColorSchemeProvider call(
Brightness brightness, {
Color? color,
bool isOverride = false,
bool ignoreConfig = false,
}) {
return GenColorSchemeProvider(
brightness,
color: color,
isOverride: isOverride,
ignoreConfig: ignoreConfig,
);
}
@@ -1796,7 +1812,7 @@ class GenColorSchemeFamily extends Family<ColorScheme> {
return call(
provider.brightness,
color: provider.color,
isOverride: provider.isOverride,
ignoreConfig: provider.ignoreConfig,
);
}
@@ -1821,13 +1837,13 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
GenColorSchemeProvider(
Brightness brightness, {
Color? color,
bool isOverride = false,
bool ignoreConfig = false,
}) : this._internal(
(ref) => genColorScheme(
ref as GenColorSchemeRef,
brightness,
color: color,
isOverride: isOverride,
ignoreConfig: ignoreConfig,
),
from: genColorSchemeProvider,
name: r'genColorSchemeProvider',
@@ -1840,7 +1856,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
GenColorSchemeFamily._allTransitiveDependencies,
brightness: brightness,
color: color,
isOverride: isOverride,
ignoreConfig: ignoreConfig,
);
GenColorSchemeProvider._internal(
@@ -1852,12 +1868,12 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
required super.from,
required this.brightness,
required this.color,
required this.isOverride,
required this.ignoreConfig,
}) : super.internal();
final Brightness brightness;
final Color? color;
final bool isOverride;
final bool ignoreConfig;
@override
Override overrideWith(
@@ -1874,7 +1890,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
debugGetCreateSourceHash: null,
brightness: brightness,
color: color,
isOverride: isOverride,
ignoreConfig: ignoreConfig,
),
);
}
@@ -1889,7 +1905,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
return other is GenColorSchemeProvider &&
other.brightness == brightness &&
other.color == color &&
other.isOverride == isOverride;
other.ignoreConfig == ignoreConfig;
}
@override
@@ -1897,7 +1913,7 @@ class GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {
var hash = _SystemHash.combine(0, runtimeType.hashCode);
hash = _SystemHash.combine(hash, brightness.hashCode);
hash = _SystemHash.combine(hash, color.hashCode);
hash = _SystemHash.combine(hash, isOverride.hashCode);
hash = _SystemHash.combine(hash, ignoreConfig.hashCode);
return _SystemHash.finish(hash);
}
@@ -1912,8 +1928,8 @@ mixin GenColorSchemeRef on AutoDisposeProviderRef<ColorScheme> {
/// The parameter `color` of this provider.
Color? get color;
/// The parameter `isOverride` of this provider.
bool get isOverride;
/// The parameter `ignoreConfig` of this provider.
bool get ignoreConfig;
}
class _GenColorSchemeProviderElement
@@ -1925,7 +1941,7 @@ class _GenColorSchemeProviderElement
@override
Color? get color => (origin as GenColorSchemeProvider).color;
@override
bool get isOverride => (origin as GenColorSchemeProvider).isOverride;
bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;
}
String _$profileOverrideStateHash() =>

View File

@@ -1,6 +1,8 @@
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/models/models.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';
@@ -73,13 +75,15 @@ CoreState coreState(Ref ref) {
ClashConfigState clashConfigState(Ref ref) {
final clashConfig = ref.watch(patchClashConfigProvider);
final overrideDns = ref.watch(overrideDnsProvider);
final overrideData = ref.watch(currentProfileProvider.select(
(state) => state?.overrideData,
));
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,
);
}
@@ -506,12 +510,23 @@ OverrideData? getProfileOverrideData(Ref ref, String profileId) {
);
}
@riverpod
VM2? layoutChange(Ref ref) {
final viewWidth = ref.watch(viewWidthProvider);
final textScale =
ref.watch(themeSettingProvider.select((state) => state.textScale));
return VM2(
a: viewWidth,
b: textScale,
);
}
@riverpod
ColorScheme genColorScheme(
Ref ref,
Brightness brightness, {
Color? color,
bool isOverride = false,
bool ignoreConfig = false,
}) {
final vm2 = ref.watch(
themeSettingProvider.select(
@@ -521,11 +536,17 @@ ColorScheme genColorScheme(
),
),
);
if (color == null && (isOverride == true || vm2.a == null)) {
final colorSchemes = ref.watch(appSchemesProvider);
return colorSchemes.getColorSchemeForBrightness(
brightness,
vm2.b,
if (color == null && (ignoreConfig == true || vm2.a == null)) {
// if (globalState.corePalette != null) {
// return globalState.corePalette!.toColorScheme(brightness: brightness);
// }
return ColorScheme.fromSeed(
seedColor: globalState.corePalette
?.toColorScheme(brightness: brightness)
.primary ??
globalState.accentColor,
brightness: brightness,
dynamicSchemeVariant: vm2.b,
);
}
return ColorScheme.fromSeed(

View File

@@ -1,6 +1,6 @@
import 'dart:async';
import 'package:animations/animations.dart';
import 'package:dynamic_color/dynamic_color.dart';
import 'package:fl_clash/clash/clash.dart';
import 'package:fl_clash/common/theme.dart';
import 'package:fl_clash/enum/enum.dart';
@@ -9,6 +9,7 @@ import 'package:fl_clash/plugins/service.dart';
import 'package:fl_clash/widgets/dialog.dart';
import 'package:fl_clash/widgets/scaffold.dart';
import 'package:flutter/material.dart';
import 'package:material_color_utilities/palettes/core_palette.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:url_launcher/url_launcher.dart';
@@ -20,17 +21,21 @@ typedef UpdateTasks = List<FutureOr Function()>;
class GlobalState {
static GlobalState? _instance;
Map<Key, double> cacheScrollPosition = {};
Map<CacheTag, double> cacheScrollPosition = {};
Map<CacheTag, FixedMap<String, double>> cacheHeightMap = {};
bool isService = false;
Timer? timer;
Timer? groupsUpdateTimer;
late Config config;
late AppState appState;
bool isPre = true;
String? coreSHA256;
late PackageInfo packageInfo;
Function? updateCurrentDelayDebounce;
late Measure measure;
late CommonTheme theme;
late Color accentColor;
CorePalette? corePalette;
DateTime? startTime;
UpdateTasks tasks = [];
final navigatorKey = GlobalKey<NavigatorState>();
@@ -50,14 +55,23 @@ class GlobalState {
appState = AppState(
version: version,
viewSize: Size.zero,
requests: FixedList(1000),
logs: FixedList(1000),
requests: FixedList(maxLength),
logs: FixedList(maxLength),
traffics: FixedList(30),
totalTraffic: Traffic(),
);
await _initDynamicColor();
await init();
}
_initDynamicColor() async {
try {
corePalette = await DynamicColorPlugin.getCorePalette();
accentColor = await DynamicColorPlugin.getAccentColor() ??
Color(defaultPrimaryColor);
} catch (_) {}
}
init() async {
packageInfo = await PackageInfo.fromPlatform();
config = await preferences.getConfig() ??
@@ -244,14 +258,17 @@ class GlobalState {
getUpdateConfigParams([bool? isPatch]) {
final currentProfile = config.currentProfile;
final clashConfig = config.patchClashConfig;
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(
routeAddress: config.networkProps.routeMode == RouteMode.bypassPrivate
? defaultBypassPrivateRouteAddress
: clashConfig.tun.routeAddress,
autoRoute: routeAddress.isEmpty ? true : false,
routeAddress: routeAddress,
),
rule: currentProfile?.overrideData.runningRule ?? [],
),

View File

@@ -103,7 +103,7 @@ class PrimaryColorBox extends ConsumerWidget {
genColorSchemeProvider(
themeData.brightness,
color: primaryColor,
isOverride: true,
ignoreConfig: true,
),
);
return Theme(

View File

@@ -0,0 +1,73 @@
import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class CommonSafeArea extends StatelessWidget {
const CommonSafeArea({
super.key,
this.left = true,
this.top = true,
this.right = true,
this.bottom = true,
this.minimum = EdgeInsets.zero,
this.maintainBottomViewPadding = false,
required this.child,
});
final bool left;
final bool top;
final bool right;
final bool bottom;
final EdgeInsets minimum;
final bool maintainBottomViewPadding;
final Widget child;
@override
Widget build(BuildContext context) {
assert(debugCheckHasMediaQuery(context));
EdgeInsets padding = MediaQuery.paddingOf(context);
final height = MediaQuery.of(context).size.height;
if (maintainBottomViewPadding) {
padding = padding.copyWith(
bottom: MediaQuery.viewPaddingOf(context).bottom,
);
}
final double realPaddingTop = padding.top > height * 0.5 ? 0 : padding.top;
return Padding(
padding: EdgeInsets.only(
left: math.max(left ? padding.left : 0.0, minimum.left),
top: math.max(top ? realPaddingTop : 0.0, minimum.top),
right: math.max(right ? padding.right : 0.0, minimum.right),
bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom),
),
child: MediaQuery.removePadding(
context: context,
removeLeft: left,
removeTop: top,
removeRight: right,
removeBottom: bottom,
child: child,
),
);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties
.add(FlagProperty('left', value: left, ifTrue: 'avoid left padding'));
properties
.add(FlagProperty('top', value: top, ifTrue: 'avoid top padding'));
properties.add(
FlagProperty('right', value: right, ifTrue: 'avoid right padding'));
properties.add(
FlagProperty('bottom', value: bottom, ifTrue: 'avoid bottom padding'));
}
}

View File

@@ -137,7 +137,7 @@ class DonutChartPainter extends CustomPainter {
@override
void paint(Canvas canvas, Size size) {
final center = Offset(size.width / 2, size.height / 2);
const strokeWidth = 10.0;
final strokeWidth = 10.0.ap;
final radius = min(size.width / 2, size.height / 2) - strokeWidth / 2;
final gapAngle = 2 * asin(strokeWidth * 1 / (2 * radius)) * 1.2;

View File

@@ -36,11 +36,13 @@ class FadeBox extends StatelessWidget {
class FadeThroughBox extends StatelessWidget {
final Widget child;
final Alignment? alignment;
final EdgeInsets? margin;
const FadeThroughBox({
super.key,
required this.child,
this.alignment,
this.margin
});
@override
@@ -52,6 +54,7 @@ class FadeThroughBox extends StatelessWidget {
secondaryAnimation,
) {
return Container(
margin: margin,
alignment: alignment ?? Alignment.centerLeft,
child: FadeThroughTransition(
animation: animation,

View File

@@ -62,6 +62,22 @@ class OpenDelegate extends Delegate {
});
}
class NextDelegate extends Delegate {
final Widget widget;
final String title;
final double? maxWidth;
final Widget? action;
final bool blur;
const NextDelegate({
required this.title,
required this.widget,
this.maxWidth,
this.action,
this.blur = true,
});
}
class OptionsDelegate<T> extends Delegate {
final List<T> options;
final String title;
@@ -138,6 +154,21 @@ class ListItem<T> extends StatelessWidget {
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
const ListItem.next({
super.key,
required this.title,
this.subtitle,
this.leading,
this.padding = const EdgeInsets.symmetric(horizontal: 16),
this.trailing,
required NextDelegate this.delegate,
this.horizontalTitleGap,
this.dense,
this.titleTextStyle,
this.subtitleTextStyle,
this.tileTitleAlignment = ListTileTitleAlignment.center,
}) : onTap = null;
const ListItem.options({
super.key,
required this.title,
@@ -226,6 +257,7 @@ class ListItem<T> extends StatelessWidget {
leading: leading ?? this.leading,
horizontalTitleGap: horizontalTitleGap,
title: title,
minVerticalPadding: 12,
subtitle: subtitle,
titleAlignment: tileTitleAlignment,
onTap: onTap,
@@ -285,6 +317,34 @@ class ListItem<T> extends StatelessWidget {
},
);
}
if (delegate is NextDelegate) {
final nextDelegate = delegate as NextDelegate;
final child = SafeArea(
child: nextDelegate.widget,
);
return _buildListTile(
onTap: () {
showExtend(
context,
props: ExtendProps(
blur: nextDelegate.blur,
maxWidth: nextDelegate.maxWidth,
),
builder: (_, type) {
return AdaptiveSheetScaffold(
actions: [
if (nextDelegate.action != null) nextDelegate.action!,
],
type: type,
body: child,
title: nextDelegate.title,
);
},
);
},
);
}
if (delegate is OptionsDelegate) {
final optionsDelegate = delegate as OptionsDelegate<T>;
return _buildListTile(
@@ -353,14 +413,11 @@ class ListItem<T> extends StatelessWidget {
radioDelegate.onChanged!(radioDelegate.value);
}
},
leading: SizedBox(
width: 32,
height: 32,
child: Radio<T>(
value: radioDelegate.value,
groupValue: radioDelegate.groupValue,
onChanged: radioDelegate.onChanged,
),
leading: Radio<T>(
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
value: radioDelegate.value,
groupValue: radioDelegate.groupValue,
onChanged: radioDelegate.onChanged,
),
trailing: trailing,
);
@@ -466,6 +523,32 @@ List<Widget> generateSection({
];
}
Widget generateSectionV2({
String? title,
required Iterable<Widget> items,
List<Widget>? actions,
bool separated = true,
}) {
return Column(
children: [
if (items.isNotEmpty && title != null)
ListHeader(
title: title,
actions: actions,
),
CommonCard(
radius: 18,
type: CommonCardType.filled,
child: Column(
children: [
...items,
],
),
)
],
);
}
List<Widget> generateInfoSection({
required Info info,
required Iterable<Widget> items,
@@ -497,4 +580,4 @@ Widget generateListView(List<Widget> items) {
bottom: 16,
),
);
}
}

View File

@@ -0,0 +1,33 @@
import 'package:fl_clash/models/config.dart';
import 'package:fl_clash/providers/config.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class TextScaleNotification extends StatelessWidget {
final Widget child;
final Function(TextScale textScale) onNotification;
const TextScaleNotification({
super.key,
required this.child,
required this.onNotification,
});
@override
Widget build(BuildContext context) {
return Consumer(
builder: (_, ref, child) {
ref.listen(
themeSettingProvider.select((state) => state.textScale),
(prev, next) {
if (prev != next) {
onNotification(next);
}
},
);
return child!;
},
child: child,
);
}
}

View File

@@ -10,7 +10,7 @@ import 'package:flutter/services.dart';
import 'chip.dart';
class CommonScaffold extends StatefulWidget {
final PreferredSizeWidget? appBar;
final AppBar? appBar;
final Widget body;
final Widget? bottomNavigationBar;
final Widget? sideNavigationBar;
@@ -125,25 +125,25 @@ class CommonScaffoldState extends State<CommonScaffold> {
}
}
ThemeData _appBarTheme(BuildContext context) {
Widget _buildSearchingAppBarTheme(Widget child) {
final ThemeData theme = Theme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
return theme.copyWith(
appBarTheme: AppBarTheme(
systemOverlayStyle: colorScheme.brightness == Brightness.dark
? SystemUiOverlayStyle.light
: SystemUiOverlayStyle.dark,
backgroundColor: colorScheme.brightness == Brightness.dark
? Colors.grey[900]
: Colors.white,
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
titleTextStyle: theme.textTheme.titleLarge,
toolbarTextStyle: theme.textTheme.bodyMedium,
),
inputDecorationTheme: InputDecorationTheme(
hintStyle: theme.inputDecorationTheme.hintStyle,
border: InputBorder.none,
return Theme(
data: theme.copyWith(
appBarTheme: theme.appBarTheme.copyWith(
backgroundColor: colorScheme.brightness == Brightness.dark
? Colors.grey[900]
: Colors.white,
iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),
titleTextStyle: theme.textTheme.titleLarge,
toolbarTextStyle: theme.textTheme.bodyMedium,
),
inputDecorationTheme: InputDecorationTheme(
hintStyle: theme.inputDecorationTheme.hintStyle,
border: InputBorder.none,
),
),
child: child,
);
}
@@ -318,72 +318,66 @@ class CommonScaffoldState extends State<CommonScaffold> {
child: appBar,
);
}
return _isSearch
? Theme(
data: _appBarTheme(context),
child: CommonPopScope(
onPop: () {
if (_isSearch) {
_handleExitSearching();
return false;
}
return true;
},
child: appBar,
),
)
: appBar;
return _isSearch ? _buildSearchingAppBarTheme(appBar) : appBar;
}
PreferredSizeWidget _buildAppBar() {
return PreferredSize(
preferredSize: const Size.fromHeight(kToolbarHeight),
child: Stack(
alignment: Alignment.bottomCenter,
children: [
ValueListenableBuilder<AppBarState>(
valueListenable: _appBarState,
builder: (_, state, __) {
return _buildAppBarWrap(
AppBar(
centerTitle: widget.centerTitle ?? false,
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness:
Theme.of(context).brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
systemNavigationBarIconBrightness:
Theme.of(context).brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
systemNavigationBarColor: widget.bottomNavigationBar != null
? context.colorScheme.surfaceContainer
: context.colorScheme.surface,
systemNavigationBarDividerColor: Colors.transparent,
),
automaticallyImplyLeading: widget.automaticallyImplyLeading,
leading: _buildLeading(),
title: _buildTitle(state.searchState),
actions: _buildActions(
state.searchState != null,
state.actions.isNotEmpty
? state.actions
: widget.actions ?? [],
),
child: Theme(
data: Theme.of(context).copyWith(
appBarTheme: AppBarTheme(
systemOverlayStyle: SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness:
Theme.of(context).brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
systemNavigationBarIconBrightness:
Theme.of(context).brightness == Brightness.dark
? Brightness.light
: Brightness.dark,
systemNavigationBarColor: widget.bottomNavigationBar != null
? context.colorScheme.surfaceContainer
: context.colorScheme.surface,
systemNavigationBarDividerColor: Colors.transparent,
),
),
),
child: Stack(
alignment: Alignment.bottomCenter,
children: [
widget.appBar ??
ValueListenableBuilder<AppBarState>(
valueListenable: _appBarState,
builder: (_, state, __) {
return _buildAppBarWrap(
AppBar(
centerTitle: widget.centerTitle ?? false,
automaticallyImplyLeading:
widget.automaticallyImplyLeading,
leading: _buildLeading(),
title: _buildTitle(state.searchState),
actions: _buildActions(
state.searchState != null,
state.actions.isNotEmpty
? state.actions
: widget.actions ?? [],
),
),
);
},
),
);
},
),
ValueListenableBuilder(
valueListenable: _loading,
builder: (_, value, __) {
return value == true
? const LinearProgressIndicator()
: Container();
},
),
],
ValueListenableBuilder(
valueListenable: _loading,
builder: (_, value, __) {
return value == true
? const LinearProgressIndicator()
: Container();
},
),
],
),
),
);
}
@@ -391,56 +385,62 @@ class CommonScaffoldState extends State<CommonScaffold> {
@override
Widget build(BuildContext context) {
assert(widget.appBar != null || widget.title != null);
final body = Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ValueListenableBuilder(
valueListenable: _keywordsNotifier,
builder: (_, keywords, __) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_onKeywordsUpdate != null) {
_onKeywordsUpdate!(keywords);
final body = SafeArea(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
ValueListenableBuilder(
valueListenable: _keywordsNotifier,
builder: (_, keywords, __) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (_onKeywordsUpdate != null) {
_onKeywordsUpdate!(keywords);
}
});
if (keywords.isEmpty) {
return SizedBox();
}
});
if (keywords.isEmpty) {
return SizedBox();
}
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
child: Wrap(
runSpacing: 8,
spacing: 8,
children: [
for (final keyword in keywords)
CommonChip(
label: keyword,
type: ChipType.delete,
onPressed: () {
_deleteKeyword(keyword);
},
),
],
),
);
},
),
Expanded(
child: widget.body,
),
],
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 16,
),
child: Wrap(
runSpacing: 8,
spacing: 8,
children: [
for (final keyword in keywords)
CommonChip(
label: keyword,
type: ChipType.delete,
onPressed: () {
_deleteKeyword(keyword);
},
),
],
),
);
},
),
Expanded(
child: widget.body,
),
],
),
);
final scaffold = Scaffold(
appBar: widget.appBar ?? _buildAppBar(),
appBar: _buildAppBar(),
body: body,
backgroundColor: widget.backgroundColor,
floatingActionButton: ValueListenableBuilder<Widget?>(
valueListenable: _floatingActionButton,
builder: (_, value, __) {
return FadeScaleBox(
child: value ?? SizedBox(),
return IntrinsicWidth(
child: IntrinsicHeight(
child: FadeScaleBox(
child: value ?? SizedBox(),
),
),
);
},
),

View File

@@ -1,6 +1,6 @@
import 'package:collection/collection.dart';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/common/list.dart';
import 'package:fl_clash/enum/enum.dart';
import 'package:fl_clash/state.dart';
import 'package:flutter/material.dart';
@@ -54,13 +54,13 @@ class ScrollToEndBox<T> extends StatefulWidget {
final ScrollController controller;
final List<T> dataSource;
final Widget child;
final Key cacheKey;
final CacheTag tag;
const ScrollToEndBox({
super.key,
required this.child,
required this.controller,
required this.cacheKey,
required this.tag,
required this.dataSource,
});
@@ -73,8 +73,7 @@ class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
_handleTryToEnd() {
WidgetsBinding.instance.addPostFrameCallback((_) {
final double offset =
globalState.cacheScrollPosition[widget.cacheKey] ?? -1;
final double offset = globalState.cacheScrollPosition[widget.tag] ?? -1;
if (offset < 0) {
widget.controller.animateTo(
duration: kThemeAnimationDuration,
@@ -85,12 +84,6 @@ class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
});
}
@override
void initState() {
super.initState();
globalState.cacheScrollPosition[widget.cacheKey] = -1;
}
@override
void didUpdateWidget(ScrollToEndBox<T> oldWidget) {
super.didUpdateWidget(oldWidget);
@@ -101,13 +94,12 @@ class _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {
@override
Widget build(BuildContext context) {
return NotificationListener<ScrollNotification>(
return NotificationListener<UserScrollNotification>(
onNotification: (details) {
double offset =
globalState.cacheScrollPosition[widget.tag] =
details.metrics.pixels == details.metrics.maxScrollExtent
? -1
: details.metrics.pixels;
globalState.cacheScrollPosition[widget.cacheKey] = offset;
return false;
},
child: widget.child,
@@ -124,6 +116,7 @@ class CacheItemExtentListView extends StatefulWidget {
final bool shrinkWrap;
final bool reverse;
final ScrollController controller;
final CacheTag tag;
const CacheItemExtentListView({
super.key,
@@ -135,6 +128,7 @@ class CacheItemExtentListView extends StatefulWidget {
required this.keyBuilder,
required this.itemCount,
required this.itemExtentBuilder,
required this.tag,
});
@override
@@ -143,21 +137,19 @@ class CacheItemExtentListView extends StatefulWidget {
}
class CacheItemExtentListViewState extends State<CacheItemExtentListView> {
late final FixedMap<String, double> _cacheHeightMap;
@override
void initState() {
super.initState();
_cacheHeightMap = FixedMap(widget.itemCount);
_updateCacheHeightMap();
}
clearCache() {
_cacheHeightMap.clear();
_updateCacheHeightMap() {
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
globalState.cacheHeightMap[widget.tag] ??= FixedMap(widget.itemCount);
}
@override
Widget build(BuildContext context) {
_cacheHeightMap.updateMaxSize(widget.itemCount);
return ListView.builder(
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
@@ -166,20 +158,14 @@ class CacheItemExtentListViewState extends State<CacheItemExtentListView> {
shrinkWrap: widget.shrinkWrap,
controller: widget.controller,
itemExtentBuilder: (index, __) {
final key = widget.keyBuilder(index);
if (_cacheHeightMap.containsKey(key)) {
return _cacheHeightMap.get(key);
}
return _cacheHeightMap.put(key, widget.itemExtentBuilder(index));
_updateCacheHeightMap();
return globalState.cacheHeightMap[widget.tag]?.updateCacheValue(
widget.keyBuilder(index),
() => widget.itemExtentBuilder(index),
);
},
);
}
@override
void dispose() {
_cacheHeightMap.clear();
super.dispose();
}
}
class CacheItemExtentSliverReorderableList extends StatefulWidget {
@@ -189,6 +175,7 @@ class CacheItemExtentSliverReorderableList extends StatefulWidget {
final double Function(int index) itemExtentBuilder;
final ReorderCallback onReorder;
final ReorderItemProxyDecorator? proxyDecorator;
final CacheTag tag;
const CacheItemExtentSliverReorderableList({
super.key,
@@ -198,6 +185,7 @@ class CacheItemExtentSliverReorderableList extends StatefulWidget {
required this.itemExtentBuilder,
required this.onReorder,
this.proxyDecorator,
required this.tag,
});
@override
@@ -207,30 +195,24 @@ class CacheItemExtentSliverReorderableList extends StatefulWidget {
class CacheItemExtentSliverReorderableListState
extends State<CacheItemExtentSliverReorderableList> {
late final FixedMap<String, double> _cacheHeightMap;
@override
void initState() {
super.initState();
_cacheHeightMap = FixedMap(widget.itemCount);
}
clearCache() {
_cacheHeightMap.clear();
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
globalState.cacheHeightMap[widget.tag] ??= FixedMap(widget.itemCount);
}
@override
Widget build(BuildContext context) {
_cacheHeightMap.updateMaxSize(widget.itemCount);
globalState.cacheHeightMap[widget.tag]?.updateMaxLength(widget.itemCount);
return SliverReorderableList(
itemBuilder: widget.itemBuilder,
itemCount: widget.itemCount,
itemExtentBuilder: (index, __) {
final key = widget.keyBuilder(index);
if (_cacheHeightMap.containsKey(key)) {
return _cacheHeightMap.get(key);
}
return _cacheHeightMap.put(key, widget.itemExtentBuilder(index));
return globalState.cacheHeightMap[widget.tag]?.updateCacheValue(
widget.keyBuilder(index),
() => widget.itemExtentBuilder(index),
);
},
onReorder: widget.onReorder,
proxyDecorator: widget.proxyDecorator,
@@ -239,7 +221,6 @@ class CacheItemExtentSliverReorderableListState
@override
void dispose() {
_cacheHeightMap.clear();
super.dispose();
}
}

View File

@@ -369,6 +369,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
}
_handleDelete(int index) async {
await _transformCompleter?.future;
_preTransformState();
final indexWhere = _tempIndexList.indexWhere((i) => i == index);
_tempIndexList.removeAt(indexWhere);
@@ -484,9 +485,24 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
Widget _draggableWrap({
required Widget childWhenDragging,
required Widget feedback,
required Widget target,
required Widget item,
required int index,
}) {
final target = DragTarget<int>(
builder: (_, __, ___) {
return AbsorbPointer(
child: item,
);
},
onWillAcceptWithDetails: (_) {
debouncer.call(
DebounceTag.handleWill,
_handleWill,
args: [index],
);
return false;
},
);
final shakeTarget = ValueListenableBuilder(
valueListenable: _dragIndexNotifier,
builder: (_, dragIndex, child) {
@@ -539,7 +555,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
valueListenable: isEditNotifier,
builder: (_, isEdit, child) {
if (!isEdit) {
return target;
return item;
}
return child!;
},
@@ -558,12 +574,10 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
_itemContexts[index] = context;
final childWhenDragging = ActivateBox(
child: Opacity(
opacity: 0.3,
opacity: 0.6,
child: _sizeBoxWrap(
CommonCard(
child: Container(
color: context.colorScheme.secondaryContainer,
),
child: child,
),
index,
),
@@ -580,25 +594,11 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
index,
),
);
final target = DragTarget<int>(
builder: (_, __, ___) {
return child;
},
onWillAcceptWithDetails: (_) {
debouncer.call(
DebounceTag.handleWill,
_handleWill,
args: [index],
);
return false;
},
);
return _wrapTransform(
_draggableWrap(
childWhenDragging: childWhenDragging,
feedback: feedback,
target: target,
item: child,
index: index,
),
index,
@@ -666,8 +666,7 @@ class SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {
crossAxisSpacing: widget.crossAxisSpacing,
mainAxisSpacing: widget.mainAxisSpacing,
children: [
for (int i = 0; i < children.length; i++)
_builderItem(i),
for (int i = 0; i < children.length; i++) _builderItem(i),
],
);
},

1100
lib/widgets/tab.dart Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -84,6 +84,7 @@ class EmojiText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RichText(
textScaler: MediaQuery.of(context).textScaler,
maxLines: maxLines,
overflow: overflow ?? TextOverflow.clip,
text: TextSpan(

View File

@@ -30,3 +30,6 @@ export 'scroll.dart';
export 'dialog.dart';
export 'effect.dart';
export 'palette.dart';
export 'tab.dart';
export 'container.dart';
export 'notification.dart';