Check for update on app start, improved theme

This commit is contained in:
JHubi1 2024-09-03 17:08:25 +02:00
parent b78ce302b5
commit a816cf8f65
No known key found for this signature in database
GPG Key ID: 7BF82570CBBBD050
9 changed files with 213 additions and 108 deletions

View File

@ -110,6 +110,11 @@
"description": "Tooltip for add host headers button", "description": "Tooltip for add host headers button",
"context": "Visible in settings view" "context": "Visible in settings view"
}, },
"tooltipReset": "Reset current chat",
"@tooltipReset": {
"description": "Tooltip for reset button",
"context": "Visible in the chat view"
},
"noModelSelected": "No model selected", "noModelSelected": "No model selected",
"@noModelSelected": { "@noModelSelected": {
"description": "Text displayed when no model is selected", "description": "Text displayed when no model is selected",
@ -328,6 +333,21 @@
"description": "Text displayed when a feature is in beta", "description": "Text displayed when a feature is in beta",
"context": "Visible in the settings view" "context": "Visible in the settings view"
}, },
"settingsExperimentalDeprecated": "deprecated",
"@settingsExperimentalDeprecated": {
"description": "Text displayed when a feature is deprecated",
"context": "Visible in the settings view"
},
"settingsExperimentalDeprecatedDescription": "This feature is deprecated and will be removed in a future version.\nIt may not work as intended or expected. Use at your own risk.",
"@settingsExperimentalDeprecatedDescription": {
"description": "Description of the deprecated feature",
"context": "Visible in the settings view"
},
"settingsExperimentalDeprecatedFeature": "Deprecated feature, hold to learn more",
"@settingsExperimentalDeprecatedFeature": {
"description": "Text displayed when a feature is deprecated",
"context": "Visible in the settings view"
},
"settingsHost": "Host", "settingsHost": "Host",
"@settingsHost": { "@settingsHost": {
"description": "Text displayed as description for host input", "description": "Text displayed as description for host input",

View File

@ -15,6 +15,7 @@ import 'worker/haptic.dart';
import 'worker/sender.dart'; import 'worker/sender.dart';
import 'worker/desktop.dart'; import 'worker/desktop.dart';
import 'worker/theme.dart'; import 'worker/theme.dart';
import 'worker/update.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
// ignore: depend_on_referenced_packages // ignore: depend_on_referenced_packages
@ -33,6 +34,7 @@ import 'package:flutter_tts/flutter_tts.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:version/version.dart';
// client configuration // client configuration
@ -75,6 +77,7 @@ bool desktopTitleVisible = true;
bool logoVisible = true; bool logoVisible = true;
bool menuVisible = false; bool menuVisible = false;
bool sendable = false; bool sendable = false;
bool updateDetectedOnStart = false;
double sidebarIconSize = 1; double sidebarIconSize = 1;
SpeechToText speech = SpeechToText(); SpeechToText speech = SpeechToText();
@ -165,7 +168,9 @@ class _AppState extends State<App> {
} }
return const Locale("en"); return const Locale("en");
}, },
title: "Ollama", onGenerateTitle: (context) {
return AppLocalizations.of(context)!.appTitle;
},
theme: themeLight(), theme: themeLight(),
darkTheme: themeDark(), darkTheme: themeDark(),
themeMode: themeMode(), themeMode: themeMode(),
@ -299,9 +304,15 @@ class _MainAppState extends State<MainApp> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(top: 16, bottom: 16), padding: const EdgeInsets.only(top: 16, bottom: 16),
child: Row(children: [ child: Row(children: [
const Padding( Padding(
padding: EdgeInsets.only(left: 16, right: 12), padding: const EdgeInsets.only(left: 16, right: 12),
child: Icon(Icons.dns_rounded)), child: (updateStatus == "ok" &&
updateDetectedOnStart &&
(Version.parse(latestVersion ?? "1.0.0") >
Version.parse(
currentVersion ?? "2.0.0")))
? const Badge(child: Icon(Icons.dns_rounded))
: const Icon(Icons.dns_rounded)),
Expanded( Expanded(
child: Text( child: Text(
AppLocalizations.of(context)!.optionSettings, AppLocalizations.of(context)!.optionSettings,
@ -486,6 +497,8 @@ class _MainAppState extends State<MainApp> {
height: 24, height: 24,
width: 24, width: 24,
child: IconButton( child: IconButton(
tooltip: AppLocalizations.of(context)!
.tooltipReset,
onPressed: () { onPressed: () {
if (!chatAllowed && if (!chatAllowed &&
chatUuid == chatUuid ==
@ -685,14 +698,6 @@ class _MainAppState extends State<MainApp> {
})); }));
} }
// prefs!.remove("welcomeFinished");
if (!(prefs!.getBool("welcomeFinished") ?? false) && allowSettings) {
// ignore: use_build_context_synchronously
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const ScreenWelcome()));
return;
}
if (!(allowSettings || useHost)) { if (!(allowSettings || useHost)) {
// ignore: use_build_context_synchronously // ignore: use_build_context_synchronously
resetSystemNavigation(context, resetSystemNavigation(context,
@ -720,6 +725,14 @@ class _MainAppState extends State<MainApp> {
}); });
} }
// prefs!.remove("welcomeFinished");
if (!(prefs!.getBool("welcomeFinished") ?? false) && allowSettings) {
// ignore: use_build_context_synchronously
Navigator.of(context).pushReplacement(
MaterialPageRoute(builder: (context) => const ScreenWelcome()));
return;
}
if (!allowMultipleChats && if (!allowMultipleChats &&
(prefs!.getStringList("chats") ?? []).isNotEmpty) { (prefs!.getStringList("chats") ?? []).isNotEmpty) {
chatUuid = chatUuid =
@ -741,6 +754,11 @@ class _MainAppState extends State<MainApp> {
content: Text(AppLocalizations.of(context)!.noHostSelected), content: Text(AppLocalizations.of(context)!.noHostSelected),
showCloseIcon: true)); showCloseIcon: true));
} }
setState(() {});
if (prefs!.getBool("checkUpdateOnSettingsOpen") ?? false) {
updateDetectedOnStart = await checkUpdate(setState);
}
}, },
); );
} }

