mirror of
https://github.com/StarCitizenToolBox/app.git
synced 2026-01-14 04:00:27 +00:00
Merge pull request #145 from StarCitizenToolBox/feat/ort_translate
Feat/ort translate
This commit is contained in:
commit
af15d106f0
84
lib/app.dart
84
lib/app.dart
@ -49,6 +49,7 @@ abstract class AppGlobalState with _$AppGlobalState {
|
||||
@Default(ThemeConf()) ThemeConf themeConf,
|
||||
Locale? appLocale,
|
||||
Box? appConfBox,
|
||||
@Default(10) windowsVersion,
|
||||
}) = _AppGlobalState;
|
||||
}
|
||||
|
||||
@ -56,17 +57,15 @@ abstract class AppGlobalState with _$AppGlobalState {
|
||||
GoRouter router(Ref ref) {
|
||||
return GoRouter(
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: '/',
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI()),
|
||||
),
|
||||
GoRoute(path: '/', pageBuilder: (context, state) => myPageBuilder(context, state, const SplashUI())),
|
||||
GoRoute(
|
||||
path: '/index',
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const IndexUI()),
|
||||
routes: [
|
||||
GoRoute(
|
||||
path: "downloader",
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI())),
|
||||
path: "downloader",
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomeDownloaderUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'game_doctor',
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomeGameDoctorUI()),
|
||||
@ -76,17 +75,19 @@ GoRouter router(Ref ref) {
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const HomePerformanceUI()),
|
||||
),
|
||||
GoRoute(
|
||||
path: 'advanced_localization',
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI()))
|
||||
path: 'advanced_localization',
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const AdvancedLocalizationUI()),
|
||||
),
|
||||
],
|
||||
),
|
||||
GoRoute(path: '/tools', builder: (_, _) => const SizedBox(), routes: [
|
||||
GoRoute(
|
||||
path: 'unp4kc',
|
||||
pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI()),
|
||||
),
|
||||
]),
|
||||
GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI()))
|
||||
GoRoute(
|
||||
path: '/tools',
|
||||
builder: (_, _) => const SizedBox(),
|
||||
routes: [
|
||||
GoRoute(path: 'unp4kc', pageBuilder: (context, state) => myPageBuilder(context, state, const UnP4kcUI())),
|
||||
],
|
||||
),
|
||||
GoRoute(path: '/guide', pageBuilder: (context, state) => myPageBuilder(context, state, const GuideUI())),
|
||||
],
|
||||
);
|
||||
}
|
||||
@ -94,13 +95,13 @@ GoRouter router(Ref ref) {
|
||||
@riverpod
|
||||
class AppGlobalModel extends _$AppGlobalModel {
|
||||
static Map<Locale, String> get appLocaleSupport => {
|
||||
const Locale("auto"): S.current.settings_app_language_auto,
|
||||
const Locale("zh", "CN"): NoL10n.langZHS,
|
||||
const Locale("zh", "TW"): NoL10n.langZHT,
|
||||
const Locale("en"): NoL10n.langEn,
|
||||
const Locale("ja"): NoL10n.langJa,
|
||||
const Locale("ru"): NoL10n.langRU,
|
||||
};
|
||||
const Locale("auto"): S.current.settings_app_language_auto,
|
||||
const Locale("zh", "CN"): NoL10n.langZHS,
|
||||
const Locale("zh", "TW"): NoL10n.langZHT,
|
||||
const Locale("en"): NoL10n.langEn,
|
||||
const Locale("ja"): NoL10n.langJa,
|
||||
const Locale("ru"): NoL10n.langRU,
|
||||
};
|
||||
|
||||
@override
|
||||
AppGlobalState build() {
|
||||
@ -174,9 +175,9 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
await Window.initialize();
|
||||
await Window.hideWindowControls();
|
||||
if (windowsDeviceInfo?.productName.contains("Windows 11") ?? false) {
|
||||
await Window.setEffect(
|
||||
effect: WindowEffect.acrylic,
|
||||
);
|
||||
await Window.setEffect(effect: WindowEffect.acrylic);
|
||||
state = state.copyWith(windowsVersion: 11);
|
||||
dPrint("---- Windows 11 Acrylic Effect init -----");
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -226,18 +227,24 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
if (state.networkVersionData == null) {
|
||||
if (!context.mounted) return false;
|
||||
await showToast(
|
||||
context, S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString()));
|
||||
context,
|
||||
S.current.app_common_network_error(ConstConf.appVersionDate, checkUpdateError.toString()),
|
||||
);
|
||||
return false;
|
||||
}
|
||||
if (!Platform.isWindows) return false;
|
||||
final lastVersion =
|
||||
ConstConf.isMSE ? state.networkVersionData?.mSELastVersionCode : state.networkVersionData?.lastVersionCode;
|
||||
final lastVersion = ConstConf.isMSE
|
||||
? state.networkVersionData?.mSELastVersionCode
|
||||
: state.networkVersionData?.lastVersionCode;
|
||||
if ((lastVersion ?? 0) > ConstConf.appVersionCode) {
|
||||
// need update
|
||||
if (!context.mounted) return false;
|
||||
|
||||
final r =
|
||||
await showDialog(dismissWithEsc: false, context: context, builder: (context) => const UpgradeDialogUI());
|
||||
final r = await showDialog(
|
||||
dismissWithEsc: false,
|
||||
context: context,
|
||||
builder: (context) => const UpgradeDialogUI(),
|
||||
);
|
||||
|
||||
if (r != true) {
|
||||
if (!context.mounted) return false;
|
||||
@ -264,8 +271,10 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
|
||||
dPrint("now == $now start == $startTime end == $endTime");
|
||||
if (now < startTime) {
|
||||
_activityThemeColorTimer =
|
||||
Timer(Duration(milliseconds: startTime - now), () => checkActivityThemeColor(networkVersionData));
|
||||
_activityThemeColorTimer = Timer(
|
||||
Duration(milliseconds: startTime - now),
|
||||
() => checkActivityThemeColor(networkVersionData),
|
||||
);
|
||||
dPrint("start Timer ....");
|
||||
} else if (now >= startTime && now <= endTime) {
|
||||
dPrint("update Color ....");
|
||||
@ -280,8 +289,10 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
);
|
||||
|
||||
// wait for end
|
||||
_activityThemeColorTimer =
|
||||
Timer(Duration(milliseconds: endTime - now), () => checkActivityThemeColor(networkVersionData));
|
||||
_activityThemeColorTimer = Timer(
|
||||
Duration(milliseconds: endTime - now),
|
||||
() => checkActivityThemeColor(networkVersionData),
|
||||
);
|
||||
} else {
|
||||
dPrint("reset Color ....");
|
||||
state = state.copyWith(
|
||||
@ -302,8 +313,9 @@ class AppGlobalModel extends _$AppGlobalModel {
|
||||
await appConfBox.put("app_locale", null);
|
||||
return;
|
||||
}
|
||||
final localeCode =
|
||||
value.countryCode != null ? "${value.languageCode}_${value.countryCode ?? ""}" : value.languageCode;
|
||||
final localeCode = value.countryCode != null
|
||||
? "${value.languageCode}_${value.countryCode ?? ""}"
|
||||
: value.languageCode;
|
||||
dPrint("changeLocale == $value localeCode=== $localeCode");
|
||||
await appConfBox.put("app_locale", localeCode);
|
||||
state = state.copyWith(appLocale: value);
|
||||
|
||||
@ -14,7 +14,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$AppGlobalState {
|
||||
|
||||
String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox;
|
||||
String? get deviceUUID; String? get applicationSupportDir; String? get applicationBinaryModuleDir; AppVersionData? get networkVersionData; ThemeConf get themeConf; Locale? get appLocale; Box? get appConfBox; dynamic get windowsVersion;
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -25,16 +25,16 @@ $AppGlobalStateCopyWith<AppGlobalState> get copyWith => _$AppGlobalStateCopyWith
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox);
|
||||
int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)';
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ abstract mixin class $AppGlobalStateCopyWith<$Res> {
|
||||
factory $AppGlobalStateCopyWith(AppGlobalState value, $Res Function(AppGlobalState) _then) = _$AppGlobalStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox
|
||||
String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ class _$AppGlobalStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable
|
||||
as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable
|
||||
@ -71,7 +71,8 @@ as String?,networkVersionData: freezed == networkVersionData ? _self.networkVers
|
||||
as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box?,
|
||||
as Box?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
/// Create a copy of AppGlobalState
|
||||
@ -165,10 +166,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState() when $default != null:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@ -186,10 +187,10 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState():
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@ -206,10 +207,10 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _AppGlobalState() when $default != null:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox);case _:
|
||||
return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBinaryModuleDir,_that.networkVersionData,_that.themeConf,_that.appLocale,_that.appConfBox,_that.windowsVersion);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@ -221,7 +222,7 @@ return $default(_that.deviceUUID,_that.applicationSupportDir,_that.applicationBi
|
||||
|
||||
|
||||
class _AppGlobalState implements AppGlobalState {
|
||||
const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox});
|
||||
const _AppGlobalState({this.deviceUUID, this.applicationSupportDir, this.applicationBinaryModuleDir, this.networkVersionData, this.themeConf = const ThemeConf(), this.appLocale, this.appConfBox, this.windowsVersion = 10});
|
||||
|
||||
|
||||
@override final String? deviceUUID;
|
||||
@ -231,6 +232,7 @@ class _AppGlobalState implements AppGlobalState {
|
||||
@override@JsonKey() final ThemeConf themeConf;
|
||||
@override final Locale? appLocale;
|
||||
@override final Box? appConfBox;
|
||||
@override@JsonKey() final dynamic windowsVersion;
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -242,16 +244,16 @@ _$AppGlobalStateCopyWith<_AppGlobalState> get copyWith => __$AppGlobalStateCopyW
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppGlobalState&&(identical(other.deviceUUID, deviceUUID) || other.deviceUUID == deviceUUID)&&(identical(other.applicationSupportDir, applicationSupportDir) || other.applicationSupportDir == applicationSupportDir)&&(identical(other.applicationBinaryModuleDir, applicationBinaryModuleDir) || other.applicationBinaryModuleDir == applicationBinaryModuleDir)&&(identical(other.networkVersionData, networkVersionData) || other.networkVersionData == networkVersionData)&&(identical(other.themeConf, themeConf) || other.themeConf == themeConf)&&(identical(other.appLocale, appLocale) || other.appLocale == appLocale)&&(identical(other.appConfBox, appConfBox) || other.appConfBox == appConfBox)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox);
|
||||
int get hashCode => Object.hash(runtimeType,deviceUUID,applicationSupportDir,applicationBinaryModuleDir,networkVersionData,themeConf,appLocale,appConfBox,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox)';
|
||||
return 'AppGlobalState(deviceUUID: $deviceUUID, applicationSupportDir: $applicationSupportDir, applicationBinaryModuleDir: $applicationBinaryModuleDir, networkVersionData: $networkVersionData, themeConf: $themeConf, appLocale: $appLocale, appConfBox: $appConfBox, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
@ -262,7 +264,7 @@ abstract mixin class _$AppGlobalStateCopyWith<$Res> implements $AppGlobalStateCo
|
||||
factory _$AppGlobalStateCopyWith(_AppGlobalState value, $Res Function(_AppGlobalState) _then) = __$AppGlobalStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox
|
||||
String? deviceUUID, String? applicationSupportDir, String? applicationBinaryModuleDir, AppVersionData? networkVersionData, ThemeConf themeConf, Locale? appLocale, Box? appConfBox, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
@ -279,7 +281,7 @@ class __$AppGlobalStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of AppGlobalState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? deviceUUID = freezed,Object? applicationSupportDir = freezed,Object? applicationBinaryModuleDir = freezed,Object? networkVersionData = freezed,Object? themeConf = null,Object? appLocale = freezed,Object? appConfBox = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_AppGlobalState(
|
||||
deviceUUID: freezed == deviceUUID ? _self.deviceUUID : deviceUUID // ignore: cast_nullable_to_non_nullable
|
||||
as String?,applicationSupportDir: freezed == applicationSupportDir ? _self.applicationSupportDir : applicationSupportDir // ignore: cast_nullable_to_non_nullable
|
||||
@ -288,7 +290,8 @@ as String?,networkVersionData: freezed == networkVersionData ? _self.networkVers
|
||||
as AppVersionData?,themeConf: null == themeConf ? _self.themeConf : themeConf // ignore: cast_nullable_to_non_nullable
|
||||
as ThemeConf,appLocale: freezed == appLocale ? _self.appLocale : appLocale // ignore: cast_nullable_to_non_nullable
|
||||
as Locale?,appConfBox: freezed == appConfBox ? _self.appConfBox : appConfBox // ignore: cast_nullable_to_non_nullable
|
||||
as Box?,
|
||||
as Box?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -82,7 +82,7 @@ final class AppGlobalModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$appGlobalModelHash() => r'53dd9ed5e197333b509d282eb01073f15572820c';
|
||||
String _$appGlobalModelHash() => r'51f72c5d8538e2a4f11d256802b1a1f2e04d03be';
|
||||
|
||||
abstract class _$AppGlobalModel extends $Notifier<AppGlobalState> {
|
||||
AppGlobalState build();
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
class ConstConf {
|
||||
static const String appVersion = "2.15.0";
|
||||
static const int appVersionCode = 70;
|
||||
static const String appVersionDate = "2025-11-8";
|
||||
static const String appVersion = "2.15.1 Beta";
|
||||
static const int appVersionCode = 71;
|
||||
static const String appVersionDate = "2025-11-15";
|
||||
static const _gameChannels = [
|
||||
"LIVE",
|
||||
"4.0_PREVIEW",
|
||||
|
||||
72
lib/common/rust/api/ort_api.dart
Normal file
72
lib/common/rust/api/ort_api.dart
Normal file
@ -0,0 +1,72 @@
|
||||
// This file is automatically generated, so please do not edit it.
|
||||
// @generated by `flutter_rust_bridge`@ 2.11.1.
|
||||
|
||||
// ignore_for_file: invalid_use_of_internal_member, unused_import, unnecessary_import
|
||||
|
||||
import '../frb_generated.dart';
|
||||
import 'package:flutter_rust_bridge/flutter_rust_bridge_for_generated.dart';
|
||||
|
||||
/// 加载 ONNX 翻译模型
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_path` - 模型文件夹路径
|
||||
/// * `model_key` - 模型缓存键(用于标识模型,如 "zh-en")
|
||||
/// * `quantization_suffix` - 量化后缀(如 "_q4", "_q8",空字符串表示使用默认模型)
|
||||
///
|
||||
Future<void> loadTranslationModel({
|
||||
required String modelPath,
|
||||
required String modelKey,
|
||||
required String quantizationSuffix,
|
||||
}) => RustLib.instance.api.crateApiOrtApiLoadTranslationModel(
|
||||
modelPath: modelPath,
|
||||
modelKey: modelKey,
|
||||
quantizationSuffix: quantizationSuffix,
|
||||
);
|
||||
|
||||
/// 翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
/// * `text` - 要翻译的文本
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<String>` - 翻译后的文本
|
||||
Future<String> translateText({
|
||||
required String modelKey,
|
||||
required String text,
|
||||
}) => RustLib.instance.api.crateApiOrtApiTranslateText(
|
||||
modelKey: modelKey,
|
||||
text: text,
|
||||
);
|
||||
|
||||
/// 批量翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
/// * `texts` - 要翻译的文本列表
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<Vec<String>>` - 翻译后的文本列表
|
||||
Future<List<String>> translateTextBatch({
|
||||
required String modelKey,
|
||||
required List<String> texts,
|
||||
}) => RustLib.instance.api.crateApiOrtApiTranslateTextBatch(
|
||||
modelKey: modelKey,
|
||||
texts: texts,
|
||||
);
|
||||
|
||||
/// 卸载模型
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
///
|
||||
Future<void> unloadTranslationModel({required String modelKey}) => RustLib
|
||||
.instance
|
||||
.api
|
||||
.crateApiOrtApiUnloadTranslationModel(modelKey: modelKey);
|
||||
|
||||
/// 清空所有已加载的模型
|
||||
///
|
||||
/// # Returns
|
||||
Future<void> clearAllModels() =>
|
||||
RustLib.instance.api.crateApiOrtApiClearAllModels();
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
import 'api/asar_api.dart';
|
||||
import 'api/http_api.dart';
|
||||
import 'api/ort_api.dart';
|
||||
import 'api/rs_process.dart';
|
||||
import 'api/win32_api.dart';
|
||||
import 'dart:async';
|
||||
@ -68,7 +69,7 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
String get codegenVersion => '2.11.1';
|
||||
|
||||
@override
|
||||
int get rustContentHash => 1832496273;
|
||||
int get rustContentHash => -706588047;
|
||||
|
||||
static const kDefaultExternalLibraryLoaderConfig =
|
||||
ExternalLibraryLoaderConfig(
|
||||
@ -79,6 +80,8 @@ class RustLib extends BaseEntrypoint<RustLibApi, RustLibApiImpl, RustLibWire> {
|
||||
}
|
||||
|
||||
abstract class RustLibApi extends BaseApi {
|
||||
Future<void> crateApiOrtApiClearAllModels();
|
||||
|
||||
Future<List<String>> crateApiHttpApiDnsLookupIps({required String host});
|
||||
|
||||
Future<List<String>> crateApiHttpApiDnsLookupTxt({required String host});
|
||||
@ -96,6 +99,12 @@ abstract class RustLibApi extends BaseApi {
|
||||
required String asarPath,
|
||||
});
|
||||
|
||||
Future<void> crateApiOrtApiLoadTranslationModel({
|
||||
required String modelPath,
|
||||
required String modelKey,
|
||||
required String quantizationSuffix,
|
||||
});
|
||||
|
||||
Future<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs({
|
||||
required RsiLauncherAsarData that,
|
||||
required List<int> content,
|
||||
@ -122,6 +131,18 @@ abstract class RustLibApi extends BaseApi {
|
||||
required String workingDirectory,
|
||||
});
|
||||
|
||||
Future<String> crateApiOrtApiTranslateText({
|
||||
required String modelKey,
|
||||
required String text,
|
||||
});
|
||||
|
||||
Future<List<String>> crateApiOrtApiTranslateTextBatch({
|
||||
required String modelKey,
|
||||
required List<String> texts,
|
||||
});
|
||||
|
||||
Future<void> crateApiOrtApiUnloadTranslationModel({required String modelKey});
|
||||
|
||||
Future<void> crateApiRsProcessWrite({
|
||||
required int rsPid,
|
||||
required String data,
|
||||
@ -136,6 +157,27 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
required super.portManager,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<void> crateApiOrtApiClearAllModels() {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
return wire.wire__crate__api__ort_api__clear_all_models(port_);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiOrtApiClearAllModelsConstMeta,
|
||||
argValues: [],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiOrtApiClearAllModelsConstMeta =>
|
||||
const TaskConstMeta(debugName: "clear_all_models", argNames: []);
|
||||
|
||||
@override
|
||||
Future<List<String>> crateApiHttpApiDnsLookupIps({required String host}) {
|
||||
return handler.executeNormal(
|
||||
@ -268,6 +310,42 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: ["asarPath"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiOrtApiLoadTranslationModel({
|
||||
required String modelPath,
|
||||
required String modelKey,
|
||||
required String quantizationSuffix,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(modelPath);
|
||||
var arg1 = cst_encode_String(modelKey);
|
||||
var arg2 = cst_encode_String(quantizationSuffix);
|
||||
return wire.wire__crate__api__ort_api__load_translation_model(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
arg2,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiOrtApiLoadTranslationModelConstMeta,
|
||||
argValues: [modelPath, modelKey, quantizationSuffix],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiOrtApiLoadTranslationModelConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "load_translation_model",
|
||||
argNames: ["modelPath", "modelKey", "quantizationSuffix"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiAsarApiRsiLauncherAsarDataWriteMainJs({
|
||||
required RsiLauncherAsarData that,
|
||||
@ -443,6 +521,102 @@ class RustLibApiImpl extends RustLibApiImplPlatform implements RustLibApi {
|
||||
argNames: ["executable", "arguments", "workingDirectory", "streamSink"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<String> crateApiOrtApiTranslateText({
|
||||
required String modelKey,
|
||||
required String text,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(modelKey);
|
||||
var arg1 = cst_encode_String(text);
|
||||
return wire.wire__crate__api__ort_api__translate_text(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_String,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiOrtApiTranslateTextConstMeta,
|
||||
argValues: [modelKey, text],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiOrtApiTranslateTextConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "translate_text",
|
||||
argNames: ["modelKey", "text"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<List<String>> crateApiOrtApiTranslateTextBatch({
|
||||
required String modelKey,
|
||||
required List<String> texts,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(modelKey);
|
||||
var arg1 = cst_encode_list_String(texts);
|
||||
return wire.wire__crate__api__ort_api__translate_text_batch(
|
||||
port_,
|
||||
arg0,
|
||||
arg1,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_list_String,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiOrtApiTranslateTextBatchConstMeta,
|
||||
argValues: [modelKey, texts],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiOrtApiTranslateTextBatchConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "translate_text_batch",
|
||||
argNames: ["modelKey", "texts"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiOrtApiUnloadTranslationModel({
|
||||
required String modelKey,
|
||||
}) {
|
||||
return handler.executeNormal(
|
||||
NormalTask(
|
||||
callFfi: (port_) {
|
||||
var arg0 = cst_encode_String(modelKey);
|
||||
return wire.wire__crate__api__ort_api__unload_translation_model(
|
||||
port_,
|
||||
arg0,
|
||||
);
|
||||
},
|
||||
codec: DcoCodec(
|
||||
decodeSuccessData: dco_decode_unit,
|
||||
decodeErrorData: dco_decode_AnyhowException,
|
||||
),
|
||||
constMeta: kCrateApiOrtApiUnloadTranslationModelConstMeta,
|
||||
argValues: [modelKey],
|
||||
apiImpl: this,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
TaskConstMeta get kCrateApiOrtApiUnloadTranslationModelConstMeta =>
|
||||
const TaskConstMeta(
|
||||
debugName: "unload_translation_model",
|
||||
argNames: ["modelKey"],
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> crateApiRsProcessWrite({
|
||||
required int rsPid,
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
|
||||
import 'api/asar_api.dart';
|
||||
import 'api/http_api.dart';
|
||||
import 'api/ort_api.dart';
|
||||
import 'api/rs_process.dart';
|
||||
import 'api/win32_api.dart';
|
||||
import 'dart:async';
|
||||
@ -583,7 +584,7 @@ abstract class RustLibApiImplPlatform extends BaseApiImpl<RustLibWire> {
|
||||
// AUTO GENERATED FILE, DO NOT EDIT.
|
||||
//
|
||||
// Generated by `package:ffigen`.
|
||||
// ignore_for_file: type=lint
|
||||
// ignore_for_file: type=lint, unused_import
|
||||
|
||||
/// generated by flutter_rust_bridge
|
||||
class RustLibWire implements BaseWire {
|
||||
@ -614,6 +615,18 @@ class RustLibWire implements BaseWire {
|
||||
late final _store_dart_post_cobject = _store_dart_post_cobjectPtr
|
||||
.asFunction<void Function(DartPostCObjectFnType)>();
|
||||
|
||||
void wire__crate__api__ort_api__clear_all_models(int port_) {
|
||||
return _wire__crate__api__ort_api__clear_all_models(port_);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__ort_api__clear_all_modelsPtr =
|
||||
_lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int64)>>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__ort_api__clear_all_models',
|
||||
);
|
||||
late final _wire__crate__api__ort_api__clear_all_models =
|
||||
_wire__crate__api__ort_api__clear_all_modelsPtr
|
||||
.asFunction<void Function(int)>();
|
||||
|
||||
void wire__crate__api__http_api__dns_lookup_ips(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> host,
|
||||
@ -733,6 +746,44 @@ class RustLibWire implements BaseWire {
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__ort_api__load_translation_model(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> model_path,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> model_key,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> quantization_suffix,
|
||||
) {
|
||||
return _wire__crate__api__ort_api__load_translation_model(
|
||||
port_,
|
||||
model_path,
|
||||
model_key,
|
||||
quantization_suffix,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__ort_api__load_translation_modelPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__ort_api__load_translation_model',
|
||||
);
|
||||
late final _wire__crate__api__ort_api__load_translation_model =
|
||||
_wire__crate__api__ort_api__load_translation_modelPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_rsi_launcher_asar_data> that,
|
||||
@ -898,6 +949,95 @@ class RustLibWire implements BaseWire {
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__ort_api__translate_text(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> model_key,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> text,
|
||||
) {
|
||||
return _wire__crate__api__ort_api__translate_text(port_, model_key, text);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__ort_api__translate_textPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>('frbgen_starcitizen_doctor_wire__crate__api__ort_api__translate_text');
|
||||
late final _wire__crate__api__ort_api__translate_text =
|
||||
_wire__crate__api__ort_api__translate_textPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__ort_api__translate_text_batch(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> model_key,
|
||||
ffi.Pointer<wire_cst_list_String> texts,
|
||||
) {
|
||||
return _wire__crate__api__ort_api__translate_text_batch(
|
||||
port_,
|
||||
model_key,
|
||||
texts,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__ort_api__translate_text_batchPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__ort_api__translate_text_batch',
|
||||
);
|
||||
late final _wire__crate__api__ort_api__translate_text_batch =
|
||||
_wire__crate__api__ort_api__translate_text_batchPtr
|
||||
.asFunction<
|
||||
void Function(
|
||||
int,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
ffi.Pointer<wire_cst_list_String>,
|
||||
)
|
||||
>();
|
||||
|
||||
void wire__crate__api__ort_api__unload_translation_model(
|
||||
int port_,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict> model_key,
|
||||
) {
|
||||
return _wire__crate__api__ort_api__unload_translation_model(
|
||||
port_,
|
||||
model_key,
|
||||
);
|
||||
}
|
||||
|
||||
late final _wire__crate__api__ort_api__unload_translation_modelPtr =
|
||||
_lookup<
|
||||
ffi.NativeFunction<
|
||||
ffi.Void Function(
|
||||
ffi.Int64,
|
||||
ffi.Pointer<wire_cst_list_prim_u_8_strict>,
|
||||
)
|
||||
>
|
||||
>(
|
||||
'frbgen_starcitizen_doctor_wire__crate__api__ort_api__unload_translation_model',
|
||||
);
|
||||
late final _wire__crate__api__ort_api__unload_translation_model =
|
||||
_wire__crate__api__ort_api__unload_translation_modelPtr
|
||||
.asFunction<
|
||||
void Function(int, ffi.Pointer<wire_cst_list_prim_u_8_strict>)
|
||||
>();
|
||||
|
||||
void wire__crate__api__rs_process__write(
|
||||
int port_,
|
||||
int rs_pid,
|
||||
|
||||
@ -2,6 +2,8 @@ import 'dart:convert';
|
||||
|
||||
import 'package:desktop_multi_window/desktop_multi_window.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
@ -11,6 +13,7 @@ import 'package:starcitizen_doctor/common/conf/conf.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/log_helper.dart';
|
||||
import 'package:starcitizen_doctor/generated/l10n.dart';
|
||||
import 'package:starcitizen_doctor/ui/tools/log_analyze_ui/log_analyze_ui.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'base_utils.dart';
|
||||
|
||||
@ -18,6 +21,15 @@ part 'multi_window_manager.freezed.dart';
|
||||
|
||||
part 'multi_window_manager.g.dart';
|
||||
|
||||
/// Window type definitions for multi-window support
|
||||
class WindowTypes {
|
||||
/// Main application window
|
||||
static const String main = 'main';
|
||||
|
||||
/// Log analyzer window
|
||||
static const String logAnalyze = 'log_analyze';
|
||||
}
|
||||
|
||||
@freezed
|
||||
abstract class MultiWindowAppState with _$MultiWindowAppState {
|
||||
const factory MultiWindowAppState({
|
||||
@ -27,35 +39,49 @@ abstract class MultiWindowAppState with _$MultiWindowAppState {
|
||||
required List<String> gameInstallPaths,
|
||||
String? languageCode,
|
||||
String? countryCode,
|
||||
@Default(10) windowsVersion,
|
||||
}) = _MultiWindowAppState;
|
||||
|
||||
factory MultiWindowAppState.fromJson(Map<String, dynamic> json) => _$MultiWindowAppStateFromJson(json);
|
||||
}
|
||||
|
||||
class MultiWindowManager {
|
||||
static Future<void> launchSubWindow(String type, String title, AppGlobalState appGlobalState) async {
|
||||
final gameInstallPaths = await SCLoggerHelper.getGameInstallPath(await SCLoggerHelper.getLauncherLogList() ?? [],
|
||||
checkExists: true, withVersion: AppConf.gameChannels);
|
||||
final window = await DesktopMultiWindow.createWindow(jsonEncode({
|
||||
'window_type': type,
|
||||
'app_state': _appStateToWindowState(
|
||||
appGlobalState,
|
||||
gameInstallPaths: gameInstallPaths,
|
||||
).toJson(),
|
||||
}));
|
||||
window.setFrame(const Rect.fromLTWH(0, 0, 900, 1200));
|
||||
window.setTitle(title);
|
||||
await window.center();
|
||||
await window.show();
|
||||
// sendAppStateBroadcast(appGlobalState);
|
||||
/// Parse window type from arguments string
|
||||
static String parseWindowType(String arguments) {
|
||||
if (arguments.isEmpty) {
|
||||
return WindowTypes.main;
|
||||
}
|
||||
try {
|
||||
final Map<String, dynamic> argument = jsonDecode(arguments);
|
||||
return argument['window_type'] ?? WindowTypes.main;
|
||||
} catch (e) {
|
||||
return WindowTypes.main;
|
||||
}
|
||||
}
|
||||
|
||||
static void sendAppStateBroadcast(AppGlobalState appGlobalState) {
|
||||
DesktopMultiWindow.invokeMethod(
|
||||
0,
|
||||
'app_state_broadcast',
|
||||
_appStateToWindowState(appGlobalState).toJson(),
|
||||
/// Launch a sub-window with specified type and title
|
||||
static Future<void> launchSubWindow(String type, String title, AppGlobalState appGlobalState) async {
|
||||
final gameInstallPaths = await SCLoggerHelper.getGameInstallPath(
|
||||
await SCLoggerHelper.getLauncherLogList() ?? [],
|
||||
checkExists: true,
|
||||
withVersion: AppConf.gameChannels,
|
||||
);
|
||||
|
||||
final controller = await WindowController.create(
|
||||
WindowConfiguration(
|
||||
hiddenAtLaunch: true,
|
||||
arguments: jsonEncode({
|
||||
'window_type': type,
|
||||
'app_state': _appStateToWindowState(appGlobalState, gameInstallPaths: gameInstallPaths).toJson(),
|
||||
}),
|
||||
),
|
||||
);
|
||||
await Future.delayed(Duration(milliseconds: 500)).then((_) async {
|
||||
await controller.setFrame(const Rect.fromLTWH(0, 0, 800, 1200));
|
||||
await controller.setTitle(title);
|
||||
await controller.center();
|
||||
await controller.show();
|
||||
});
|
||||
}
|
||||
|
||||
static MultiWindowAppState _appStateToWindowState(AppGlobalState appGlobalState, {List<String>? gameInstallPaths}) {
|
||||
@ -66,53 +92,147 @@ class MultiWindowManager {
|
||||
languageCode: appGlobalState.appLocale?.languageCode,
|
||||
countryCode: appGlobalState.appLocale?.countryCode,
|
||||
gameInstallPaths: gameInstallPaths ?? [],
|
||||
windowsVersion: appGlobalState.windowsVersion,
|
||||
);
|
||||
}
|
||||
|
||||
static void runSubWindowApp(List<String> args) {
|
||||
final argument = args[2].isEmpty ? const {} : jsonDecode(args[2]) as Map<String, dynamic>;
|
||||
/// Run sub-window app with parsed arguments
|
||||
static Future<void> runSubWindowApp(String arguments, String windowType) async {
|
||||
final Map<String, dynamic> argument = arguments.isEmpty ? const {} : jsonDecode(arguments);
|
||||
final windowAppState = MultiWindowAppState.fromJson(argument['app_state'] ?? {});
|
||||
Widget? windowWidget;
|
||||
switch (argument["window_type"]) {
|
||||
case "log_analyze":
|
||||
|
||||
switch (windowType) {
|
||||
case WindowTypes.logAnalyze:
|
||||
windowWidget = ToolsLogAnalyzeDialogUI(appState: windowAppState);
|
||||
break;
|
||||
default:
|
||||
throw Exception('Unknown window type');
|
||||
throw Exception('Unknown window type: $windowType');
|
||||
}
|
||||
return runApp(ProviderScope(
|
||||
child: FluentApp(
|
||||
title: "StarCitizenToolBox",
|
||||
restorationScopeId: "StarCitizenToolBox",
|
||||
themeMode: ThemeMode.dark,
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
FluentLocalizations.delegate,
|
||||
S.delegate,
|
||||
],
|
||||
supportedLocales: S.delegate.supportedLocales,
|
||||
home: windowWidget,
|
||||
theme: FluentThemeData(
|
||||
|
||||
await Window.initialize();
|
||||
|
||||
if (windowAppState.windowsVersion >= 10) {
|
||||
await Window.setEffect(effect: WindowEffect.acrylic);
|
||||
}
|
||||
|
||||
final backgroundColor = HexColor(windowAppState.backgroundColor).withValues(alpha: .1);
|
||||
|
||||
return runApp(
|
||||
ProviderScope(
|
||||
child: FluentApp(
|
||||
title: "StarCitizenToolBox",
|
||||
restorationScopeId: "StarCitizenToolBox",
|
||||
themeMode: ThemeMode.dark,
|
||||
localizationsDelegates: const [
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
FluentLocalizations.delegate,
|
||||
S.delegate,
|
||||
],
|
||||
supportedLocales: S.delegate.supportedLocales,
|
||||
home: windowWidget,
|
||||
theme: FluentThemeData(
|
||||
brightness: Brightness.dark,
|
||||
fontFamily: "SourceHanSansCN-Regular",
|
||||
navigationPaneTheme: NavigationPaneThemeData(
|
||||
backgroundColor: HexColor(windowAppState.backgroundColor),
|
||||
),
|
||||
navigationPaneTheme: NavigationPaneThemeData(backgroundColor: backgroundColor),
|
||||
menuColor: HexColor(windowAppState.menuColor),
|
||||
micaBackgroundColor: HexColor(windowAppState.micaColor),
|
||||
scaffoldBackgroundColor: backgroundColor,
|
||||
buttonTheme: ButtonThemeData(
|
||||
defaultButtonStyle: ButtonStyle(
|
||||
shape: WidgetStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
side: BorderSide(color: Colors.white.withValues(alpha: .01)))),
|
||||
))),
|
||||
locale: windowAppState.languageCode != null
|
||||
? Locale(windowAppState.languageCode!, windowAppState.countryCode)
|
||||
: null,
|
||||
debugShowCheckedModeBanner: false,
|
||||
defaultButtonStyle: ButtonStyle(
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
side: BorderSide(color: Colors.white.withValues(alpha: .01)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
locale: windowAppState.languageCode != null
|
||||
? Locale(windowAppState.languageCode!, windowAppState.countryCode)
|
||||
: null,
|
||||
debugShowCheckedModeBanner: false,
|
||||
),
|
||||
),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension methods for WindowController to add custom functionality
|
||||
extension WindowControllerExtension on WindowController {
|
||||
/// Initialize custom window method handlers
|
||||
Future<void> doCustomInitialize() async {
|
||||
windowManager.ensureInitialized();
|
||||
return await setWindowMethodHandler((call) async {
|
||||
switch (call.method) {
|
||||
case 'window_center':
|
||||
return await windowManager.center();
|
||||
case 'window_close':
|
||||
return await windowManager.close();
|
||||
case 'window_show':
|
||||
return await windowManager.show();
|
||||
case 'window_hide':
|
||||
return await windowManager.hide();
|
||||
case 'window_focus':
|
||||
return await windowManager.focus();
|
||||
case 'window_set_frame':
|
||||
final args = call.arguments as Map;
|
||||
return await windowManager.setBounds(
|
||||
Rect.fromLTWH(
|
||||
args['left'] as double,
|
||||
args['top'] as double,
|
||||
args['width'] as double,
|
||||
args['height'] as double,
|
||||
),
|
||||
);
|
||||
case 'window_set_title':
|
||||
return await windowManager.setTitle(call.arguments as String);
|
||||
default:
|
||||
throw MissingPluginException('Not implemented: ${call.method}');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// Center the window
|
||||
Future<void> center() {
|
||||
return invokeMethod('window_center');
|
||||
}
|
||||
|
||||
/// Close the window
|
||||
void close() async {
|
||||
await invokeMethod('window_close');
|
||||
}
|
||||
|
||||
/// Show the window
|
||||
Future<void> show() {
|
||||
return invokeMethod('window_show');
|
||||
}
|
||||
|
||||
/// Hide the window
|
||||
Future<void> hide() {
|
||||
return invokeMethod('window_hide');
|
||||
}
|
||||
|
||||
/// Focus the window
|
||||
Future<void> focus() {
|
||||
return invokeMethod('window_focus');
|
||||
}
|
||||
|
||||
/// Set window frame (position and size)
|
||||
Future<void> setFrame(Rect frame) {
|
||||
return invokeMethod('window_set_frame', {
|
||||
'left': frame.left,
|
||||
'top': frame.top,
|
||||
'width': frame.width,
|
||||
'height': frame.height,
|
||||
});
|
||||
}
|
||||
|
||||
/// Set window title
|
||||
Future<void> setTitle(String title) {
|
||||
return invokeMethod('window_set_title', title);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ T _$identity<T>(T value) => value;
|
||||
/// @nodoc
|
||||
mixin _$MultiWindowAppState {
|
||||
|
||||
String get backgroundColor; String get menuColor; String get micaColor; List<String> get gameInstallPaths; String? get languageCode; String? get countryCode;
|
||||
String get backgroundColor; String get menuColor; String get micaColor; List<String> get gameInstallPaths; String? get languageCode; String? get countryCode; dynamic get windowsVersion;
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -28,16 +28,16 @@ $MultiWindowAppStateCopyWith<MultiWindowAppState> get copyWith => _$MultiWindowA
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other.gameInstallPaths, gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other.gameInstallPaths, gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(gameInstallPaths),languageCode,countryCode);
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(gameInstallPaths),languageCode,countryCode,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode)';
|
||||
return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ abstract mixin class $MultiWindowAppStateCopyWith<$Res> {
|
||||
factory $MultiWindowAppStateCopyWith(MultiWindowAppState value, $Res Function(MultiWindowAppState) _then) = _$MultiWindowAppStateCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode
|
||||
String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ class _$MultiWindowAppStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_self.copyWith(
|
||||
backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable
|
||||
@ -73,7 +73,8 @@ as String,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore:
|
||||
as String,gameInstallPaths: null == gameInstallPaths ? _self.gameInstallPaths : gameInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,languageCode: freezed == languageCode ? _self.languageCode : languageCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
as String?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
@ -158,10 +159,10 @@ return $default(_that);case _:
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion)? $default,{required TResult orElse(),}) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState() when $default != null:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode);case _:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _:
|
||||
return orElse();
|
||||
|
||||
}
|
||||
@ -179,10 +180,10 @@ return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.game
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode) $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion) $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState():
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode);case _:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _:
|
||||
throw StateError('Unexpected subclass');
|
||||
|
||||
}
|
||||
@ -199,10 +200,10 @@ return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.game
|
||||
/// }
|
||||
/// ```
|
||||
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode)? $default,) {final _that = this;
|
||||
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion)? $default,) {final _that = this;
|
||||
switch (_that) {
|
||||
case _MultiWindowAppState() when $default != null:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode);case _:
|
||||
return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.gameInstallPaths,_that.languageCode,_that.countryCode,_that.windowsVersion);case _:
|
||||
return null;
|
||||
|
||||
}
|
||||
@ -214,7 +215,7 @@ return $default(_that.backgroundColor,_that.menuColor,_that.micaColor,_that.game
|
||||
@JsonSerializable()
|
||||
|
||||
class _MultiWindowAppState implements MultiWindowAppState {
|
||||
const _MultiWindowAppState({required this.backgroundColor, required this.menuColor, required this.micaColor, required final List<String> gameInstallPaths, this.languageCode, this.countryCode}): _gameInstallPaths = gameInstallPaths;
|
||||
const _MultiWindowAppState({required this.backgroundColor, required this.menuColor, required this.micaColor, required final List<String> gameInstallPaths, this.languageCode, this.countryCode, this.windowsVersion = 10}): _gameInstallPaths = gameInstallPaths;
|
||||
factory _MultiWindowAppState.fromJson(Map<String, dynamic> json) => _$MultiWindowAppStateFromJson(json);
|
||||
|
||||
@override final String backgroundColor;
|
||||
@ -229,6 +230,7 @@ class _MultiWindowAppState implements MultiWindowAppState {
|
||||
|
||||
@override final String? languageCode;
|
||||
@override final String? countryCode;
|
||||
@override@JsonKey() final dynamic windowsVersion;
|
||||
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@ -243,16 +245,16 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other._gameInstallPaths, _gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode));
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _MultiWindowAppState&&(identical(other.backgroundColor, backgroundColor) || other.backgroundColor == backgroundColor)&&(identical(other.menuColor, menuColor) || other.menuColor == menuColor)&&(identical(other.micaColor, micaColor) || other.micaColor == micaColor)&&const DeepCollectionEquality().equals(other._gameInstallPaths, _gameInstallPaths)&&(identical(other.languageCode, languageCode) || other.languageCode == languageCode)&&(identical(other.countryCode, countryCode) || other.countryCode == countryCode)&&const DeepCollectionEquality().equals(other.windowsVersion, windowsVersion));
|
||||
}
|
||||
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@override
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(_gameInstallPaths),languageCode,countryCode);
|
||||
int get hashCode => Object.hash(runtimeType,backgroundColor,menuColor,micaColor,const DeepCollectionEquality().hash(_gameInstallPaths),languageCode,countryCode,const DeepCollectionEquality().hash(windowsVersion));
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode)';
|
||||
return 'MultiWindowAppState(backgroundColor: $backgroundColor, menuColor: $menuColor, micaColor: $micaColor, gameInstallPaths: $gameInstallPaths, languageCode: $languageCode, countryCode: $countryCode, windowsVersion: $windowsVersion)';
|
||||
}
|
||||
|
||||
|
||||
@ -263,7 +265,7 @@ abstract mixin class _$MultiWindowAppStateCopyWith<$Res> implements $MultiWindow
|
||||
factory _$MultiWindowAppStateCopyWith(_MultiWindowAppState value, $Res Function(_MultiWindowAppState) _then) = __$MultiWindowAppStateCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode
|
||||
String backgroundColor, String menuColor, String micaColor, List<String> gameInstallPaths, String? languageCode, String? countryCode, dynamic windowsVersion
|
||||
});
|
||||
|
||||
|
||||
@ -280,7 +282,7 @@ class __$MultiWindowAppStateCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of MultiWindowAppState
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? backgroundColor = null,Object? menuColor = null,Object? micaColor = null,Object? gameInstallPaths = null,Object? languageCode = freezed,Object? countryCode = freezed,Object? windowsVersion = freezed,}) {
|
||||
return _then(_MultiWindowAppState(
|
||||
backgroundColor: null == backgroundColor ? _self.backgroundColor : backgroundColor // ignore: cast_nullable_to_non_nullable
|
||||
as String,menuColor: null == menuColor ? _self.menuColor : menuColor // ignore: cast_nullable_to_non_nullable
|
||||
@ -288,7 +290,8 @@ as String,micaColor: null == micaColor ? _self.micaColor : micaColor // ignore:
|
||||
as String,gameInstallPaths: null == gameInstallPaths ? _self._gameInstallPaths : gameInstallPaths // ignore: cast_nullable_to_non_nullable
|
||||
as List<String>,languageCode: freezed == languageCode ? _self.languageCode : languageCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,countryCode: freezed == countryCode ? _self.countryCode : countryCode // ignore: cast_nullable_to_non_nullable
|
||||
as String?,
|
||||
as String?,windowsVersion: freezed == windowsVersion ? _self.windowsVersion : windowsVersion // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@ -16,6 +16,7 @@ _MultiWindowAppState _$MultiWindowAppStateFromJson(Map<String, dynamic> json) =>
|
||||
.toList(),
|
||||
languageCode: json['languageCode'] as String?,
|
||||
countryCode: json['countryCode'] as String?,
|
||||
windowsVersion: json['windowsVersion'] ?? 10,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MultiWindowAppStateToJson(
|
||||
@ -27,4 +28,5 @@ Map<String, dynamic> _$MultiWindowAppStateToJson(
|
||||
'gameInstallPaths': instance.gameInstallPaths,
|
||||
'languageCode': instance.languageCode,
|
||||
'countryCode': instance.countryCode,
|
||||
'windowsVersion': instance.windowsVersion,
|
||||
};
|
||||
|
||||
148
lib/main.dart
148
lib/main.dart
@ -11,28 +11,42 @@ import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'app.dart';
|
||||
import 'common/utils/multi_window_manager.dart';
|
||||
|
||||
void main(List<String> args) async {
|
||||
Future<void> main(List<String> args) async {
|
||||
// webview window
|
||||
if (runWebViewTitleBarWidget(args,
|
||||
backgroundColor: const Color.fromRGBO(19, 36, 49, 1), builder: _defaultWebviewTitleBar)) {
|
||||
return;
|
||||
}
|
||||
if (args.firstOrNull == 'multi_window') {
|
||||
MultiWindowManager.runSubWindowApp(args);
|
||||
if (runWebViewTitleBarWidget(
|
||||
args,
|
||||
backgroundColor: const Color.fromRGBO(19, 36, 49, 1),
|
||||
builder: _defaultWebviewTitleBar,
|
||||
)) {
|
||||
return;
|
||||
}
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await _initWindow();
|
||||
// run app
|
||||
runApp(const ProviderScope(child: App()));
|
||||
await windowManager.ensureInitialized();
|
||||
|
||||
// Get the current window controller
|
||||
final windowController = await WindowController.fromCurrentEngine();
|
||||
|
||||
// Parse window arguments to determine which window to show
|
||||
final windowType = MultiWindowManager.parseWindowType(windowController.arguments);
|
||||
|
||||
// Initialize window-specific handlers for sub-windows
|
||||
if (windowType != WindowTypes.main) {
|
||||
await windowController.doCustomInitialize();
|
||||
}
|
||||
|
||||
// Run different apps based on the window type
|
||||
switch (windowType) {
|
||||
case WindowTypes.main:
|
||||
await _initWindow();
|
||||
runApp(const ProviderScope(child: App()));
|
||||
default:
|
||||
MultiWindowManager.runSubWindowApp(windowController.arguments, windowType);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _initWindow() async {
|
||||
await windowManager.ensureInitialized();
|
||||
await windowManager.setTitleBarStyle(
|
||||
TitleBarStyle.hidden,
|
||||
windowButtonVisibility: false,
|
||||
);
|
||||
await windowManager.setTitleBarStyle(TitleBarStyle.hidden, windowButtonVisibility: false);
|
||||
await windowManager.setSize(const Size(1280, 810));
|
||||
await windowManager.setMinimumSize(const Size(1280, 810));
|
||||
await windowManager.center(animate: true);
|
||||
@ -73,18 +87,22 @@ class App extends HookConsumerWidget with WindowListener {
|
||||
);
|
||||
},
|
||||
theme: FluentThemeData(
|
||||
brightness: Brightness.dark,
|
||||
fontFamily: "SourceHanSansCN-Regular",
|
||||
navigationPaneTheme: NavigationPaneThemeData(
|
||||
backgroundColor: appState.themeConf.backgroundColor,
|
||||
brightness: Brightness.dark,
|
||||
fontFamily: "SourceHanSansCN-Regular",
|
||||
navigationPaneTheme: NavigationPaneThemeData(backgroundColor: appState.themeConf.backgroundColor),
|
||||
menuColor: appState.themeConf.menuColor,
|
||||
micaBackgroundColor: appState.themeConf.micaColor,
|
||||
buttonTheme: ButtonThemeData(
|
||||
defaultButtonStyle: ButtonStyle(
|
||||
shape: WidgetStateProperty.all(
|
||||
RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
side: BorderSide(color: Colors.white.withValues(alpha: .01)),
|
||||
),
|
||||
),
|
||||
),
|
||||
menuColor: appState.themeConf.menuColor,
|
||||
micaBackgroundColor: appState.themeConf.micaColor,
|
||||
buttonTheme: ButtonThemeData(
|
||||
defaultButtonStyle: ButtonStyle(
|
||||
shape: WidgetStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(4), side: BorderSide(color: Colors.white.withValues(alpha: .01)))),
|
||||
))),
|
||||
),
|
||||
),
|
||||
locale: appState.appLocale,
|
||||
debugShowCheckedModeBanner: false,
|
||||
routeInformationParser: router.routeInformationParser,
|
||||
@ -97,11 +115,19 @@ class App extends HookConsumerWidget with WindowListener {
|
||||
Future<void> onWindowClose() async {
|
||||
debugPrint("onWindowClose");
|
||||
if (await windowManager.isPreventClose()) {
|
||||
final windows = await DesktopMultiWindow.getAllSubWindowIds();
|
||||
for (final id in windows) {
|
||||
await WindowController.fromWindowId(id).close();
|
||||
final mainWindow = await WindowController.fromCurrentEngine();
|
||||
final windows = await WindowController.getAll();
|
||||
for (final controller in windows) {
|
||||
if (controller.windowId != mainWindow.windowId) {
|
||||
try {
|
||||
controller.close();
|
||||
} catch (e) {
|
||||
debugPrint("Error closing window ${controller.windowId}: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
await windowManager.destroy();
|
||||
await windowManager.setPreventClose(false);
|
||||
await windowManager.close();
|
||||
exit(0);
|
||||
}
|
||||
super.onWindowClose();
|
||||
@ -112,42 +138,28 @@ Widget _defaultWebviewTitleBar(BuildContext context) {
|
||||
final state = TitleBarWebViewState.of(context);
|
||||
final controller = TitleBarWebViewController.of(context);
|
||||
return FluentTheme(
|
||||
data: FluentThemeData.dark(),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (Platform.isMacOS) const SizedBox(width: 96),
|
||||
IconButton(
|
||||
onPressed: !state.canGoBack ? null : controller.back,
|
||||
icon: const Icon(FluentIcons.chevron_left),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
IconButton(
|
||||
onPressed: !state.canGoForward ? null : controller.forward,
|
||||
icon: const Icon(FluentIcons.chevron_right),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (state.isLoading)
|
||||
IconButton(
|
||||
onPressed: controller.stop,
|
||||
icon: const Icon(FluentIcons.chrome_close),
|
||||
)
|
||||
else
|
||||
IconButton(
|
||||
onPressed: controller.reload,
|
||||
icon: const Icon(FluentIcons.refresh),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
(state.isLoading)
|
||||
? const SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: ProgressRing(),
|
||||
)
|
||||
: const SizedBox(width: 24),
|
||||
const SizedBox(width: 12),
|
||||
SelectableText(state.url ?? ""),
|
||||
const Spacer()
|
||||
],
|
||||
));
|
||||
data: FluentThemeData.dark(),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (Platform.isMacOS) const SizedBox(width: 96),
|
||||
IconButton(onPressed: !state.canGoBack ? null : controller.back, icon: const Icon(FluentIcons.chevron_left)),
|
||||
const SizedBox(width: 12),
|
||||
IconButton(
|
||||
onPressed: !state.canGoForward ? null : controller.forward,
|
||||
icon: const Icon(FluentIcons.chevron_right),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (state.isLoading)
|
||||
IconButton(onPressed: controller.stop, icon: const Icon(FluentIcons.chrome_close))
|
||||
else
|
||||
IconButton(onPressed: controller.reload, icon: const Icon(FluentIcons.refresh)),
|
||||
const SizedBox(width: 12),
|
||||
(state.isLoading) ? const SizedBox(width: 24, height: 24, child: ProgressRing()) : const SizedBox(width: 24),
|
||||
const SizedBox(width: 12),
|
||||
SelectableText(state.url ?? ""),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@ -8,11 +8,11 @@ import 'package:flutter/foundation.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:starcitizen_doctor/api/api.dart';
|
||||
import 'package:starcitizen_doctor/common/helper/system_helper.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/rs_process.dart'
|
||||
as rs_process;
|
||||
import 'package:starcitizen_doctor/common/rust/api/rs_process.dart' as rs_process;
|
||||
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/downloader/home_downloader_ui_model.dart';
|
||||
|
||||
part 'aria2c.g.dart';
|
||||
|
||||
@ -20,11 +20,8 @@ part 'aria2c.freezed.dart';
|
||||
|
||||
@freezed
|
||||
abstract class Aria2cModelState with _$Aria2cModelState {
|
||||
const factory Aria2cModelState({
|
||||
required String aria2cDir,
|
||||
Aria2c? aria2c,
|
||||
Aria2GlobalStat? aria2globalStat,
|
||||
}) = _Aria2cModelState;
|
||||
const factory Aria2cModelState({required String aria2cDir, Aria2c? aria2c, Aria2GlobalStat? aria2globalStat}) =
|
||||
_Aria2cModelState;
|
||||
}
|
||||
|
||||
extension Aria2cModelExt on Aria2cModelState {
|
||||
@ -32,10 +29,8 @@ extension Aria2cModelExt on Aria2cModelState {
|
||||
|
||||
bool get hasDownloadTask => aria2globalStat != null && aria2TotalTaskNum > 0;
|
||||
|
||||
int get aria2TotalTaskNum => aria2globalStat == null
|
||||
? 0
|
||||
: ((aria2globalStat!.numActive ?? 0) +
|
||||
(aria2globalStat!.numWaiting ?? 0));
|
||||
int get aria2TotalTaskNum =>
|
||||
aria2globalStat == null ? 0 : ((aria2globalStat!.numActive ?? 0) + (aria2globalStat!.numWaiting ?? 0));
|
||||
}
|
||||
|
||||
@riverpod
|
||||
@ -57,16 +52,16 @@ class Aria2cModel extends _$Aria2cModel {
|
||||
try {
|
||||
final sessionFile = File("$aria2cDir\\aria2.session");
|
||||
// 有下载任务则第一时间初始化
|
||||
if (await sessionFile.exists() &&
|
||||
(await sessionFile.readAsString()).trim().isNotEmpty) {
|
||||
dPrint("launch Aria2c daemon");
|
||||
await launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
} else {
|
||||
dPrint("LazyLoad Aria2c daemon");
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("Aria2cManager.checkLazyLoad Error:$e");
|
||||
}
|
||||
if (await sessionFile.exists ()
|
||||
&& (await sessionFile.readAsString()).trim().isNotEmpty) {
|
||||
dPrint("launch Aria2c daemon");
|
||||
await launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
} else {
|
||||
dPrint("LazyLoad Aria2c daemon");
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("Aria2cManager.checkLazyLoad Error:$e");
|
||||
}
|
||||
}();
|
||||
|
||||
return Aria2cModelState(aria2cDir: aria2cDir);
|
||||
@ -74,8 +69,7 @@ class Aria2cModel extends _$Aria2cModel {
|
||||
|
||||
Future launchDaemon(String applicationBinaryModuleDir) async {
|
||||
if (state.aria2c != null) return;
|
||||
await BinaryModuleConf.extractModule(
|
||||
["aria2c"], applicationBinaryModuleDir);
|
||||
await BinaryModuleConf.extractModule(["aria2c"], applicationBinaryModuleDir);
|
||||
|
||||
/// skip for debug hot reload
|
||||
if (kDebugMode) {
|
||||
@ -99,30 +93,30 @@ class Aria2cModel extends _$Aria2cModel {
|
||||
dPrint("Aria2cManager .----- aria2c start $port------");
|
||||
|
||||
final stream = rs_process.start(
|
||||
executable: exePath,
|
||||
arguments: [
|
||||
"-V",
|
||||
"-c",
|
||||
"-x 16",
|
||||
"--dir=${state.aria2cDir}\\downloads",
|
||||
"--disable-ipv6",
|
||||
"--enable-rpc",
|
||||
"--pause",
|
||||
"--rpc-listen-port=$port",
|
||||
"--rpc-secret=$pwd",
|
||||
"--input-file=${sessionFile.absolute.path.trim()}",
|
||||
"--save-session=${sessionFile.absolute.path.trim()}",
|
||||
"--save-session-interval=60",
|
||||
"--file-allocation=trunc",
|
||||
"--seed-time=0",
|
||||
],
|
||||
workingDirectory: state.aria2cDir);
|
||||
executable: exePath,
|
||||
arguments: [
|
||||
"-V",
|
||||
"-c",
|
||||
"-x 16",
|
||||
"--dir=${state.aria2cDir}\\downloads",
|
||||
"--disable-ipv6",
|
||||
"--enable-rpc",
|
||||
"--pause",
|
||||
"--rpc-listen-port=$port",
|
||||
"--rpc-secret=$pwd",
|
||||
"--input-file=${sessionFile.absolute.path.trim()}",
|
||||
"--save-session=${sessionFile.absolute.path.trim()}",
|
||||
"--save-session-interval=60",
|
||||
"--file-allocation=trunc",
|
||||
"--seed-time=0",
|
||||
],
|
||||
workingDirectory: state.aria2cDir,
|
||||
);
|
||||
|
||||
String launchError = "";
|
||||
|
||||
stream.listen((event) {
|
||||
dPrint(
|
||||
"Aria2cManager.rs_process event === [${event.rsPid}] ${event.dataType} >> ${event.data}");
|
||||
dPrint("Aria2cManager.rs_process event === [${event.rsPid}] ${event.dataType} >> ${event.data}");
|
||||
switch (event.dataType) {
|
||||
case rs_process.RsProcessStreamDataType.output:
|
||||
if (event.data.contains("IPv4 RPC: listening on TCP port")) {
|
||||
@ -155,8 +149,7 @@ class Aria2cModel extends _$Aria2cModel {
|
||||
}
|
||||
|
||||
String generateRandomPassword(int length) {
|
||||
const String charset =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
const String charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
Random random = Random();
|
||||
StringBuffer buffer = StringBuffer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
@ -190,12 +183,12 @@ class Aria2cModel extends _$Aria2cModel {
|
||||
_listenState(aria2c);
|
||||
});
|
||||
final box = await Hive.openBox("app_conf");
|
||||
aria2c.changeGlobalOption(Aria2Option()
|
||||
..maxOverallUploadLimit =
|
||||
textToByte(box.get("downloader_up_limit", defaultValue: "0"))
|
||||
..maxOverallDownloadLimit =
|
||||
textToByte(box.get("downloader_down_limit", defaultValue: "0"))
|
||||
..btTracker = trackerList);
|
||||
aria2c.changeGlobalOption(
|
||||
Aria2Option()
|
||||
..maxOverallUploadLimit = textToByte(box.get("downloader_up_limit", defaultValue: "0"))
|
||||
..maxOverallDownloadLimit = textToByte(box.get("downloader_down_limit", defaultValue: "0"))
|
||||
..btTracker = trackerList,
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _listenState(Aria2c aria2c) async {
|
||||
@ -214,4 +207,16 @@ class Aria2cModel extends _$Aria2cModel {
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> isNameInTask(String name) async {
|
||||
final aria2c = state.aria2c;
|
||||
if (aria2c == null) return false;
|
||||
for (var value in [...await aria2c.tellActive(), ...await aria2c.tellWaiting(0, 100000)]) {
|
||||
final t = HomeDownloaderUIModel.getTaskTypeAndName(value);
|
||||
if (t.key == "torrent" && t.value.contains(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ final class Aria2cModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$aria2cModelHash() => r'3d51aeefd92e5291dca1f01db961f9c5496ec24f';
|
||||
String _$aria2cModelHash() => r'17956c60a79c68ae13b8b8e700ebbafb70e93194';
|
||||
|
||||
abstract class _$Aria2cModel extends $Notifier<Aria2cModelState> {
|
||||
Aria2cModelState build();
|
||||
|
||||
@ -79,9 +79,10 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
return;
|
||||
case "cancel_all":
|
||||
final userOK = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_all_tasks,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note));
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_all_tasks,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note),
|
||||
);
|
||||
if (userOK == true) {
|
||||
if (!aria2cState.isRunning) return;
|
||||
try {
|
||||
@ -101,31 +102,19 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
}
|
||||
|
||||
int getTasksLen() {
|
||||
return state.tasks.length +
|
||||
state.waitingTasks.length +
|
||||
state.stoppedTasks.length;
|
||||
return state.tasks.length + state.waitingTasks.length + state.stoppedTasks.length;
|
||||
}
|
||||
|
||||
(Aria2Task, String, bool) getTaskAndType(int index) {
|
||||
final tempList = <Aria2Task>[
|
||||
...state.tasks,
|
||||
...state.waitingTasks,
|
||||
...state.stoppedTasks
|
||||
];
|
||||
final tempList = <Aria2Task>[...state.tasks, ...state.waitingTasks, ...state.stoppedTasks];
|
||||
if (index >= 0 && index < state.tasks.length) {
|
||||
return (tempList[index], "active", index == 0);
|
||||
}
|
||||
if (index >= state.tasks.length &&
|
||||
index < state.tasks.length + state.waitingTasks.length) {
|
||||
if (index >= state.tasks.length && index < state.tasks.length + state.waitingTasks.length) {
|
||||
return (tempList[index], "waiting", index == state.tasks.length);
|
||||
}
|
||||
if (index >= state.tasks.length + state.waitingTasks.length &&
|
||||
index < tempList.length) {
|
||||
return (
|
||||
tempList[index],
|
||||
"stopped",
|
||||
index == state.tasks.length + state.waitingTasks.length
|
||||
);
|
||||
if (index >= state.tasks.length + state.waitingTasks.length && index < tempList.length) {
|
||||
return (tempList[index], "stopped", index == state.tasks.length + state.waitingTasks.length);
|
||||
}
|
||||
throw Exception("Index out of range or element is null");
|
||||
}
|
||||
@ -148,8 +137,7 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
|
||||
int getETA(Aria2Task task) {
|
||||
if (task.downloadSpeed == null || task.downloadSpeed == 0) return 0;
|
||||
final remainingBytes =
|
||||
(task.totalLength ?? 0) - (task.completedLength ?? 0);
|
||||
final remainingBytes = (task.totalLength ?? 0) - (task.completedLength ?? 0);
|
||||
return remainingBytes ~/ (task.downloadSpeed!);
|
||||
}
|
||||
|
||||
@ -172,9 +160,10 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
if (gid != null) {
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_download,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note));
|
||||
context,
|
||||
S.current.downloader_action_confirm_cancel_download,
|
||||
Text(S.current.downloader_info_manual_file_deletion_note),
|
||||
);
|
||||
if (ok == true) {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c;
|
||||
await aria2c?.remove(gid);
|
||||
@ -204,8 +193,8 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
Future<void> _listenDownloader() async {
|
||||
try {
|
||||
while (true) {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
if (_disposed) return;
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
if (aria2cState.isRunning) {
|
||||
final aria2c = aria2cState.aria2c!;
|
||||
final tasks = await aria2c.tellActive();
|
||||
@ -219,12 +208,7 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
globalStat: globalStat,
|
||||
);
|
||||
} else {
|
||||
state = state.copyWith(
|
||||
tasks: [],
|
||||
waitingTasks: [],
|
||||
stoppedTasks: [],
|
||||
globalStat: null,
|
||||
);
|
||||
state = state.copyWith(tasks: [], waitingTasks: [], stoppedTasks: [], globalStat: null);
|
||||
}
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
}
|
||||
@ -236,72 +220,64 @@ class HomeDownloaderUIModel extends _$HomeDownloaderUIModel {
|
||||
Future<void> _showDownloadSpeedSettings(BuildContext context) async {
|
||||
final box = await Hive.openBox("app_conf");
|
||||
|
||||
final upCtrl = TextEditingController(
|
||||
text: box.get("downloader_up_limit", defaultValue: ""));
|
||||
final downCtrl = TextEditingController(
|
||||
text: box.get("downloader_down_limit", defaultValue: ""));
|
||||
final upCtrl = TextEditingController(text: box.get("downloader_up_limit", defaultValue: ""));
|
||||
final downCtrl = TextEditingController(text: box.get("downloader_down_limit", defaultValue: ""));
|
||||
|
||||
final ifr = FilteringTextInputFormatter.allow(RegExp(r'^\d*[km]?$'));
|
||||
|
||||
if (!context.mounted) return;
|
||||
final ok = await showConfirmDialogs(
|
||||
context,
|
||||
S.current.downloader_speed_limit_settings,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
S.current.downloader_info_p2p_network_note,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(S.current.downloader_info_download_unit_input_prompt),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.downloader_input_upload_speed_limit),
|
||||
const SizedBox(height: 6),
|
||||
TextFormBox(
|
||||
placeholder: "1、100k、10m、0",
|
||||
controller: upCtrl,
|
||||
placeholderStyle:
|
||||
TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
inputFormatters: [ifr],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.downloader_input_download_speed_limit),
|
||||
const SizedBox(height: 6),
|
||||
TextFormBox(
|
||||
placeholder: "1、100k、10m、0",
|
||||
controller: downCtrl,
|
||||
placeholderStyle:
|
||||
TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
inputFormatters: [ifr],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
S.current.downloader_input_info_p2p_upload_note,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.white.withValues(alpha: .6),
|
||||
),
|
||||
)
|
||||
],
|
||||
));
|
||||
context,
|
||||
S.current.downloader_speed_limit_settings,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
S.current.downloader_info_p2p_network_note,
|
||||
style: TextStyle(fontSize: 14, color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(S.current.downloader_info_download_unit_input_prompt),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.downloader_input_upload_speed_limit),
|
||||
const SizedBox(height: 6),
|
||||
TextFormBox(
|
||||
placeholder: "1、100k、10m、0",
|
||||
controller: upCtrl,
|
||||
placeholderStyle: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
inputFormatters: [ifr],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(S.current.downloader_input_download_speed_limit),
|
||||
const SizedBox(height: 6),
|
||||
TextFormBox(
|
||||
placeholder: "1、100k、10m、0",
|
||||
controller: downCtrl,
|
||||
placeholderStyle: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
inputFormatters: [ifr],
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
S.current.downloader_input_info_p2p_upload_note,
|
||||
style: TextStyle(fontSize: 13, color: Colors.white.withValues(alpha: .6)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
if (ok == true) {
|
||||
final aria2cState = ref.read(aria2cModelProvider);
|
||||
final aria2cModel = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cModel
|
||||
.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
await aria2cModel.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = aria2cState.aria2c!;
|
||||
final upByte = aria2cModel.textToByte(upCtrl.text.trim());
|
||||
final downByte = aria2cModel.textToByte(downCtrl.text.trim());
|
||||
final r = await aria2c
|
||||
.changeGlobalOption(Aria2Option()
|
||||
..maxOverallUploadLimit = upByte
|
||||
..maxOverallDownloadLimit = downByte)
|
||||
.changeGlobalOption(
|
||||
Aria2Option()
|
||||
..maxOverallUploadLimit = upByte
|
||||
..maxOverallDownloadLimit = downByte,
|
||||
)
|
||||
.unwrap();
|
||||
if (r != null) {
|
||||
await box.put('downloader_up_limit', upCtrl.text.trim());
|
||||
|
||||
@ -42,7 +42,7 @@ final class HomeDownloaderUIModelProvider
|
||||
}
|
||||
|
||||
String _$homeDownloaderUIModelHash() =>
|
||||
r'5b410cd38315d94279b18f147903eca4b09bd445';
|
||||
r'cb5d0973d56bbf40673afc2a734b49f5d034ab98';
|
||||
|
||||
abstract class _$HomeDownloaderUIModel
|
||||
extends $Notifier<HomeDownloaderUIState> {
|
||||
|
||||
@ -41,7 +41,7 @@ final class HomeUIModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$homeUIModelHash() => r'9dc8191f358c2d8e21ed931b3755e08ce394558e';
|
||||
String _$homeUIModelHash() => r'7dfe73383f7be2e520a42d176e199a8db208f008';
|
||||
|
||||
abstract class _$HomeUIModel extends $Notifier<HomeUIModelState> {
|
||||
HomeUIModelState build();
|
||||
|
||||
@ -3,6 +3,7 @@ import 'package:flutter/services.dart';
|
||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/input_method/input_method_dialog_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/input_method/server.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
@ -60,7 +61,9 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
TextFormBox(
|
||||
placeholder: S.current.input_method_input_placeholder,
|
||||
placeholder: state.isEnableAutoTranslate
|
||||
? "${S.current.input_method_input_placeholder}\n\n本地翻译模型对中英混合处理能力较差,如有需要,建议分开发送。"
|
||||
: S.current.input_method_input_placeholder,
|
||||
controller: srcTextCtrl,
|
||||
maxLines: 5,
|
||||
placeholderStyle: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
@ -68,7 +71,9 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
||||
onChanged: (str) async {
|
||||
final text = model.onTextChange("src", str);
|
||||
destTextCtrl.text = text ?? "";
|
||||
if (text != null) {}
|
||||
if (text != null) {
|
||||
model.checkAutoTranslate();
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
@ -91,17 +96,23 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
||||
placeholderStyle: TextStyle(color: Colors.white.withValues(alpha: .6)),
|
||||
style: TextStyle(fontSize: 16, color: Colors.white),
|
||||
enabled: true,
|
||||
onChanged: (str) {
|
||||
// final text = model.onTextChange("dest", str);
|
||||
// if (text != null) {
|
||||
// srcTextCtrl.text = text;
|
||||
// }
|
||||
},
|
||||
onChanged: (str) {},
|
||||
),
|
||||
SizedBox(height: 24),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(S.current.input_method_auto_translate),
|
||||
SizedBox(width: 6),
|
||||
ToggleSwitch(
|
||||
checked: state.isEnableAutoTranslate,
|
||||
onChanged: (b) => _onSwitchAutoTranslate(context, model, b),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(width: 24),
|
||||
Row(
|
||||
children: [
|
||||
Text(S.current.input_method_remote_input_service),
|
||||
@ -194,4 +205,52 @@ class InputMethodDialogUI extends HookConsumerWidget {
|
||||
await serverModel.stopServer().unwrap(context: context);
|
||||
}
|
||||
}
|
||||
|
||||
void _onSwitchAutoTranslate(BuildContext context, InputMethodDialogUIModel model, bool b) async {
|
||||
if (b) {
|
||||
// 检查下载任务
|
||||
if (await model.isTranslateModelDownloading()) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, "模型正在下载中,请稍后...");
|
||||
return;
|
||||
}
|
||||
// 打开,检查本地模型
|
||||
if (!await model.checkLocalTranslateModelAvailable()) {
|
||||
if (!context.mounted) return;
|
||||
// 询问用户是否下载模型
|
||||
final userOK = await showConfirmDialogs(
|
||||
context,
|
||||
"是否下载 AI 模型以使用翻译功能?",
|
||||
Text(
|
||||
"大约需要 200MB 的本地空间。"
|
||||
"\n\n我们使用本地模型进行翻译,您的翻译数据不会发送给任何第三方。"
|
||||
"\n\n模型未对游戏术语优化,请自行判断使用。",
|
||||
),
|
||||
);
|
||||
if (userOK) {
|
||||
try {
|
||||
final guid = await model.doDownloadTranslateModel();
|
||||
if (guid.isNotEmpty) {
|
||||
if (!context.mounted) return;
|
||||
context.go("/index/downloader");
|
||||
await Future.delayed(Duration(seconds: 1)).then((_) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, "下载已开始,请在模型下载完成后重新启用翻译功能。");
|
||||
});
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("下载模型失败:$e");
|
||||
if (context.mounted) {
|
||||
showToast(context, "下载模型失败:$e");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
model.toggleAutoTranslate(b, context: context).unwrap(context: context);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,22 @@
|
||||
// ignore_for_file: avoid_build_context_in_providers, use_build_context_synchronously
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:starcitizen_doctor/api/api.dart';
|
||||
import 'package:starcitizen_doctor/common/io/rs_http.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/async.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/base_utils.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/localization/localization_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/common/rust/api/ort_api.dart' as ort;
|
||||
|
||||
part 'input_method_dialog_ui_model.g.dart';
|
||||
|
||||
@ -44,7 +54,8 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
||||
final worldMaps = keyMaps?.map((key, value) => MapEntry(value.trim(), key));
|
||||
final appBox = await Hive.openBox("app_conf");
|
||||
final enableAutoCopy = appBox.get("enableAutoCopy", defaultValue: false);
|
||||
final isEnableAutoTranslate = appBox.get("isEnableAutoTranslate", defaultValue: false);
|
||||
final isEnableAutoTranslate = appBox.get("isEnableAutoTranslate_v2", defaultValue: false);
|
||||
_checkAutoTranslateOnInit();
|
||||
state = state.copyWith(
|
||||
keyMaps: keyMaps,
|
||||
worldMaps: worldMaps,
|
||||
@ -134,14 +145,216 @@ class InputMethodDialogUIModel extends _$InputMethodDialogUIModel {
|
||||
_srcTextCtrl?.text = text;
|
||||
_destTextCtrl?.text = onTextChange("src", text) ?? "";
|
||||
if (_destTextCtrl?.text.isEmpty ?? true) return;
|
||||
checkAutoTranslate(webMessage: true);
|
||||
if (autoCopy && !state.isAutoTranslateWorking) {
|
||||
Clipboard.setData(ClipboardData(text: _destTextCtrl?.text ?? ""));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> toggleAutoTranslate(bool b) async {
|
||||
// ignore: duplicate_ignore
|
||||
// ignore: avoid_build_context_in_providers
|
||||
Future<void> toggleAutoTranslate(bool b, {BuildContext? context}) async {
|
||||
state = state.copyWith(isEnableAutoTranslate: b);
|
||||
final appConf = await Hive.openBox("app_conf");
|
||||
await appConf.put("isEnableAutoTranslate", b);
|
||||
await appConf.put("isEnableAutoTranslate_v2", b);
|
||||
if (b) {
|
||||
mountOnnxTranslationProvider(_localTranslateModelDir, _localTranslateModelName, context: context);
|
||||
}
|
||||
}
|
||||
|
||||
Timer? _translateTimer;
|
||||
|
||||
Future<void> checkAutoTranslate({bool webMessage = false}) async {
|
||||
final sourceText = _srcTextCtrl?.text ?? "";
|
||||
final content = _destTextCtrl?.text ?? "";
|
||||
if (sourceText.trim().isEmpty) return;
|
||||
if (state.isEnableAutoTranslate) {
|
||||
if (_translateTimer != null) _translateTimer?.cancel();
|
||||
state = state.copyWith(isAutoTranslateWorking: true);
|
||||
_translateTimer = Timer(Duration(milliseconds: webMessage ? 150 : 400), () async {
|
||||
try {
|
||||
final inputText = sourceText.replaceAll("\n", " ");
|
||||
final r = await doTranslateText(inputText);
|
||||
if (r != null) {
|
||||
String resultText = r;
|
||||
// resultText 首字母大写
|
||||
if (content.isNotEmpty) {
|
||||
final firstChar = resultText.characters.first;
|
||||
resultText = resultText.replaceFirst(firstChar, firstChar.toUpperCase());
|
||||
}
|
||||
_destTextCtrl?.text = "$content \n[en] $resultText";
|
||||
if (state.enableAutoCopy || webMessage) {
|
||||
Clipboard.setData(ClipboardData(text: _destTextCtrl?.text ?? ""));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
dPrint("[InputMethodDialogUIModel] AutoTranslate error: $e");
|
||||
}
|
||||
state = state.copyWith(isAutoTranslateWorking: false);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String get _localTranslateModelName => "opus-mt-zh-en_onnx";
|
||||
|
||||
String get _localTranslateModelDir => "${appGlobalState.applicationSupportDir}/onnx_models";
|
||||
|
||||
OnnxTranslationProvider get _localTranslateModelProvider =>
|
||||
onnxTranslationProvider(_localTranslateModelDir, _localTranslateModelName);
|
||||
|
||||
void _checkAutoTranslateOnInit() {
|
||||
// 检查模型文件是否存在,不存在则关闭自动翻译
|
||||
if (state.isEnableAutoTranslate) {
|
||||
checkLocalTranslateModelAvailable().then((available) {
|
||||
if (!available) {
|
||||
toggleAutoTranslate(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> checkLocalTranslateModelAvailable() async {
|
||||
final fileCheckList = const [
|
||||
"config.json",
|
||||
"tokenizer.json",
|
||||
"vocab.json",
|
||||
"onnx/decoder_model_q4f16.onnx",
|
||||
"onnx/encoder_model_q4f16.onnx",
|
||||
];
|
||||
var allExist = true;
|
||||
for (var fileName in fileCheckList) {
|
||||
final filePath = "$_localTranslateModelDir/$_localTranslateModelName/$fileName";
|
||||
if (!await File(filePath).exists()) {
|
||||
allExist = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return allExist;
|
||||
}
|
||||
|
||||
Future<String> doDownloadTranslateModel() async {
|
||||
state = state.copyWith(isAutoTranslateWorking: true);
|
||||
try {
|
||||
final aria2cManager = ref.read(aria2cModelProvider.notifier);
|
||||
await aria2cManager.launchDaemon(appGlobalState.applicationBinaryModuleDir!);
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c!;
|
||||
|
||||
if (await aria2cManager.isNameInTask(_localTranslateModelName)) {
|
||||
throw Exception("Model is already downloading");
|
||||
}
|
||||
|
||||
final l = await Api.getAppTorrentDataList();
|
||||
final modelTorrent = l.firstWhere(
|
||||
(element) => element.name == _localTranslateModelName,
|
||||
orElse: () => throw Exception("Model torrent not found"),
|
||||
);
|
||||
final torrentUrl = modelTorrent.url;
|
||||
if (torrentUrl?.isEmpty ?? true) {
|
||||
throw Exception("Get model torrent url failed");
|
||||
}
|
||||
// get torrent Data
|
||||
final data = await RSHttp.get(torrentUrl!);
|
||||
final b64Str = base64Encode(data.data!);
|
||||
final gid = await aria2c.addTorrent(b64Str, extraParams: {"dir": _localTranslateModelDir});
|
||||
return gid;
|
||||
} catch (e) {
|
||||
dPrint("[InputMethodDialogUIModel] doDownloadTranslateModel error: $e");
|
||||
rethrow;
|
||||
} finally {
|
||||
state = state.copyWith(isAutoTranslateWorking: false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> mountOnnxTranslationProvider(
|
||||
String localTranslateModelDir,
|
||||
String localTranslateModelName, {
|
||||
BuildContext? context,
|
||||
}) async {
|
||||
if (!ref.exists(_localTranslateModelProvider)) {
|
||||
ref.listen(_localTranslateModelProvider, ((_, _) {}));
|
||||
final err = await ref.read(_localTranslateModelProvider.notifier).initModel();
|
||||
_handleTranslateModel(context, err);
|
||||
} else {
|
||||
// 重新加载
|
||||
final err = await ref.read(_localTranslateModelProvider.notifier).initModel();
|
||||
_handleTranslateModel(context, err);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleTranslateModel(BuildContext? context, String? err) async {
|
||||
if (err != null) {
|
||||
dPrint("[InputMethodDialogUIModel] mountOnnxTranslationProvider failed to init model");
|
||||
if (context != null) {
|
||||
if (!context.mounted) return;
|
||||
final userOK = await showConfirmDialogs(context, "翻译模型加载失败", Text("是否删除本地文件,稍后您可以尝试重新下载。错误信息:\n$err"));
|
||||
if (userOK) {
|
||||
// 删除文件,并禁用开关
|
||||
final dir = Directory("$_localTranslateModelDir/$_localTranslateModelName");
|
||||
if (await dir.exists()) {
|
||||
await dir.delete(recursive: true);
|
||||
dPrint("[InputMethodDialogUIModel] Deleted local translate model files.");
|
||||
toggleAutoTranslate(false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 禁用开关
|
||||
toggleAutoTranslate(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<String?> doTranslateText(String text) async {
|
||||
if (!ref.exists(_localTranslateModelProvider)) {
|
||||
await mountOnnxTranslationProvider(_localTranslateModelDir, _localTranslateModelName);
|
||||
}
|
||||
final onnxTranslationState = ref.read(_localTranslateModelProvider);
|
||||
if (!onnxTranslationState) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
final result = await ort.translateText(modelKey: _localTranslateModelName, text: text);
|
||||
return result;
|
||||
} catch (e) {
|
||||
dPrint("[InputMethodDialogUIModel] doTranslateText error: $e");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<bool> isTranslateModelDownloading() async {
|
||||
final aria2cManager = ref.read(aria2cModelProvider.notifier);
|
||||
return await aria2cManager.isNameInTask(_localTranslateModelName);
|
||||
}
|
||||
}
|
||||
|
||||
@riverpod
|
||||
class OnnxTranslation extends _$OnnxTranslation {
|
||||
@override
|
||||
bool build(String modelDir, String modelName) {
|
||||
dPrint("[OnnxTranslation] Build provider for model: $modelName");
|
||||
ref.onDispose(disposeModel);
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<String?> initModel() async {
|
||||
dPrint("[OnnxTranslation] Load model: $modelName from $modelDir");
|
||||
String? errorMessage;
|
||||
try {
|
||||
await ort.loadTranslationModel(
|
||||
modelPath: "$modelDir/$modelName",
|
||||
modelKey: modelName,
|
||||
quantizationSuffix: "_q4f16",
|
||||
);
|
||||
state = true;
|
||||
} catch (e) {
|
||||
dPrint("[OnnxTranslation] Load model error: $e");
|
||||
errorMessage = e.toString();
|
||||
state = false;
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
Future<void> disposeModel() async {
|
||||
await ort.unloadTranslationModel(modelKey: modelName).unwrap();
|
||||
dPrint("[OnnxTranslation] Unload model: $modelName");
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,7 +43,7 @@ final class InputMethodDialogUIModelProvider
|
||||
}
|
||||
|
||||
String _$inputMethodDialogUIModelHash() =>
|
||||
r'c07ef2474866bdb3944892460879121e0f90591f';
|
||||
r'f216c1a5b6d68b3924af7b351314c618dcac80b5';
|
||||
|
||||
abstract class _$InputMethodDialogUIModel
|
||||
extends $Notifier<InputMethodDialogUIState> {
|
||||
@ -65,3 +65,102 @@ abstract class _$InputMethodDialogUIModel
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ProviderFor(OnnxTranslation)
|
||||
const onnxTranslationProvider = OnnxTranslationFamily._();
|
||||
|
||||
final class OnnxTranslationProvider
|
||||
extends $NotifierProvider<OnnxTranslation, bool> {
|
||||
const OnnxTranslationProvider._({
|
||||
required OnnxTranslationFamily super.from,
|
||||
required (String, String) super.argument,
|
||||
}) : super(
|
||||
retry: null,
|
||||
name: r'onnxTranslationProvider',
|
||||
isAutoDispose: true,
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
);
|
||||
|
||||
@override
|
||||
String debugGetCreateSourceHash() => _$onnxTranslationHash();
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return r'onnxTranslationProvider'
|
||||
''
|
||||
'$argument';
|
||||
}
|
||||
|
||||
@$internal
|
||||
@override
|
||||
OnnxTranslation create() => OnnxTranslation();
|
||||
|
||||
/// {@macro riverpod.override_with_value}
|
||||
Override overrideWithValue(bool value) {
|
||||
return $ProviderOverride(
|
||||
origin: this,
|
||||
providerOverride: $SyncValueProvider<bool>(value),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return other is OnnxTranslationProvider && other.argument == argument;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return argument.hashCode;
|
||||
}
|
||||
}
|
||||
|
||||
String _$onnxTranslationHash() => r'4f3dc0e361dca2d6b00f557496bdf006cc6c235c';
|
||||
|
||||
final class OnnxTranslationFamily extends $Family
|
||||
with
|
||||
$ClassFamilyOverride<
|
||||
OnnxTranslation,
|
||||
bool,
|
||||
bool,
|
||||
bool,
|
||||
(String, String)
|
||||
> {
|
||||
const OnnxTranslationFamily._()
|
||||
: super(
|
||||
retry: null,
|
||||
name: r'onnxTranslationProvider',
|
||||
dependencies: null,
|
||||
$allTransitiveDependencies: null,
|
||||
isAutoDispose: true,
|
||||
);
|
||||
|
||||
OnnxTranslationProvider call(String modelDir, String modelName) =>
|
||||
OnnxTranslationProvider._(argument: (modelDir, modelName), from: this);
|
||||
|
||||
@override
|
||||
String toString() => r'onnxTranslationProvider';
|
||||
}
|
||||
|
||||
abstract class _$OnnxTranslation extends $Notifier<bool> {
|
||||
late final _$args = ref.$arg as (String, String);
|
||||
String get modelDir => _$args.$1;
|
||||
String get modelName => _$args.$2;
|
||||
|
||||
bool build(String modelDir, String modelName);
|
||||
@$mustCallSuper
|
||||
@override
|
||||
void runBuild() {
|
||||
final created = build(_$args.$1, _$args.$2);
|
||||
final ref = this.ref as $Ref<bool, bool>;
|
||||
final element =
|
||||
ref.element
|
||||
as $ClassProviderElement<
|
||||
AnyNotifier<bool, bool>,
|
||||
bool,
|
||||
Object?,
|
||||
Object?
|
||||
>;
|
||||
element.handleValue(ref, created);
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,7 +47,7 @@ final class AdvancedLocalizationUIModelProvider
|
||||
}
|
||||
|
||||
String _$advancedLocalizationUIModelHash() =>
|
||||
r'2f890c854bc56e506c441acabc2014438a163617';
|
||||
r'c7cca8935ac7df2281e83297b11b6b82d94f7a59';
|
||||
|
||||
abstract class _$AdvancedLocalizationUIModel
|
||||
extends $Notifier<AdvancedLocalizationUIState> {
|
||||
|
||||
@ -66,6 +66,10 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
@override
|
||||
LocalizationUIState build() {
|
||||
state = LocalizationUIState(selectedLanguage: languageSupport.keys.first);
|
||||
ref.onDispose(() {
|
||||
_customizeDirListenSub?.cancel();
|
||||
_customizeDirListenSub = null;
|
||||
});
|
||||
_init();
|
||||
return state;
|
||||
}
|
||||
@ -74,10 +78,6 @@ class LocalizationUIModel extends _$LocalizationUIModel {
|
||||
if (_scInstallPath == "not_install") {
|
||||
return;
|
||||
}
|
||||
ref.onDispose(() {
|
||||
_customizeDirListenSub?.cancel();
|
||||
_customizeDirListenSub = null;
|
||||
});
|
||||
final appConfBox = await Hive.openBox("app_conf");
|
||||
final lang = await appConfBox.get("localization_selectedLanguage", defaultValue: languageSupport.keys.first);
|
||||
state = state.copyWith(selectedLanguage: lang);
|
||||
|
||||
@ -42,7 +42,7 @@ final class LocalizationUIModelProvider
|
||||
}
|
||||
|
||||
String _$localizationUIModelHash() =>
|
||||
r'd3797a7ff3d31dd1d4b05aed4a9969f4be6853c5';
|
||||
r'3d3f0ed7fa3631eca4e10d456c437f6fca8eedff';
|
||||
|
||||
abstract class _$LocalizationUIModel extends $Notifier<LocalizationUIState> {
|
||||
LocalizationUIState build();
|
||||
|
||||
@ -42,7 +42,7 @@ final class HomePerformanceUIModelProvider
|
||||
}
|
||||
|
||||
String _$homePerformanceUIModelHash() =>
|
||||
r'c3c55c0470ef8c8be4915a1878deba332653ecde';
|
||||
r'4c5c33fe7d85dc8f6bf0d019c1b870d285d594ff';
|
||||
|
||||
abstract class _$HomePerformanceUIModel
|
||||
extends $Notifier<HomePerformanceUIState> {
|
||||
|
||||
@ -19,7 +19,6 @@ import 'package:starcitizen_doctor/common/utils/log.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/multi_window_manager.dart';
|
||||
import 'package:starcitizen_doctor/common/utils/provider.dart';
|
||||
import 'package:starcitizen_doctor/provider/aria2c.dart';
|
||||
import 'package:starcitizen_doctor/ui/home/downloader/home_downloader_ui_model.dart';
|
||||
import 'package:starcitizen_doctor/widgets/widgets.dart';
|
||||
import 'package:url_launcher/url_launcher_string.dart';
|
||||
import 'package:xml/xml.dart';
|
||||
@ -176,7 +175,8 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
"remove_nvme_settings",
|
||||
S.current.tools_action_remove_nvme_registry_patch,
|
||||
S.current.tools_action_info_nvme_patch_issue(
|
||||
nvmePatchStatus ? S.current.localization_info_installed : S.current.tools_action_info_not_installed),
|
||||
nvmePatchStatus ? S.current.localization_info_installed : S.current.tools_action_info_not_installed,
|
||||
),
|
||||
const Icon(FluentIcons.hard_drive, size: 24),
|
||||
onTap: nvmePatchStatus
|
||||
? () async {
|
||||
@ -208,7 +208,7 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
state = state.copyWith(working: false);
|
||||
loadToolsCard(context, skipPathScan: true);
|
||||
},
|
||||
)
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@ -266,8 +266,11 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
if (listData == null) {
|
||||
return;
|
||||
}
|
||||
scInstallPaths = await SCLoggerHelper.getGameInstallPath(listData,
|
||||
checkExists: checkActive, withVersion: AppConf.gameChannels);
|
||||
scInstallPaths = await SCLoggerHelper.getGameInstallPath(
|
||||
listData,
|
||||
checkExists: checkActive,
|
||||
withVersion: AppConf.gameChannels,
|
||||
);
|
||||
if (scInstallPaths.isNotEmpty) {
|
||||
scInstalledPath = scInstallPaths.first;
|
||||
}
|
||||
@ -337,11 +340,12 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
|
||||
Future<String> getSystemInfo() async {
|
||||
return S.current.tools_action_info_system_info_content(
|
||||
await SystemHelper.getSystemName(),
|
||||
await SystemHelper.getCpuName(),
|
||||
await SystemHelper.getSystemMemorySizeGB(),
|
||||
await SystemHelper.getGpuInfo(),
|
||||
await SystemHelper.getDiskInfo());
|
||||
await SystemHelper.getSystemName(),
|
||||
await SystemHelper.getCpuName(),
|
||||
await SystemHelper.getSystemMemorySizeGB(),
|
||||
await SystemHelper.getGpuInfo(),
|
||||
await SystemHelper.getDiskInfo(),
|
||||
);
|
||||
}
|
||||
|
||||
/// 管理员模式运行 RSI 启动器
|
||||
@ -365,9 +369,7 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
builder: (context) => ContentDialog(
|
||||
title: Text(S.current.tools_action_info_system_info_title),
|
||||
content: Text(systemInfo),
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * .65,
|
||||
),
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .65),
|
||||
actions: [
|
||||
FilledButton(
|
||||
child: Padding(
|
||||
@ -404,8 +406,11 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
|
||||
if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_rsi_launcher_running_warning,
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35));
|
||||
showToast(
|
||||
context,
|
||||
S.current.tools_action_info_rsi_launcher_running_warning,
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -423,14 +428,11 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
final aria2c = ref.read(aria2cModelProvider).aria2c!;
|
||||
|
||||
// check download task list
|
||||
for (var value in [...await aria2c.tellActive(), ...await aria2c.tellWaiting(0, 100000)]) {
|
||||
final t = HomeDownloaderUIModel.getTaskTypeAndName(value);
|
||||
if (t.key == "torrent" && t.value.contains("Data.p4k")) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_p4k_download_in_progress);
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
}
|
||||
if (await aria2cManager.isNameInTask("Data.p4k")) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_p4k_download_in_progress);
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (torrentUrl == "") {
|
||||
@ -440,8 +442,11 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
return;
|
||||
}
|
||||
|
||||
final userSelect =
|
||||
await FilePicker.platform.saveFile(initialDirectory: savePath, fileName: fileName, lockParentWindow: true);
|
||||
final userSelect = await FilePicker.platform.saveFile(
|
||||
initialDirectory: savePath,
|
||||
fileName: fileName,
|
||||
lockParentWindow: true,
|
||||
);
|
||||
if (userSelect == null) {
|
||||
state = state.copyWith(working: false);
|
||||
return;
|
||||
@ -550,16 +555,18 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
static Future<void> rsiEnhance(BuildContext context, {bool showNotGameInstallMsg = false}) async {
|
||||
if ((await SystemHelper.getPID("\"RSI Launcher\"")).isNotEmpty) {
|
||||
if (!context.mounted) return;
|
||||
showToast(context, S.current.tools_action_info_rsi_launcher_running_warning,
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35));
|
||||
showToast(
|
||||
context,
|
||||
S.current.tools_action_info_rsi_launcher_running_warning,
|
||||
constraints: BoxConstraints(maxWidth: MediaQuery.of(context).size.width * .35),
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => RsiLauncherEnhanceDialogUI(
|
||||
showNotGameInstallMsg: showNotGameInstallMsg,
|
||||
));
|
||||
context: context,
|
||||
builder: (BuildContext context) => RsiLauncherEnhanceDialogUI(showNotGameInstallMsg: showNotGameInstallMsg),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _showLogAnalyze(BuildContext context) async {
|
||||
@ -568,6 +575,10 @@ class ToolsUIModel extends _$ToolsUIModel {
|
||||
return;
|
||||
}
|
||||
if (!context.mounted) return;
|
||||
await MultiWindowManager.launchSubWindow("log_analyze", S.current.log_analyzer_window_title, appGlobalState);
|
||||
await MultiWindowManager.launchSubWindow(
|
||||
WindowTypes.logAnalyze,
|
||||
S.current.log_analyzer_window_title,
|
||||
appGlobalState,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ final class ToolsUIModelProvider
|
||||
}
|
||||
}
|
||||
|
||||
String _$toolsUIModelHash() => r'81a73aeccf978f7e620681eaf1a3d4182ff48f9e';
|
||||
String _$toolsUIModelHash() => r'78732ff16e87cc9f92174bda43d0fafadba51146';
|
||||
|
||||
abstract class _$ToolsUIModel extends $Notifier<ToolsUIState> {
|
||||
ToolsUIState build();
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
|
||||
#include "desktop_multi_window/desktop_multi_window_plugin.h"
|
||||
|
||||
struct _MyApplication {
|
||||
GtkApplication parent_instance;
|
||||
char** dart_entrypoint_arguments;
|
||||
@ -59,6 +61,10 @@ static void my_application_activate(GApplication* application) {
|
||||
|
||||
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
||||
|
||||
desktop_multi_window_plugin_set_window_created_callback([](FlPluginRegistry* registry){
|
||||
fl_register_plugins(registry);
|
||||
});
|
||||
|
||||
gtk_widget_grab_focus(GTK_WIDGET(view));
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import Cocoa
|
||||
import FlutterMacOS
|
||||
import desktop_multi_window
|
||||
|
||||
class MainFlutterWindow: NSWindow {
|
||||
override func awakeFromNib() {
|
||||
@ -9,6 +10,11 @@ class MainFlutterWindow: NSWindow {
|
||||
self.setFrame(windowFrame, display: true)
|
||||
|
||||
RegisterGeneratedPlugins(registry: flutterViewController)
|
||||
|
||||
FlutterMultiWindowPlugin.setOnWindowCreatedCallback { controller in
|
||||
// Register the plugin which you want access from other isolate.
|
||||
RegisterGeneratedPlugins(registry: controller)
|
||||
}
|
||||
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
97
pubspec.lock
97
pubspec.lock
@ -5,34 +5,34 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
|
||||
sha256: f0bb5d1648339c8308cc0b9838d8456b3cfe5c91f9dc1a735b4d003269e5da9a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "85.0.0"
|
||||
version: "88.0.0"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: f4ad0fea5f102201015c9aae9d93bc02f75dd9491529a8c21f88d17a8523d44c
|
||||
sha256: "0b7b9c329d2879f8f05d6c05b32ee9ec025f39b077864bdb5ac9a7b63418a98f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.6.0"
|
||||
version: "8.1.1"
|
||||
analyzer_buffer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer_buffer
|
||||
sha256: f7833bee67c03c37241c67f8741b17cc501b69d9758df7a5a4a13ed6c947be43
|
||||
sha256: aba2f75e63b3135fd1efaa8b6abefe1aa6e41b6bd9806221620fa48f98156033
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.10"
|
||||
version: "0.1.11"
|
||||
analyzer_plugin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer_plugin
|
||||
sha256: a5ab7590c27b779f3d4de67f31c4109dbe13dd7339f86461a6f2a8ab2594d8ce
|
||||
sha256: dd574a0ab77de88b7d9c12bc4b626109a5ca9078216a79041a5c24c3a1bd103c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.4"
|
||||
version: "0.13.7"
|
||||
archive:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -278,42 +278,42 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: custom_lint
|
||||
sha256: "78085fbe842de7c5bef92de811ca81536968dbcbbcdac5c316711add2d15e796"
|
||||
sha256: "751ee9440920f808266c3ec2553420dea56d3c7837dd2d62af76b11be3fcece5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.8.1"
|
||||
custom_lint_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: custom_lint_builder
|
||||
sha256: cc5532d5733d4eccfccaaec6070a1926e9f21e613d93ad0927fad020b95c9e52
|
||||
sha256: "1128db6f58e71d43842f3b9be7465c83f0c47f4dd8918f878dd6ad3b72a32072"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.8.1"
|
||||
custom_lint_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: custom_lint_core
|
||||
sha256: cc4684d22ca05bf0a4a51127e19a8aea576b42079ed2bc9e956f11aaebe35dd1
|
||||
sha256: "85b339346154d5646952d44d682965dfe9e12cae5febd706f0db3aa5010d6423"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.8.0"
|
||||
version: "0.8.1"
|
||||
custom_lint_visitor:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: custom_lint_visitor
|
||||
sha256: "4a86a0d8415a91fbb8298d6ef03e9034dc8e323a599ddc4120a0e36c433983a2"
|
||||
sha256: "446d68322747ec1c36797090de776aa72228818d3d80685a91ff524d163fee6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0+7.7.0"
|
||||
version: "1.0.0+8.1.1"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "8a0e5fba27e8ee025d2ffb4ee820b4e6e2cf5e4246a6b1a477eb66866947e0bb"
|
||||
sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.1.2"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -326,10 +326,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: desktop_multi_window
|
||||
sha256: "3ea2d696e50c3df696aabfddbd98c220ab4dde38f12c2ab12d1103bfe00ae79b"
|
||||
sha256: "60ba38725b8887b60e44d15afdcf0c3813568b5da2ccaf1e7f6fd09a380a6e24"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1"
|
||||
version: "0.3.0"
|
||||
desktop_webview_window:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -422,10 +422,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: file_picker
|
||||
sha256: f2d9f173c2c14635cc0e9b14c143c49ef30b4934e8d1d274d6206fcb0086a06f
|
||||
sha256: f8f4ea435f791ab1f817b4e338ed958cb3d04ba43d6736ffc39958d950754967
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.3.3"
|
||||
version: "10.3.6"
|
||||
file_sizes:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -618,10 +618,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: hexcolor
|
||||
sha256: c07f4bbb9095df87eeca87e7c69e8c3d60f70c66102d7b8d61c4af0453add3f6
|
||||
sha256: "0f237eed7db96ebacd8fda00d17f5ae262aaa84c213d53457c06b1dcbdfa81f2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.1"
|
||||
version: "3.0.2"
|
||||
highlight:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -666,10 +666,10 @@ packages:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: http
|
||||
sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
version: "1.6.0"
|
||||
http_client_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -874,10 +874,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
version: "1.17.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -890,10 +890,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mockito
|
||||
sha256: "2314cbe9165bcd16106513df9cf3c3224713087f09723b128928dc11a4379f99"
|
||||
sha256: "4feb43bc4eb6c03e832f5fcd637d1abb44b98f9cfa245c58e27382f58859f8f6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.0"
|
||||
version: "5.5.1"
|
||||
msix:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@ -1261,10 +1261,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "7b19d6ba131c6eb98bfcbf8d56c1a7002eba438af2e7ae6f8398b2b0f4f381e3"
|
||||
sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
version: "4.0.2"
|
||||
source_helper:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1297,14 +1297,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
sprintf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sprintf
|
||||
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1373,26 +1365,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test
|
||||
sha256: "65e29d831719be0591f7b3b1a32a3cda258ec98c58c7b25f7b84241bc31215bb"
|
||||
sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.26.2"
|
||||
version: "1.26.3"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.6"
|
||||
version: "0.7.7"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "80bf5a02b60af04b09e14f6fe68b921aad119493e26e490deaca5993fef1b05a"
|
||||
sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.11"
|
||||
version: "0.6.12"
|
||||
timing:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1485,10 +1477,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.1"
|
||||
version: "4.5.2"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -1588,10 +1580,11 @@ packages:
|
||||
window_manager:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: window_manager
|
||||
sha256: "7eb6d6c4164ec08e1bf978d6e733f3cebe792e2a23fb07cbca25c2872bfdbdcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
path: "packages/window_manager"
|
||||
ref: "6fae92d21b4c80ce1b8f71c1190d7970cf722bd4"
|
||||
resolved-ref: "6fae92d21b4c80ce1b8f71c1190d7970cf722bd4"
|
||||
url: "https://github.com/boyan01/window_manager.git"
|
||||
source: git
|
||||
version: "0.5.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
|
||||
23
pubspec.yaml
23
pubspec.yaml
@ -15,11 +15,15 @@ dependencies:
|
||||
sdk: flutter
|
||||
flutter_riverpod: ^3.0.3
|
||||
riverpod_annotation: ^3.0.3
|
||||
flutter_hooks: ^0.21.3+1
|
||||
flutter_hooks: ^0.21.3
|
||||
hooks_riverpod: ^3.0.3
|
||||
json_annotation: ^4.9.0
|
||||
go_router: ^17.0.0
|
||||
window_manager: ^0.5.1
|
||||
window_manager:
|
||||
git:
|
||||
url: https://github.com/boyan01/window_manager.git
|
||||
path: packages/window_manager
|
||||
ref: 6fae92d21b4c80ce1b8f71c1190d7970cf722bd4
|
||||
fluent_ui: 4.11.3
|
||||
flutter_staggered_grid_view: ^0.7.0
|
||||
flutter_acrylic: ^1.1.4
|
||||
@ -33,20 +37,20 @@ dependencies:
|
||||
markdown_widget: ^2.3.2+8
|
||||
extended_image: ^10.0.1
|
||||
device_info_plus: ^12.2.0
|
||||
file_picker: ^10.3.3
|
||||
file_picker: ^10.3.6
|
||||
file_sizes: ^1.0.6
|
||||
desktop_webview_window: ^0.2.3
|
||||
flutter_svg: ^2.2.2
|
||||
archive: ^4.0.7
|
||||
jwt_decode: ^0.3.1
|
||||
uuid: ^4.5.1
|
||||
uuid: ^4.5.2
|
||||
flutter_tilt: ^3.3.2
|
||||
card_swiper: ^3.0.1
|
||||
ffi: ^2.1.4
|
||||
flutter_rust_bridge: ^2.11.1
|
||||
freezed_annotation: ^3.1.0
|
||||
meta: ^1.16.0
|
||||
hexcolor: ^3.0.1
|
||||
meta: ^1.17.0
|
||||
hexcolor: ^3.0.2
|
||||
html: ^0.15.6
|
||||
fixnum: ^1.1.1
|
||||
rust_builder:
|
||||
@ -54,7 +58,6 @@ dependencies:
|
||||
aria2:
|
||||
git: https://github.com/xkeyC/dart_aria2_rpc.git
|
||||
# path: ../../xkeyC/dart_aria2_rpc
|
||||
# path: ../../xkeyC/dart_aria2_rpc
|
||||
intl: any
|
||||
synchronized: ^3.4.0
|
||||
super_sliver_list: ^0.4.1
|
||||
@ -63,13 +66,13 @@ dependencies:
|
||||
re_highlight: ^0.0.3
|
||||
shelf: ^1.4.2
|
||||
qr_flutter: ^4.1.0
|
||||
desktop_multi_window: ^0.2.1
|
||||
desktop_multi_window: ^0.3.0
|
||||
watcher: ^1.1.4
|
||||
path: ^1.9.1
|
||||
crypto: ^3.0.7
|
||||
xml: ^6.6.1
|
||||
dependency_overrides:
|
||||
http: ^1.5.0
|
||||
http: ^1.6.0
|
||||
intl: ^0.20.2
|
||||
|
||||
dev_dependencies:
|
||||
@ -81,7 +84,7 @@ dev_dependencies:
|
||||
freezed: ^3.2.3
|
||||
json_serializable: ^6.11.1
|
||||
riverpod_generator: ^3.0.3
|
||||
custom_lint: ^0.8.0
|
||||
custom_lint: ^0.8.1
|
||||
riverpod_lint: ^3.0.3
|
||||
ffigen: ^20.0.0
|
||||
sct_dev_tools:
|
||||
|
||||
568
rust/Cargo.lock
generated
568
rust/Cargo.lock
generated
@ -17,6 +17,20 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "ahash"
|
||||
version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"getrandom 0.3.4",
|
||||
"once_cell",
|
||||
"serde",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
@ -316,12 +330,24 @@ dependencies = [
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
@ -389,6 +415,15 @@ version = "1.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.43"
|
||||
@ -496,6 +531,21 @@ version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb1325a1cece981e8a296ab8f0f9b63ae357bd0784a9faaf548cc7b480707a"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"serde",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compression-codecs"
|
||||
version = "0.4.31"
|
||||
@ -630,6 +680,16 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
@ -655,14 +715,38 @@ dependencies = [
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee"
|
||||
dependencies = [
|
||||
"darling_core 0.20.11",
|
||||
"darling_macro 0.20.11",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
"darling_core 0.21.3",
|
||||
"darling_macro 0.21.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e"
|
||||
dependencies = [
|
||||
"fnv",
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -679,13 +763,24 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.20.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
|
||||
dependencies = [
|
||||
"darling_core 0.20.11",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_core 0.21.3",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
@ -699,6 +794,15 @@ dependencies = [
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dary_heap"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
@ -729,6 +833,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "deranged"
|
||||
version = "0.5.4"
|
||||
@ -739,6 +853,37 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947"
|
||||
dependencies = [
|
||||
"derive_builder_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_core"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8"
|
||||
dependencies = [
|
||||
"darling 0.20.11",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_builder_macro"
|
||||
version = "0.20.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
|
||||
dependencies = [
|
||||
"derive_builder_core",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
@ -865,6 +1010,12 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "esaxx-rs"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6"
|
||||
|
||||
[[package]]
|
||||
name = "event-listener"
|
||||
version = "5.4.1"
|
||||
@ -902,6 +1053,18 @@ version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.4"
|
||||
@ -1230,7 +1393,7 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"rand",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
"smallvec 1.15.1",
|
||||
"thiserror 2.0.17",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -1293,7 +1456,7 @@ dependencies = [
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"smallvec",
|
||||
"smallvec 1.15.1",
|
||||
"tokio",
|
||||
"want",
|
||||
]
|
||||
@ -1337,7 +1500,7 @@ version = "0.1.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
@ -1418,7 +1581,7 @@ dependencies = [
|
||||
"icu_normalizer_data",
|
||||
"icu_properties",
|
||||
"icu_provider",
|
||||
"smallvec",
|
||||
"smallvec 1.15.1",
|
||||
"zerovec",
|
||||
]
|
||||
|
||||
@ -1480,7 +1643,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
|
||||
dependencies = [
|
||||
"idna_adapter",
|
||||
"smallvec",
|
||||
"smallvec 1.15.1",
|
||||
"utf8_iter",
|
||||
]
|
||||
|
||||
@ -1575,6 +1738,15 @@ dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.15"
|
||||
@ -1603,6 +1775,17 @@ version = "0.2.177"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
@ -1654,6 +1837,32 @@ dependencies = [
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520"
|
||||
dependencies = [
|
||||
"macro_rules_attribute-proc_macro",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute-proc_macro"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
|
||||
|
||||
[[package]]
|
||||
name = "matrixmultiply"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "md-5"
|
||||
version = "0.10.6"
|
||||
@ -1725,11 +1934,33 @@ dependencies = [
|
||||
"parking_lot",
|
||||
"portable-atomic",
|
||||
"rustc_version",
|
||||
"smallvec",
|
||||
"smallvec 1.15.1",
|
||||
"tagptr",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monostate"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3341a273f6c9d5bef1908f17b7267bbab0e95c9bf69a0d4dcf8e9e1b2c76ef67"
|
||||
dependencies = [
|
||||
"monostate-impl",
|
||||
"serde",
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "monostate-impl"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "native-tls"
|
||||
version = "0.2.14"
|
||||
@ -1747,6 +1978,36 @@ dependencies = [
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841"
|
||||
dependencies = [
|
||||
"matrixmultiply",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndarray"
|
||||
version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c7c9125e8f6f10c9da3aad044cc918cf8784fa34de857b1aa68038eb05a50a9"
|
||||
dependencies = [
|
||||
"matrixmultiply",
|
||||
"num-complex",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
"portable-atomic",
|
||||
"portable-atomic-util",
|
||||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
@ -1784,12 +2045,30 @@ dependencies = [
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-complex"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-conv"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9"
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
@ -1873,6 +2152,28 @@ version = "1.70.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||
|
||||
[[package]]
|
||||
name = "onig"
|
||||
version = "6.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"once_cell",
|
||||
"onig_sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "onig_sys"
|
||||
version = "69.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.74"
|
||||
@ -1927,6 +2228,31 @@ dependencies = [
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ort"
|
||||
version = "2.0.0-rc.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa7e49bd669d32d7bc2a15ec540a527e7764aec722a45467814005725bcd721"
|
||||
dependencies = [
|
||||
"ndarray 0.16.1",
|
||||
"ort-sys",
|
||||
"smallvec 2.0.0-alpha.10",
|
||||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ort-sys"
|
||||
version = "2.0.0-rc.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2aba9f5c7c479925205799216e7e5d07cc1d4fa76ea8058c60a9a30f6a4e890"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"pkg-config",
|
||||
"sha2",
|
||||
"tar",
|
||||
"ureq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "oslog"
|
||||
version = "0.2.0"
|
||||
@ -1969,10 +2295,25 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"smallvec 1.15.1",
|
||||
"windows-link 0.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pem-rfc7468"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
@ -2037,6 +2378,15 @@ version = "1.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483"
|
||||
|
||||
[[package]]
|
||||
name = "portable-atomic-util"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507"
|
||||
dependencies = [
|
||||
"portable-atomic",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "potential_utf"
|
||||
version = "0.1.3"
|
||||
@ -2203,6 +2553,43 @@ dependencies = [
|
||||
"getrandom 0.3.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rawpointer"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-cond"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2964d0cf57a3e7a06e8183d14a8b527195c706b7983549cd5462d5aa3747438f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"itertools 0.14.0",
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.5.18"
|
||||
@ -2268,7 +2655,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
|
||||
dependencies = [
|
||||
"async-compression",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bytes",
|
||||
"cookie",
|
||||
"cookie_store",
|
||||
@ -2340,10 +2727,14 @@ dependencies = [
|
||||
"flutter_rust_bridge",
|
||||
"futures",
|
||||
"hickory-resolver",
|
||||
"ndarray 0.17.1",
|
||||
"notify-rust",
|
||||
"once_cell",
|
||||
"ort",
|
||||
"reqwest",
|
||||
"scopeguard",
|
||||
"serde_json",
|
||||
"tokenizers",
|
||||
"tokio",
|
||||
"url",
|
||||
"walkdir",
|
||||
@ -2581,7 +2972,7 @@ version = "3.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"hex",
|
||||
"indexmap 1.9.3",
|
||||
@ -2600,7 +2991,7 @@ version = "3.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"darling 0.21.3",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@ -2659,6 +3050,12 @@ version = "1.15.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "2.0.0-alpha.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51d44cfb396c3caf6fbfd0ab422af02631b69ddd96d2eff0b0f0724f9024051b"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.5.10"
|
||||
@ -2679,6 +3076,29 @@ dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "socks"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spm_precompiled"
|
||||
version = "0.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5851699c4033c63636f7ea4cf7b7c1f1bf06d0cc03cfb42e711de5a5c46cf326"
|
||||
dependencies = [
|
||||
"base64 0.13.1",
|
||||
"nom",
|
||||
"serde",
|
||||
"unicode-segmentation",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
@ -2761,6 +3181,17 @@ version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b2093cf4c8eb1e67749a6762251bc9cd836b6fc171623bd0a9d324d37af2417"
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-winrt-notification"
|
||||
version = "0.7.2"
|
||||
@ -2900,6 +3331,39 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||
|
||||
[[package]]
|
||||
name = "tokenizers"
|
||||
version = "0.22.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6475a27088c98ea96d00b39a9ddfb63780d1ad4cceb6f48374349a96ab2b7842"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"aho-corasick",
|
||||
"compact_str",
|
||||
"dary_heap",
|
||||
"derive_builder",
|
||||
"esaxx-rs",
|
||||
"getrandom 0.3.4",
|
||||
"itertools 0.14.0",
|
||||
"log",
|
||||
"macro_rules_attribute",
|
||||
"monostate",
|
||||
"onig",
|
||||
"paste",
|
||||
"rand",
|
||||
"rayon",
|
||||
"rayon-cond",
|
||||
"regex",
|
||||
"regex-syntax",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"spm_precompiled",
|
||||
"thiserror 2.0.17",
|
||||
"unicode-normalization-alignments",
|
||||
"unicode-segmentation",
|
||||
"unicode_categories",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.48.0"
|
||||
@ -3117,18 +3581,69 @@ version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "462eeb75aeb73aea900253ce739c8e18a67423fadf006037cd3ff27e82748a06"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization-alignments"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43f613e4fa046e69818dd287fdc4bc78175ff20331479dab6e1b0f98d57062de"
|
||||
dependencies = [
|
||||
"smallvec 1.15.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-xid"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
|
||||
|
||||
[[package]]
|
||||
name = "unicode_categories"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
|
||||
|
||||
[[package]]
|
||||
name = "ureq"
|
||||
version = "3.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cb1dbab692d82a977c0392ffac19e188bd9186a9f32806f0aaa859d75585a"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"der",
|
||||
"log",
|
||||
"native-tls",
|
||||
"percent-encoding",
|
||||
"rustls-pki-types",
|
||||
"socks",
|
||||
"ureq-proto",
|
||||
"utf-8",
|
||||
"webpki-root-certs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ureq-proto"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60b4531c118335662134346048ddb0e54cc86bd7e81866757873055f0e38f5d2"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"http",
|
||||
"httparse",
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.5.7"
|
||||
@ -3141,6 +3656,12 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "utf-8"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
|
||||
|
||||
[[package]]
|
||||
name = "utf8_iter"
|
||||
version = "1.0.4"
|
||||
@ -3309,7 +3830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8d12a78aa0bab22d2f26ed1a96df7ab58e8a93506a3e20adb47c51a93b4e1357"
|
||||
dependencies = [
|
||||
"const_format",
|
||||
"itertools",
|
||||
"itertools 0.11.0",
|
||||
"nom",
|
||||
"pori",
|
||||
"regex",
|
||||
@ -3337,6 +3858,15 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-root-certs"
|
||||
version = "1.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee3e3b5f5e80bc89f30ce8d0343bf4e5f12341c51f3e26cbeecbc7c85443e85b"
|
||||
dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webpki-roots"
|
||||
version = "1.0.3"
|
||||
@ -3865,6 +4395,16 @@ version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yoke"
|
||||
version = "0.8.0"
|
||||
|
||||
@ -23,6 +23,10 @@ scopeguard = "1.2"
|
||||
notify-rust = "4"
|
||||
asar = "0.3.0"
|
||||
walkdir = "2.5.0"
|
||||
ort = { version = "2.0.0-rc.10", features = ["xnnpack", "download-binaries", "ndarray"] }
|
||||
tokenizers = { version = "0.22", default-features = false, features = ["onig"] }
|
||||
ndarray = "0.17"
|
||||
serde_json = "1.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.62.2", features = ["Win32_UI_WindowsAndMessaging"] }
|
||||
|
||||
@ -5,3 +5,4 @@ pub mod http_api;
|
||||
pub mod rs_process;
|
||||
pub mod win32_api;
|
||||
pub mod asar_api;
|
||||
pub mod ort_api;
|
||||
|
||||
107
rust/src/api/ort_api.rs
Normal file
107
rust/src/api/ort_api.rs
Normal file
@ -0,0 +1,107 @@
|
||||
use anyhow::Result;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use crate::ort_models::opus_mt::OpusMtModel;
|
||||
|
||||
/// 全局模型缓存
|
||||
static MODEL_CACHE: Lazy<Mutex<HashMap<String, OpusMtModel>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
/// 加载 ONNX 翻译模型
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_path` - 模型文件夹路径
|
||||
/// * `model_key` - 模型缓存键(用于标识模型,如 "zh-en")
|
||||
/// * `quantization_suffix` - 量化后缀(如 "_q4", "_q8",空字符串表示使用默认模型)
|
||||
///
|
||||
pub fn load_translation_model(
|
||||
model_path: String,
|
||||
model_key: String,
|
||||
quantization_suffix: String,
|
||||
) -> Result<()> {
|
||||
let model = OpusMtModel::new(&model_path, &quantization_suffix)?;
|
||||
|
||||
let mut cache = MODEL_CACHE
|
||||
.lock()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to lock model cache: {}", e))?;
|
||||
|
||||
cache.insert(model_key, model);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
/// * `text` - 要翻译的文本
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<String>` - 翻译后的文本
|
||||
pub fn translate_text(model_key: String, text: String) -> Result<String> {
|
||||
let cache = MODEL_CACHE
|
||||
.lock()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to lock model cache: {}", e))?;
|
||||
|
||||
let model = cache.get(&model_key).ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Model not found: {}. Please load the model first.",
|
||||
model_key
|
||||
)
|
||||
})?;
|
||||
|
||||
model.translate(&text)
|
||||
}
|
||||
|
||||
/// 批量翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
/// * `texts` - 要翻译的文本列表
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<Vec<String>>` - 翻译后的文本列表
|
||||
pub fn translate_text_batch(model_key: String, texts: Vec<String>) -> Result<Vec<String>> {
|
||||
let cache = MODEL_CACHE
|
||||
.lock()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to lock model cache: {}", e))?;
|
||||
|
||||
let model = cache.get(&model_key).ok_or_else(|| {
|
||||
anyhow::anyhow!(
|
||||
"Model not found: {}. Please load the model first.",
|
||||
model_key
|
||||
)
|
||||
})?;
|
||||
|
||||
model.translate_batch(&texts)
|
||||
}
|
||||
|
||||
/// 卸载模型
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_key` - 模型缓存键(如 "zh-en")
|
||||
///
|
||||
pub fn unload_translation_model(model_key: String) -> Result<()> {
|
||||
let mut cache = MODEL_CACHE
|
||||
.lock()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to lock model cache: {}", e))?;
|
||||
|
||||
cache.remove(&model_key);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 清空所有已加载的模型
|
||||
///
|
||||
/// # Returns
|
||||
pub fn clear_all_models() -> Result<()> {
|
||||
let mut cache = MODEL_CACHE
|
||||
.lock()
|
||||
.map_err(|e| anyhow::anyhow!("Failed to lock model cache: {}", e))?;
|
||||
|
||||
cache.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -37,7 +37,7 @@ flutter_rust_bridge::frb_generated_boilerplate!(
|
||||
default_rust_auto_opaque = RustAutoOpaqueNom,
|
||||
);
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_VERSION: &str = "2.11.1";
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = 1832496273;
|
||||
pub(crate) const FLUTTER_RUST_BRIDGE_CODEGEN_CONTENT_HASH: i32 = -706588047;
|
||||
|
||||
// Section: executor
|
||||
|
||||
@ -45,6 +45,27 @@ flutter_rust_bridge::frb_generated_default_handler!();
|
||||
|
||||
// Section: wire_funcs
|
||||
|
||||
fn wire__crate__api__ort_api__clear_all_models_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "clear_all_models",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok = crate::api::ort_api::clear_all_models()?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__http_api__dns_lookup_ips_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
host: impl CstDecode<String>,
|
||||
@ -161,6 +182,37 @@ fn wire__crate__api__asar_api__get_rsi_launcher_asar_data_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__ort_api__load_translation_model_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
model_path: impl CstDecode<String>,
|
||||
model_key: impl CstDecode<String>,
|
||||
quantization_suffix: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "load_translation_model",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_model_path = model_path.cst_decode();
|
||||
let api_model_key = model_key.cst_decode();
|
||||
let api_quantization_suffix = quantization_suffix.cst_decode();
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok = crate::api::ort_api::load_translation_model(
|
||||
api_model_path,
|
||||
api_model_key,
|
||||
api_quantization_suffix,
|
||||
)?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
that: impl CstDecode<crate::api::asar_api::RsiLauncherAsarData>,
|
||||
@ -315,6 +367,82 @@ fn wire__crate__api__rs_process__start_impl(
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__ort_api__translate_text_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
model_key: impl CstDecode<String>,
|
||||
text: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "translate_text",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_model_key = model_key.cst_decode();
|
||||
let api_text = text.cst_decode();
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok =
|
||||
crate::api::ort_api::translate_text(api_model_key, api_text)?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__ort_api__translate_text_batch_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
model_key: impl CstDecode<String>,
|
||||
texts: impl CstDecode<Vec<String>>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "translate_text_batch",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_model_key = model_key.cst_decode();
|
||||
let api_texts = texts.cst_decode();
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok =
|
||||
crate::api::ort_api::translate_text_batch(api_model_key, api_texts)?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__ort_api__unload_translation_model_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
model_key: impl CstDecode<String>,
|
||||
) {
|
||||
FLUTTER_RUST_BRIDGE_HANDLER.wrap_normal::<flutter_rust_bridge::for_generated::DcoCodec, _, _>(
|
||||
flutter_rust_bridge::for_generated::TaskInfo {
|
||||
debug_name: "unload_translation_model",
|
||||
port: Some(port_),
|
||||
mode: flutter_rust_bridge::for_generated::FfiCallMode::Normal,
|
||||
},
|
||||
move || {
|
||||
let api_model_key = model_key.cst_decode();
|
||||
move |context| {
|
||||
transform_result_dco::<_, _, flutter_rust_bridge::for_generated::anyhow::Error>(
|
||||
(move || {
|
||||
let output_ok =
|
||||
crate::api::ort_api::unload_translation_model(api_model_key)?;
|
||||
Ok(output_ok)
|
||||
})(),
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
fn wire__crate__api__rs_process__write_impl(
|
||||
port_: flutter_rust_bridge::for_generated::MessagePort,
|
||||
rs_pid: impl CstDecode<u32>,
|
||||
@ -1361,6 +1489,13 @@ mod io {
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__clear_all_models(
|
||||
port_: i64,
|
||||
) {
|
||||
wire__crate__api__ort_api__clear_all_models_impl(port_)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__http_api__dns_lookup_ips(
|
||||
port_: i64,
|
||||
@ -1406,6 +1541,21 @@ mod io {
|
||||
wire__crate__api__asar_api__get_rsi_launcher_asar_data_impl(port_, asar_path)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__load_translation_model(
|
||||
port_: i64,
|
||||
model_path: *mut wire_cst_list_prim_u_8_strict,
|
||||
model_key: *mut wire_cst_list_prim_u_8_strict,
|
||||
quantization_suffix: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__ort_api__load_translation_model_impl(
|
||||
port_,
|
||||
model_path,
|
||||
model_key,
|
||||
quantization_suffix,
|
||||
)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__asar_api__rsi_launcher_asar_data_write_main_js(
|
||||
port_: i64,
|
||||
@ -1459,6 +1609,32 @@ mod io {
|
||||
)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__translate_text(
|
||||
port_: i64,
|
||||
model_key: *mut wire_cst_list_prim_u_8_strict,
|
||||
text: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__ort_api__translate_text_impl(port_, model_key, text)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__translate_text_batch(
|
||||
port_: i64,
|
||||
model_key: *mut wire_cst_list_prim_u_8_strict,
|
||||
texts: *mut wire_cst_list_String,
|
||||
) {
|
||||
wire__crate__api__ort_api__translate_text_batch_impl(port_, model_key, texts)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__ort_api__unload_translation_model(
|
||||
port_: i64,
|
||||
model_key: *mut wire_cst_list_prim_u_8_strict,
|
||||
) {
|
||||
wire__crate__api__ort_api__unload_translation_model_impl(port_, model_key)
|
||||
}
|
||||
|
||||
#[unsafe(no_mangle)]
|
||||
pub extern "C" fn frbgen_starcitizen_doctor_wire__crate__api__rs_process__write(
|
||||
port_: i64,
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod api;
|
||||
mod frb_generated;
|
||||
pub mod http_package;
|
||||
pub mod ort_models;
|
||||
|
||||
1
rust/src/ort_models/mod.rs
Normal file
1
rust/src/ort_models/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod opus_mt;
|
||||
403
rust/src/ort_models/opus_mt.rs
Normal file
403
rust/src/ort_models/opus_mt.rs
Normal file
@ -0,0 +1,403 @@
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use ndarray::{Array2, ArrayD};
|
||||
use ort::{
|
||||
execution_providers::XNNPACKExecutionProvider, session::builder::GraphOptimizationLevel,
|
||||
session::Session, value::Value,
|
||||
};
|
||||
use std::path::Path;
|
||||
use std::sync::Mutex;
|
||||
use tokenizers::Tokenizer;
|
||||
|
||||
/// Opus-MT 翻译模型的推理结构
|
||||
pub struct OpusMtModel {
|
||||
encoder_session: Mutex<Session>,
|
||||
decoder_session: Mutex<Session>,
|
||||
tokenizer: Tokenizer,
|
||||
config: ModelConfig,
|
||||
}
|
||||
|
||||
/// 模型配置
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModelConfig {
|
||||
pub max_length: usize,
|
||||
pub num_beams: usize,
|
||||
pub decoder_start_token_id: i64,
|
||||
pub eos_token_id: i64,
|
||||
pub pad_token_id: i64,
|
||||
}
|
||||
|
||||
impl Default for ModelConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
max_length: 512,
|
||||
num_beams: 1,
|
||||
decoder_start_token_id: 0,
|
||||
eos_token_id: 0,
|
||||
pad_token_id: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpusMtModel {
|
||||
/// 从模型路径创建新的 OpusMT 模型实例
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `model_path` - 模型文件夹路径(应包含 onnx 子文件夹)
|
||||
/// * `quantization_suffix` - 量化后缀,如 "_q4", "_q8",为空字符串则使用默认模型
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<Self>` - 成功返回模型实例,失败返回错误
|
||||
pub fn new<P: AsRef<Path>>(model_path: P, quantization_suffix: &str) -> Result<Self> {
|
||||
let model_path = model_path.as_ref();
|
||||
|
||||
// onnx-community 标准:模型在 onnx 子文件夹中
|
||||
let onnx_dir = model_path.join("onnx");
|
||||
|
||||
// 加载 tokenizer(在根目录)
|
||||
let tokenizer_path = model_path.join("tokenizer.json");
|
||||
|
||||
// 动态加载并修复 tokenizer
|
||||
let tokenizer =
|
||||
Self::load_tokenizer(&tokenizer_path).context("Failed to load tokenizer")?;
|
||||
|
||||
// 构建模型文件名
|
||||
let encoder_filename = if quantization_suffix.is_empty() {
|
||||
"encoder_model.onnx".to_string()
|
||||
} else {
|
||||
format!("encoder_model{}.onnx", quantization_suffix)
|
||||
};
|
||||
|
||||
let decoder_filename = if quantization_suffix.is_empty() {
|
||||
"decoder_model.onnx".to_string()
|
||||
} else {
|
||||
format!("decoder_model{}.onnx", quantization_suffix)
|
||||
};
|
||||
|
||||
// 加载 encoder 模型(在 onnx 子目录)
|
||||
let encoder_path = onnx_dir.join(&encoder_filename);
|
||||
if !encoder_path.exists() {
|
||||
return Err(anyhow!(
|
||||
"Encoder model not found: {}",
|
||||
encoder_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
let encoder_session = Session::builder()
|
||||
.context("Failed to create encoder session builder")?
|
||||
.with_optimization_level(GraphOptimizationLevel::Level3)
|
||||
.context("Failed to set optimization level")?
|
||||
.with_intra_threads(4)
|
||||
.context("Failed to set intra threads")?
|
||||
.with_execution_providers([XNNPACKExecutionProvider::default().build()])
|
||||
.context("Failed to register XNNPACK execution provider")?
|
||||
.commit_from_file(&encoder_path)
|
||||
.context(format!(
|
||||
"Failed to load encoder model: {}",
|
||||
encoder_filename
|
||||
))?;
|
||||
|
||||
// 加载 decoder 模型(在 onnx 子目录)
|
||||
let decoder_path = onnx_dir.join(&decoder_filename);
|
||||
if !decoder_path.exists() {
|
||||
return Err(anyhow!(
|
||||
"Decoder model not found: {}",
|
||||
decoder_path.display()
|
||||
));
|
||||
}
|
||||
|
||||
let decoder_session = Session::builder()
|
||||
.context("Failed to create decoder session builder")?
|
||||
.with_optimization_level(GraphOptimizationLevel::Level3)
|
||||
.context("Failed to set optimization level")?
|
||||
.with_intra_threads(4)
|
||||
.context("Failed to set intra threads")?
|
||||
.with_execution_providers([XNNPACKExecutionProvider::default().build()])
|
||||
.context("Failed to register XNNPACK execution provider")?
|
||||
.commit_from_file(&decoder_path)
|
||||
.context(format!(
|
||||
"Failed to load decoder model: {}",
|
||||
decoder_filename
|
||||
))?;
|
||||
|
||||
// 加载配置(如果存在,在根目录)
|
||||
let config = Self::load_config(model_path)?;
|
||||
|
||||
Ok(Self {
|
||||
encoder_session: Mutex::new(encoder_session),
|
||||
decoder_session: Mutex::new(decoder_session),
|
||||
tokenizer,
|
||||
config,
|
||||
})
|
||||
}
|
||||
|
||||
/// 动态加载 tokenizer,自动修复常见问题
|
||||
fn load_tokenizer(tokenizer_path: &Path) -> Result<Tokenizer> {
|
||||
use std::fs;
|
||||
|
||||
// 读取原始文件
|
||||
let content =
|
||||
fs::read_to_string(tokenizer_path).context("Failed to read tokenizer.json")?;
|
||||
|
||||
// 解析为 JSON
|
||||
let mut json: serde_json::Value =
|
||||
serde_json::from_str(&content).context("Failed to parse tokenizer.json")?;
|
||||
|
||||
let mut needs_fix = false;
|
||||
|
||||
// 修复 normalizer 中的问题
|
||||
if let Some(obj) = json.as_object_mut() {
|
||||
if let Some(normalizer) = obj.get("normalizer") {
|
||||
let mut should_remove_normalizer = false;
|
||||
|
||||
if normalizer.is_null() {
|
||||
// normalizer 是 null,需要移除
|
||||
should_remove_normalizer = true;
|
||||
} else if let Some(norm_obj) = normalizer.as_object() {
|
||||
// 检查是否是有问题的 Precompiled 类型
|
||||
if let Some(type_val) = norm_obj.get("type") {
|
||||
if type_val.as_str() == Some("Precompiled") {
|
||||
// 检查 precompiled_charsmap 字段
|
||||
if let Some(precompiled) = norm_obj.get("precompiled_charsmap") {
|
||||
if precompiled.is_null() {
|
||||
// precompiled_charsmap 是 null,移除整个 normalizer
|
||||
should_remove_normalizer = true;
|
||||
}
|
||||
} else {
|
||||
// 缺少 precompiled_charsmap 字段,移除整个 normalizer
|
||||
should_remove_normalizer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if should_remove_normalizer {
|
||||
obj.remove("normalizer");
|
||||
needs_fix = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 从修复后的 JSON 字符串加载 tokenizer
|
||||
let json_str = if needs_fix {
|
||||
serde_json::to_string(&json).context("Failed to serialize fixed tokenizer")?
|
||||
} else {
|
||||
content
|
||||
};
|
||||
|
||||
// 从字节数组加载 tokenizer
|
||||
Tokenizer::from_bytes(json_str.as_bytes())
|
||||
.map_err(|e| anyhow!("Failed to load tokenizer: {}", e))
|
||||
}
|
||||
|
||||
/// 从配置文件加载模型配置
|
||||
fn load_config(model_path: &Path) -> Result<ModelConfig> {
|
||||
let config_path = model_path.join("config.json");
|
||||
|
||||
if config_path.exists() {
|
||||
let config_str =
|
||||
std::fs::read_to_string(config_path).context("Failed to read config.json")?;
|
||||
let config_json: serde_json::Value =
|
||||
serde_json::from_str(&config_str).context("Failed to parse config.json")?;
|
||||
|
||||
Ok(ModelConfig {
|
||||
max_length: config_json["max_length"].as_u64().unwrap_or(512) as usize,
|
||||
num_beams: config_json["num_beams"].as_u64().unwrap_or(1) as usize,
|
||||
decoder_start_token_id: config_json["decoder_start_token_id"].as_i64().unwrap_or(0),
|
||||
eos_token_id: config_json["eos_token_id"].as_i64().unwrap_or(0),
|
||||
pad_token_id: config_json["pad_token_id"].as_i64().unwrap_or(0),
|
||||
})
|
||||
} else {
|
||||
Ok(ModelConfig::default())
|
||||
}
|
||||
}
|
||||
|
||||
/// 翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `text` - 要翻译的文本
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<String>` - 翻译后的文本
|
||||
pub fn translate(&self, text: &str) -> Result<String> {
|
||||
// 1. Tokenize 输入文本
|
||||
let encoding = self
|
||||
.tokenizer
|
||||
.encode(text, true)
|
||||
.map_err(|e| anyhow!("Failed to encode text: {}", e))?;
|
||||
|
||||
let input_ids = encoding.get_ids();
|
||||
let attention_mask = encoding.get_attention_mask();
|
||||
|
||||
// 2. 准备 encoder 输入
|
||||
let batch_size = 1;
|
||||
let seq_len = input_ids.len();
|
||||
|
||||
let input_ids_array: Array2<i64> = Array2::from_shape_vec(
|
||||
(batch_size, seq_len),
|
||||
input_ids.iter().map(|&id| id as i64).collect(),
|
||||
)
|
||||
.context("Failed to create input_ids array")?;
|
||||
|
||||
let attention_mask_array: Array2<i64> = Array2::from_shape_vec(
|
||||
(batch_size, seq_len),
|
||||
attention_mask.iter().map(|&mask| mask as i64).collect(),
|
||||
)
|
||||
.context("Failed to create attention_mask array")?;
|
||||
|
||||
// 3. 运行 encoder
|
||||
let input_ids_value = Value::from_array((
|
||||
input_ids_array.shape().to_vec(),
|
||||
input_ids_array.into_raw_vec_and_offset().0,
|
||||
))
|
||||
.context("Failed to create input_ids value")?;
|
||||
let attention_mask_value = Value::from_array((
|
||||
attention_mask_array.shape().to_vec(),
|
||||
attention_mask_array.clone().into_raw_vec_and_offset().0,
|
||||
))
|
||||
.context("Failed to create attention_mask value")?;
|
||||
|
||||
let encoder_inputs = ort::inputs![
|
||||
"input_ids" => input_ids_value,
|
||||
"attention_mask" => attention_mask_value,
|
||||
];
|
||||
|
||||
let mut encoder_session = self
|
||||
.encoder_session
|
||||
.lock()
|
||||
.map_err(|e| anyhow!("Failed to lock encoder session: {}", e))?;
|
||||
let encoder_outputs = encoder_session
|
||||
.run(encoder_inputs)
|
||||
.context("Failed to run encoder")?;
|
||||
|
||||
let encoder_hidden_states = encoder_outputs["last_hidden_state"]
|
||||
.try_extract_tensor::<f32>()
|
||||
.context("Failed to extract encoder hidden states")?;
|
||||
|
||||
// 将 tensor 转换为 ArrayD
|
||||
let (shape, data) = encoder_hidden_states;
|
||||
let shape_vec: Vec<usize> = shape.iter().map(|&x| x as usize).collect();
|
||||
let encoder_array = ArrayD::from_shape_vec(shape_vec, data.to_vec())
|
||||
.context("Failed to create encoder array")?;
|
||||
|
||||
// 4. 贪婪解码生成输出
|
||||
let output_ids = self.greedy_decode(encoder_array, &attention_mask_array)?;
|
||||
|
||||
// 5. Decode 输出 token IDs
|
||||
let output_tokens: Vec<u32> = output_ids.iter().map(|&id| id as u32).collect();
|
||||
let decoded = self
|
||||
.tokenizer
|
||||
.decode(&output_tokens, true)
|
||||
.map_err(|e| anyhow!("Failed to decode output: {}", e))?;
|
||||
|
||||
Ok(decoded)
|
||||
}
|
||||
|
||||
/// 贪婪解码
|
||||
fn greedy_decode(
|
||||
&self,
|
||||
encoder_hidden_states: ArrayD<f32>,
|
||||
encoder_attention_mask: &Array2<i64>,
|
||||
) -> Result<Vec<i64>> {
|
||||
let batch_size = 1;
|
||||
let mut generated_ids = vec![self.config.decoder_start_token_id];
|
||||
|
||||
for _ in 0..self.config.max_length {
|
||||
// 准备 decoder 输入
|
||||
let decoder_input_len = generated_ids.len();
|
||||
let decoder_input_ids: Array2<i64> =
|
||||
Array2::from_shape_vec((batch_size, decoder_input_len), generated_ids.clone())
|
||||
.context("Failed to create decoder input_ids")?;
|
||||
|
||||
// 创建 ORT Value
|
||||
let decoder_input_value = Value::from_array((
|
||||
decoder_input_ids.shape().to_vec(),
|
||||
decoder_input_ids.into_raw_vec_and_offset().0,
|
||||
))
|
||||
.context("Failed to create decoder input value")?;
|
||||
let encoder_hidden_value = Value::from_array((
|
||||
encoder_hidden_states.shape().to_vec(),
|
||||
encoder_hidden_states.clone().into_raw_vec_and_offset().0,
|
||||
))
|
||||
.context("Failed to create encoder hidden value")?;
|
||||
let encoder_mask_value = Value::from_array((
|
||||
encoder_attention_mask.shape().to_vec(),
|
||||
encoder_attention_mask.clone().into_raw_vec_and_offset().0,
|
||||
))
|
||||
.context("Failed to create encoder mask value")?;
|
||||
|
||||
// 运行 decoder
|
||||
let decoder_inputs = ort::inputs![
|
||||
"input_ids" => decoder_input_value,
|
||||
"encoder_hidden_states" => encoder_hidden_value,
|
||||
"encoder_attention_mask" => encoder_mask_value,
|
||||
];
|
||||
|
||||
let mut decoder_session = self
|
||||
.decoder_session
|
||||
.lock()
|
||||
.map_err(|e| anyhow!("Failed to lock decoder session: {}", e))?;
|
||||
let decoder_outputs = decoder_session
|
||||
.run(decoder_inputs)
|
||||
.context("Failed to run decoder")?;
|
||||
|
||||
// 获取 logits
|
||||
let logits_tensor = decoder_outputs["logits"]
|
||||
.try_extract_tensor::<f32>()
|
||||
.context("Failed to extract logits")?;
|
||||
|
||||
let (logits_shape, logits_data) = logits_tensor;
|
||||
let vocab_size = logits_shape[2] as usize;
|
||||
|
||||
// 获取最后一个 token 的 logits
|
||||
let last_token_idx = decoder_input_len - 1;
|
||||
let last_logits_start = last_token_idx * vocab_size;
|
||||
let last_logits_end = last_logits_start + vocab_size;
|
||||
|
||||
let last_logits_slice = &logits_data[last_logits_start..last_logits_end];
|
||||
|
||||
// 找到最大概率的 token
|
||||
let next_token_id = last_logits_slice
|
||||
.iter()
|
||||
.enumerate()
|
||||
.max_by(|(_, a), (_, b)| a.partial_cmp(b).unwrap())
|
||||
.map(|(idx, _)| idx as i64)
|
||||
.context("Failed to find max token")?;
|
||||
|
||||
// 检查是否到达结束 token
|
||||
if next_token_id == self.config.eos_token_id {
|
||||
break;
|
||||
}
|
||||
|
||||
generated_ids.push(next_token_id);
|
||||
}
|
||||
|
||||
Ok(generated_ids)
|
||||
}
|
||||
|
||||
/// 批量翻译文本
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `texts` - 要翻译的文本列表
|
||||
///
|
||||
/// # Returns
|
||||
/// * `Result<Vec<String>>` - 翻译后的文本列表
|
||||
pub fn translate_batch(&self, texts: &[String]) -> Result<Vec<String>> {
|
||||
texts.iter().map(|text| self.translate(text)).collect()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_translation() {
|
||||
let model = OpusMtModel::new(
|
||||
"C:\\Users\\xkeyc\\Downloads\\onnx_models\\opus-mt-zh-en",
|
||||
"_q4f16",
|
||||
)
|
||||
.unwrap();
|
||||
let result = model.translate("你好世界").unwrap();
|
||||
println!("Translation: {}", result);
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
#include <optional>
|
||||
|
||||
#include "flutter/generated_plugin_registrant.h"
|
||||
#include "desktop_multi_window/desktop_multi_window_plugin.h"
|
||||
|
||||
FlutterWindow::FlutterWindow(const flutter::DartProject& project)
|
||||
: project_(project) {}
|
||||
@ -25,6 +26,12 @@ bool FlutterWindow::OnCreate() {
|
||||
return false;
|
||||
}
|
||||
RegisterPlugins(flutter_controller_->engine());
|
||||
DesktopMultiWindowSetWindowCreatedCallback([](void *controller) {
|
||||
auto *flutter_view_controller =
|
||||
reinterpret_cast<flutter::FlutterViewController *>(controller);
|
||||
auto *registry = flutter_view_controller->engine();
|
||||
RegisterPlugins(registry);
|
||||
});
|
||||
SetChildContent(flutter_controller_->view()->GetNativeWindow());
|
||||
|
||||
flutter_controller_->engine()->SetNextFrameCallback([&]() {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user