View File

@ -21,6 +21,7 @@ import 'package:http/http.dart' as http;
import 'package:bitsdojo_window/bitsdojo_window.dart'; import 'package:bitsdojo_window/bitsdojo_window.dart';
import 'package:dynamic_color/dynamic_color.dart'; import 'package:dynamic_color/dynamic_color.dart';
import 'package:transparent_image/transparent_image.dart'; import 'package:transparent_image/transparent_image.dart';
import 'package:version/version.dart';
Widget toggle(BuildContext context, String text, bool value, Widget toggle(BuildContext context, String text, bool value,
Function(bool value) onChanged, Function(bool value) onChanged,
@ -158,6 +159,7 @@ Widget button(String text, IconData? icon, void Function()? onPressed,
bool onlyDesktopDescription = true, bool onlyDesktopDescription = true,
bool alwaysMobileDescription = false, bool alwaysMobileDescription = false,
String? badge, String? badge,
String? iconBadge,
void Function()? onDisabledTap, void Function()? onDisabledTap,
void Function()? onLongTap, void Function()? onLongTap,
void Function()? onDoubleTap}) { void Function()? onDoubleTap}) {
@ -202,12 +204,18 @@ Widget button(String text, IconData? icon, void Function()? onPressed,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
child: Padding( child: Padding(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
child: Row(children: [ child: Builder(builder: (context) {
(icon != null || replaceIconIfNull) var iconContent = (icon != null || replaceIconIfNull)
? replaceIconIfNull ? replaceIconIfNull
? ImageIcon(MemoryImage(kTransparentImage)) ? ImageIcon(MemoryImage(kTransparentImage))
: Icon(icon, color: disabled ? Colors.grey : color) : Icon(icon, color: disabled ? Colors.grey : color)
: const SizedBox.shrink(), : const SizedBox.shrink();
return Row(children: [
(iconBadge == null)
? iconContent
: Badge(
label: (iconBadge != "") ? Text(iconBadge) : null,
child: iconContent),
(icon != null || replaceIconIfNull) (icon != null || replaceIconIfNull)
? const SizedBox(width: 16, height: 42) ? const SizedBox(width: 16, height: 42)
: const SizedBox.shrink(), : const SizedBox.shrink(),
@ -260,7 +268,8 @@ Widget button(String text, IconData? icon, void Function()? onPressed,
]); ]);
} }
})) }))
]), ]);
}),
)), )),
); );
} }
@ -353,7 +362,6 @@ class _ScreenSettingsState extends State<ScreenSettings> {
fixedHost)) { fixedHost)) {
checkHost(); checkHost();
} }
updatesSupported(setState, true);
} }
@override @override
@ -608,8 +616,10 @@ class _ScreenSettingsState extends State<ScreenSettings> {
context: context, context: context,
description: description:
"\n${AppLocalizations.of(context)!.settingsDescriptionExport}"), "\n${AppLocalizations.of(context)!.settingsDescriptionExport}"),
button( Builder(builder: (context) {
AppLocalizations.of(context)!.settingsTitleAbout, return button(
AppLocalizations.of(context)!
.settingsTitleAbout,
Icons.help_rounded, () { Icons.help_rounded, () {
selectionHaptic(); selectionHaptic();
Navigator.push( Navigator.push(
@ -620,7 +630,16 @@ class _ScreenSettingsState extends State<ScreenSettings> {
}, },
context: context, context: context,
description: description:
"\n${AppLocalizations.of(context)!.settingsDescriptionAbout}") "\n${AppLocalizations.of(context)!.settingsDescriptionAbout}",
iconBadge: (updateStatus == "ok" &&
updateDetectedOnStart &&
(Version.parse(
latestVersion ?? "1.0.0") >
Version.parse(
currentVersion ?? "2.0.0")))
? ""
: null);
})
]); ]);
animatedDesktop = desktopLayoutNotRequired(context); animatedDesktop = desktopLayoutNotRequired(context);
return Column(children: [ return Column(children: [

View File

@ -26,9 +26,6 @@ class _ScreenSettingsAboutState extends State<ScreenSettingsAbout> {
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
updatesSupported(setState, true); updatesSupported(setState, true);
setState(() {}); setState(() {});
if (prefs!.getBool("checkUpdateOnSettingsOpen") ?? false) {
checkUpdate(setState);
}
} }
@override @override
@ -75,12 +72,10 @@ class _ScreenSettingsAboutState extends State<ScreenSettingsAbout> {
Version.parse( Version.parse(
currentVersion ?? currentVersion ??
"2.0.0")) "2.0.0"))
? AppLocalizations.of( ? AppLocalizations.of(context)!
context)!
.settingsUpdateAvailable( .settingsUpdateAvailable(
latestVersion!) latestVersion!)
: AppLocalizations.of( : AppLocalizations.of(context)!
context)!
.settingsUpdateLatest), .settingsUpdateLatest),
((updateStatus != "ok") ((updateStatus != "ok")
? Icons.warning_rounded ? Icons.warning_rounded
@ -100,7 +95,13 @@ class _ScreenSettingsAboutState extends State<ScreenSettingsAbout> {
checkUpdate(setState); checkUpdate(setState);
return; return;
} }
}), },
iconBadge: (updateStatus == "ok" &&
updateDetectedOnStart &&
(Version.parse(latestVersion ?? "1.0.0") >
Version.parse(currentVersion ?? "2.0.0")))
? ""
: null),
(updateStatus == "notAvailable") (updateStatus == "notAvailable")
? const SizedBox.shrink() ? const SizedBox.shrink()
: toggle( : toggle(

View File

@ -8,6 +8,7 @@ import 'package:flutter/foundation.dart';
import '../main.dart'; import '../main.dart';
import '../worker/haptic.dart'; import '../worker/haptic.dart';
import '../worker/desktop.dart'; import '../worker/desktop.dart';
import '../worker/theme.dart';
import '../screen_settings.dart'; import '../screen_settings.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -101,9 +102,13 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
allowMultipleChats allowMultipleChats
? button( ? button(
AppLocalizations.of(context)!.settingsImportChats, AppLocalizations.of(context)!.settingsImportChats,
Icons.download_rounded, () { Icons.download_rounded, () async {
selectionHaptic(); selectionHaptic();
showDialog( resetSystemNavigation(context,
systemNavigationBarColor: Color.alphaBlend(
Colors.black54,
Theme.of(context).colorScheme.surface));
await showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AlertDialog(
@ -214,6 +219,8 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
.settingsImportChatsImport)) .settingsImportChatsImport))
]); ]);
}); });
// ignore: use_build_context_synchronously
resetSystemNavigation(context);
}) })
: const SizedBox.shrink() : const SizedBox.shrink()
]), ]),

View File

@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
import '../main.dart'; import '../main.dart';
import '../worker/haptic.dart'; import '../worker/haptic.dart';
import '../worker/desktop.dart'; import '../worker/desktop.dart';
import '../worker/theme.dart';
import '../screen_settings.dart'; import '../screen_settings.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
@ -190,6 +191,10 @@ class _ScreenSettingsInterfaceState extends State<ScreenSettingsInterface> {
.settingsKeepModelLoadedFor, .settingsKeepModelLoadedFor,
Icons.snooze_rounded, () async { Icons.snooze_rounded, () async {
selectionHaptic(); selectionHaptic();
resetSystemNavigation(context,
systemNavigationBarColor: Color.alphaBlend(
Colors.black54,
Theme.of(context).colorScheme.surface));
bool loaded = false; bool loaded = false;
await showDialog( await showDialog(
context: context, context: context,
@ -276,6 +281,8 @@ class _ScreenSettingsInterfaceState extends State<ScreenSettingsInterface> {
); );
})); }));
}); });
// ignore: use_build_context_synchronously
resetSystemNavigation(context);
}), }),
titleDivider(context: context), titleDivider(context: context),
button( button(
@ -307,7 +314,7 @@ class _ScreenSettingsInterfaceState extends State<ScreenSettingsInterface> {
null, null,
onlyDesktopDescription: false, onlyDesktopDescription: false,
description: description:
"\n${(prefs!.getDouble("timeoutMultiplier") ?? 1)} x 30s = ${((prefs!.getDouble("timeoutMultiplier") ?? 1) * 30).round()}s ${secondsBeautify((prefs!.getDouble("timeoutMultiplier") ?? 1) * 30)}"), "\n${((prefs!.getDouble("timeoutMultiplier") ?? 1) == 10) ? "${(prefs!.getDouble("timeoutMultiplier") ?? 1).round()}." : (prefs!.getDouble("timeoutMultiplier") ?? 1)} x 30s = ${((prefs!.getDouble("timeoutMultiplier") ?? 1) * 30).round()}s ${secondsBeautify((prefs!.getDouble("timeoutMultiplier") ?? 1) * 30)}"),
titleDivider(context: context), titleDivider(context: context),
toggle( toggle(
context, context,

View File

@ -366,6 +366,9 @@ void addModel(BuildContext context, Function setState) async {
} }
if (response.statusCode == 200) { if (response.statusCode == 200) {
bool returnValue = false; bool returnValue = false;
resetSystemNavigation(mainContext!,
systemNavigationBarColor: Color.alphaBlend(
Colors.black54, Theme.of(mainContext!).colorScheme.surface));
await showDialog( await showDialog(
context: mainContext!, context: mainContext!,
barrierDismissible: false, barrierDismissible: false,
@ -392,6 +395,7 @@ void addModel(BuildContext context, Function setState) async {
.modelDialogAddAssuranceAdd)) .modelDialogAddAssuranceAdd))
]); ]);
}); });
resetSystemNavigation(mainContext!);
return returnValue; return returnValue;
} }
return false; return false;
@ -665,8 +669,12 @@ Future<bool> deleteChatDialog(BuildContext context, Function setState,
} }
if ((prefs!.getBool("askBeforeDeletion") ?? false) && additionalCondition) { if ((prefs!.getBool("askBeforeDeletion") ?? false) && additionalCondition) {
// ignore: use_build_context_synchronously
resetSystemNavigation(context, resetSystemNavigation(context,
systemNavigationBarColor: Colors.grey.withOpacity(0.2)); systemNavigationBarColor: Color.alphaBlend(
Colors.black54,
// ignore: use_build_context_synchronously
Theme.of(context).colorScheme.surface));
await showDialog( await showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
@ -695,6 +703,8 @@ Future<bool> deleteChatDialog(BuildContext context, Function setState,
]); ]);
}); });
}); });
// ignore: use_build_context_synchronously
resetSystemNavigation(context);
} else { } else {
delete(context); delete(context);
} }

View File

@ -1,9 +1,11 @@
import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:ollama_app/worker/desktop.dart'; import 'package:ollama_app/worker/desktop.dart';
import 'haptic.dart'; import 'haptic.dart';
import 'theme.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import '../main.dart'; import '../main.dart';
@ -13,6 +15,7 @@ import 'package:install_referrer/install_referrer.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
import 'package:flutter_markdown/flutter_markdown.dart'; import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:version/version.dart';
const repoUrl = "https://github.com/JHubi1/ollama-app"; const repoUrl = "https://github.com/JHubi1/ollama-app";
@ -25,10 +28,6 @@ String? currentVersion;
String? updateChangeLog; String? updateChangeLog;
Future<bool> updatesSupported(Function setState, Future<bool> updatesSupported(Function setState,
[bool takeAction = false]) async { [bool takeAction = false]) async {
var tmp = (await PackageInfo.fromPlatform()).version;
setState(() {
currentVersion = tmp;
});
bool returnValue = true; bool returnValue = true;
var installerApps = [ var installerApps = [
"org.fdroid.fdroid", "org.fdroid.fdroid",
@ -45,10 +44,10 @@ Future<bool> updatesSupported(Function setState,
(installerApps (installerApps
.contains((await InstallReferrer.app).packageName ?? ""))) { .contains((await InstallReferrer.app).packageName ?? ""))) {
returnValue = false; returnValue = false;
// if (await InstallReferrer.referrer == if (await InstallReferrer.referrer ==
// InstallationAppReferrer.androidDebug) { InstallationAppReferrer.androidDebug) {
// returnValue = true; returnValue = true;
// } }
} }
if (!repoUrl.startsWith("https://github.com")) { if (!repoUrl.startsWith("https://github.com")) {
returnValue = false; returnValue = false;
@ -64,7 +63,7 @@ Future<bool> updatesSupported(Function setState,
return returnValue; return returnValue;
} }
void checkUpdate(Function setState) async { Future<bool> checkUpdate(Function setState) async {
try { try {
setState(() { setState(() {
updateChecked = true; updateChecked = true;
@ -76,7 +75,7 @@ void checkUpdate(Function setState) async {
updateStatus = "notAvailable"; updateStatus = "notAvailable";
updateLoading = false; updateLoading = false;
}); });
return; return false;
} }
var repo = repoUrl.split("/"); var repo = repoUrl.split("/");
@ -98,7 +97,7 @@ void checkUpdate(Function setState) async {
updateStatus = "rateLimit"; updateStatus = "rateLimit";
updateLoading = false; updateLoading = false;
}); });
return; return false;
} }
version = jsonDecode(request.body)[0]["tag_name"]; version = jsonDecode(request.body)[0]["tag_name"];
updateChangeLog = jsonDecode(request.body)[0]["body"]; updateChangeLog = jsonDecode(request.body)[0]["body"];
@ -108,7 +107,7 @@ void checkUpdate(Function setState) async {
updateStatus = "error"; updateStatus = "error";
updateLoading = false; updateLoading = false;
}); });
return; return false;
} }
latestVersion = version; latestVersion = version;
@ -123,10 +122,16 @@ void checkUpdate(Function setState) async {
updateLoading = false; updateLoading = false;
}); });
} }
return (updateStatus == "ok" &&
(Version.parse(latestVersion ?? "1.0.0") >
Version.parse(currentVersion ?? "2.0.0")));
} }
void updateDialog(BuildContext context, Function title) { void updateDialog(BuildContext context, Function title) async {
showDialog( resetSystemNavigation(context,
systemNavigationBarColor: Color.alphaBlend(
Colors.black54, Theme.of(context).colorScheme.surface));
await showDialog(
context: context, context: context,
builder: (context) { builder: (context) {
return AlertDialog( return AlertDialog(
@ -165,4 +170,6 @@ void updateDialog(BuildContext context, Function title) {
AppLocalizations.of(context)!.settingsUpdateDialogUpdate)) AppLocalizations.of(context)!.settingsUpdateDialogUpdate))
]); ]);
}); });
// ignore: use_build_context_synchronously
resetSystemNavigation(context);
} }

View File

@ -2,6 +2,7 @@
"de": [ "de": [
"deleteChat", "deleteChat",
"renameChat", "renameChat",
"tooltipReset",
"modelDialogAddPromptTitle", "modelDialogAddPromptTitle",
"modelDialogAddPromptDescription", "modelDialogAddPromptDescription",
"modelDialogAddPromptAlreadyExists", "modelDialogAddPromptAlreadyExists",
@ -19,6 +20,9 @@
"settingsDescriptionVoice", "settingsDescriptionVoice",
"settingsDescriptionExport", "settingsDescriptionExport",
"settingsDescriptionAbout", "settingsDescriptionAbout",
"settingsExperimentalDeprecated",
"settingsExperimentalDeprecatedDescription",
"settingsExperimentalDeprecatedFeature",
"settingsUseSystem", "settingsUseSystem",
"settingsUseSystemDescription", "settingsUseSystemDescription",
"settingsPreloadModels", "settingsPreloadModels",
@ -39,6 +43,7 @@
"it": [ "it": [
"deleteChat", "deleteChat",
"renameChat", "renameChat",
"tooltipReset",
"modelDialogAddPromptTitle", "modelDialogAddPromptTitle",
"modelDialogAddPromptDescription", "modelDialogAddPromptDescription",
"modelDialogAddPromptAlreadyExists", "modelDialogAddPromptAlreadyExists",
@ -56,6 +61,9 @@
"settingsDescriptionVoice", "settingsDescriptionVoice",
"settingsDescriptionExport", "settingsDescriptionExport",
"settingsDescriptionAbout", "settingsDescriptionAbout",
"settingsExperimentalDeprecated",
"settingsExperimentalDeprecatedDescription",
"settingsExperimentalDeprecatedFeature",
"settingsUseSystem", "settingsUseSystem",
"settingsUseSystemDescription", "settingsUseSystemDescription",
"settingsPreloadModels", "settingsPreloadModels",
@ -76,6 +84,7 @@
"tr": [ "tr": [
"deleteChat", "deleteChat",
"renameChat", "renameChat",
"tooltipReset",
"modelDialogAddPromptTitle", "modelDialogAddPromptTitle",
"modelDialogAddPromptDescription", "modelDialogAddPromptDescription",
"modelDialogAddPromptAlreadyExists", "modelDialogAddPromptAlreadyExists",
@ -93,6 +102,9 @@
"settingsDescriptionVoice", "settingsDescriptionVoice",
"settingsDescriptionExport", "settingsDescriptionExport",
"settingsDescriptionAbout", "settingsDescriptionAbout",
"settingsExperimentalDeprecated",
"settingsExperimentalDeprecatedDescription",
"settingsExperimentalDeprecatedFeature",
"settingsUseSystem", "settingsUseSystem",
"settingsUseSystemDescription", "settingsUseSystemDescription",
"settingsPreloadModels", "settingsPreloadModels",
@ -113,6 +125,7 @@
"zh": [ "zh": [
"deleteChat", "deleteChat",
"renameChat", "renameChat",
"tooltipReset",
"modelDialogAddPromptTitle", "modelDialogAddPromptTitle",
"modelDialogAddPromptDescription", "modelDialogAddPromptDescription",
"modelDialogAddPromptAlreadyExists", "modelDialogAddPromptAlreadyExists",
@ -130,6 +143,9 @@
"settingsDescriptionVoice", "settingsDescriptionVoice",
"settingsDescriptionExport", "settingsDescriptionExport",
"settingsDescriptionAbout", "settingsDescriptionAbout",
"settingsExperimentalDeprecated",
"settingsExperimentalDeprecatedDescription",
"settingsExperimentalDeprecatedFeature",
"settingsUseSystem", "settingsUseSystem",
"settingsUseSystemDescription", "settingsUseSystemDescription",
"settingsPreloadModels", "settingsPreloadModels",