Added update checker, updated desktop view
This commit is contained in:
parent
08d566e855
commit
0a996e6ed2
|
@ -317,6 +317,47 @@
|
|||
"description": "Text displayed when chats are imported successfully",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsUpdateCheck": "Check for updates",
|
||||
"@settingsUpdateCheck": {
|
||||
"description": "Text displayed as description for check for updates button",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsUpdateChecking": "Checking for updates ...",
|
||||
"@settingsUpdateChecking": {
|
||||
"description": "Text displayed while looking for updates",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsUpdateLatest": "You are on the latest version",
|
||||
"@settingsUpdateLatest": {
|
||||
"description": "Text displayed when the app is up to date",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsUpdateAvailable": "Update available (v{version})",
|
||||
"@settingsUpdateAvailable": {
|
||||
"description": "Text displayed when an update is available",
|
||||
"context": "Visible in the settings view",
|
||||
"placeholders": {
|
||||
"version": {
|
||||
"type": "String",
|
||||
"description": "Version number of the available update"
|
||||
}
|
||||
}
|
||||
},
|
||||
"settingsUpdateRateLimit": "API rate limit exceeded. Not able to check for updates",
|
||||
"@settingsUpdateRateLimit": {
|
||||
"description": "Text displayed when the API rate limit is exceeded",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsUpdateIssue": "An issue occurred while checking for updates. Click to retry",
|
||||
"@settingsUpdateIssue": {
|
||||
"description": "Text displayed when an issue occurs while checking for updates",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsCheckForUpdates": "Check for update on settings open",
|
||||
"@settingsCheckForUpdates": {
|
||||
"description": "Text displayed as description for check for updates toggle",
|
||||
"context": "Visible in the settings view"
|
||||
},
|
||||
"settingsGithub": "GitHub",
|
||||
"@settingsGithub": {
|
||||
"description": "Text displayed as description for GitHub button",
|
||||
|
|
|
@ -64,6 +64,8 @@ bool chatAllowed = true;
|
|||
final user = types.User(id: const Uuid().v4());
|
||||
final assistant = types.User(id: const Uuid().v4());
|
||||
|
||||
bool settingsOpen = false;
|
||||
|
||||
void main() {
|
||||
runApp(const App());
|
||||
|
||||
|
@ -92,7 +94,9 @@ class _AppState extends State<App> {
|
|||
super.initState();
|
||||
|
||||
void load() async {
|
||||
try {
|
||||
await FlutterDisplayMode.setHighRefreshRate();
|
||||
} catch (_) {}
|
||||
SharedPreferences.setPrefix("ollama.");
|
||||
SharedPreferences tmp = await SharedPreferences.getInstance();
|
||||
setState(() {
|
||||
|
@ -215,7 +219,12 @@ class _MainAppState extends State<MainApp> {
|
|||
borderRadius: BorderRadius.all(Radius.circular(50))),
|
||||
onTap: () {
|
||||
HapticFeedback.selectionClick();
|
||||
Navigator.of(context).pop();
|
||||
if (!(Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width <= 1000) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (!chatAllowed) return;
|
||||
chatUuid = null;
|
||||
messages = [];
|
||||
|
@ -246,9 +255,14 @@ class _MainAppState extends State<MainApp> {
|
|||
borderRadius: BorderRadius.all(Radius.circular(50))),
|
||||
onTap: () {
|
||||
HapticFeedback.selectionClick();
|
||||
Navigator.of(context).pop();
|
||||
if (!(Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width <= 1000) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
setState(() {
|
||||
logoVisible = false;
|
||||
settingsOpen = true;
|
||||
});
|
||||
Navigator.push(
|
||||
context,
|
||||
|
@ -328,6 +342,7 @@ class _MainAppState extends State<MainApp> {
|
|||
splashFactory: NoSplash.splashFactory,
|
||||
highlightColor: Colors.transparent,
|
||||
enableFeedback: false,
|
||||
hoverColor: Colors.transparent,
|
||||
onTap: () {
|
||||
HapticFeedback.selectionClick();
|
||||
setState(() {
|
||||
|
@ -422,7 +437,12 @@ class _MainAppState extends State<MainApp> {
|
|||
if (chatUuid == jsonDecode(item)["uuid"]) {
|
||||
messages = [];
|
||||
chatUuid = null;
|
||||
Navigator.of(context).pop();
|
||||
if (!(Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width <= 1000) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
}
|
||||
setState(() {});
|
||||
},
|
||||
|
@ -433,7 +453,12 @@ class _MainAppState extends State<MainApp> {
|
|||
borderRadius: BorderRadius.all(Radius.circular(50))),
|
||||
onTap: () {
|
||||
HapticFeedback.selectionClick();
|
||||
Navigator.of(context).pop();
|
||||
if (!(Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width <= 1000) {
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
if (!chatAllowed) return;
|
||||
loadChat(jsonDecode(item)["uuid"], setState);
|
||||
chatUuid = jsonDecode(item)["uuid"];
|
||||
|
@ -609,6 +634,7 @@ class _MainAppState extends State<MainApp> {
|
|||
splashFactory: NoSplash.splashFactory,
|
||||
highlightColor: Colors.transparent,
|
||||
enableFeedback: false,
|
||||
hoverColor: Colors.transparent,
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
child: Row(
|
||||
|
@ -787,7 +813,20 @@ class _MainAppState extends State<MainApp> {
|
|||
preferredSize: const Size.fromHeight(1),
|
||||
child: (!chatAllowed && model != null)
|
||||
? const LinearProgressIndicator()
|
||||
: const SizedBox.shrink()),
|
||||
: ((Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000)
|
||||
? AnimatedOpacity(
|
||||
opacity: menuVisible ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: Divider(
|
||||
height: 2,
|
||||
color: (Theme.of(context).brightness ==
|
||||
Brightness.light)
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[900]))
|
||||
: const SizedBox.shrink()),
|
||||
leading: ((Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
|
@ -806,6 +845,7 @@ class _MainAppState extends State<MainApp> {
|
|||
child: VisibilityDetector(
|
||||
key: const Key("menuVisible"),
|
||||
onVisibilityChanged: (VisibilityInfo info) {
|
||||
if (settingsOpen) return;
|
||||
menuVisible = info.visibleFraction > 0;
|
||||
try {
|
||||
setState(() {});
|
||||
|
@ -819,11 +859,15 @@ class _MainAppState extends State<MainApp> {
|
|||
: const SizedBox.shrink(),
|
||||
((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000)
|
||||
? VerticalDivider(
|
||||
width: 2,
|
||||
color: (Theme.of(context).brightness == Brightness.light)
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[900])
|
||||
? AnimatedOpacity(
|
||||
opacity: menuVisible ? 1.0 : 0.0,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
child: VerticalDivider(
|
||||
width: 2,
|
||||
color:
|
||||
(Theme.of(context).brightness == Brightness.light)
|
||||
? Colors.grey[400]
|
||||
: Colors.grey[900]))
|
||||
: const SizedBox.shrink(),
|
||||
Expanded(
|
||||
child: Chat(
|
||||
|
@ -1021,6 +1065,7 @@ class _MainAppState extends State<MainApp> {
|
|||
child: VisibilityDetector(
|
||||
key: const Key("logoVisible"),
|
||||
onVisibilityChanged: (VisibilityInfo info) {
|
||||
if (settingsOpen) return;
|
||||
logoVisible = info.visibleFraction > 0;
|
||||
try {
|
||||
setState(() {});
|
||||
|
@ -1515,9 +1560,13 @@ class _MainAppState extends State<MainApp> {
|
|||
sendable = p0.trim().isNotEmpty;
|
||||
});
|
||||
},
|
||||
sendButtonVisibilityMode: (sendable)
|
||||
sendButtonVisibilityMode: (Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS)
|
||||
? SendButtonVisibilityMode.always
|
||||
: SendButtonVisibilityMode.hidden),
|
||||
: (sendable)
|
||||
? SendButtonVisibilityMode.always
|
||||
: SendButtonVisibilityMode.hidden),
|
||||
user: user,
|
||||
hideBackgroundOnEmojiMessages: false,
|
||||
theme: (Theme.of(context).brightness == Brightness.light)
|
||||
|
@ -1585,6 +1634,10 @@ class _MainAppState extends State<MainApp> {
|
|||
: 440))),
|
||||
],
|
||||
),
|
||||
drawerEdgeDragWidth:
|
||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)
|
||||
? null
|
||||
: MediaQuery.of(context).size.width,
|
||||
drawer: Builder(builder: (context) {
|
||||
if ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000) {
|
||||
|
|
|
@ -16,6 +16,9 @@ import 'package:url_launcher/url_launcher.dart';
|
|||
import 'package:restart_app/restart_app.dart';
|
||||
import 'package:file_picker/file_picker.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:install_referrer/install_referrer.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'package:version/version.dart';
|
||||
|
||||
class ScreenSettings extends StatefulWidget {
|
||||
const ScreenSettings({super.key});
|
||||
|
@ -68,7 +71,6 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
|||
}
|
||||
if ((request.statusCode == 200 && request.body == "Ollama is running") ||
|
||||
(Uri.parse(tmpHost).toString() == fixedHost)) {
|
||||
// messages = [];
|
||||
setState(() {
|
||||
hostLoading = false;
|
||||
host = tmpHost;
|
||||
|
@ -91,10 +93,102 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
|||
|
||||
final repoUrl = "https://github.com/JHubi1/ollama-app";
|
||||
|
||||
bool updateChecked = false;
|
||||
bool updateLoading = false;
|
||||
String updateStatus = "ok";
|
||||
String? updateUrl;
|
||||
String? latestVersion;
|
||||
String? currentVersion;
|
||||
Future<bool> updatesSupported([bool takeAction = false]) async {
|
||||
bool returnValue = true;
|
||||
var installerApps = [
|
||||
"org.fdroid.fdroid",
|
||||
"org.gdroid.gdroid",
|
||||
"eu.bubu1.fdroidclassic",
|
||||
"in.sunilpaulmathew.izzyondroid",
|
||||
"com.looker.droidify",
|
||||
"com.machiav3lli.fdroid",
|
||||
"nya.kitsunyan.foxydroid"
|
||||
];
|
||||
if ((await InstallReferrer.referrer ==
|
||||
InstallationAppReferrer.androidManually) &&
|
||||
!(installerApps
|
||||
.contains((await InstallReferrer.app).packageName ?? ""))) {
|
||||
returnValue = false;
|
||||
}
|
||||
if (!repoUrl.startsWith("https://github.com")) {
|
||||
returnValue = false;
|
||||
}
|
||||
|
||||
if (!returnValue && takeAction) {
|
||||
setState(() {
|
||||
updateStatus = "notAvailable";
|
||||
updateLoading = false;
|
||||
});
|
||||
}
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
void checkUpdate() async {
|
||||
setState(() {
|
||||
updateChecked = true;
|
||||
updateLoading = true;
|
||||
});
|
||||
|
||||
if (!await updatesSupported()) {
|
||||
setState(() {
|
||||
updateStatus = "notAvailable";
|
||||
updateLoading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var repo = repoUrl.split("/");
|
||||
|
||||
currentVersion = (await PackageInfo.fromPlatform()).version;
|
||||
// currentVersion = "1.0.0";
|
||||
|
||||
String? version;
|
||||
try {
|
||||
var request = await http
|
||||
.get(Uri.parse(
|
||||
"https://api.github.com/repos/${repo[3]}/${repo[4]}/tags"))
|
||||
.timeout(const Duration(seconds: 5));
|
||||
if (request.statusCode == 403) {
|
||||
setState(() {
|
||||
updateStatus = "rateLimit";
|
||||
updateLoading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
version = jsonDecode(request.body)[0]["name"];
|
||||
} catch (_) {
|
||||
setState(() {
|
||||
updateStatus = "error";
|
||||
updateLoading = false;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
latestVersion = version;
|
||||
updateUrl = "$repoUrl/releases/tag/$latestVersion";
|
||||
updateStatus = "ok";
|
||||
|
||||
setState(() {
|
||||
updateLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
checkHost();
|
||||
updatesSupported(true);
|
||||
if (prefs!.getBool("checkUpdateOnSettingsOpen") ?? false) {
|
||||
checkUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -153,73 +247,54 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
|||
return PopScope(
|
||||
canPop: !hostLoading,
|
||||
onPopInvoked: (didPop) {
|
||||
settingsOpen = false;
|
||||
FocusManager.instance.primaryFocus?.unfocus();
|
||||
},
|
||||
child: WindowBorder(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Row(children: [
|
||||
Text(AppLocalizations.of(context)!.optionSettings),
|
||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||
]),
|
||||
actions:
|
||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)
|
||||
? [
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: WindowTitleBarBox(
|
||||
child: Row(
|
||||
children: [
|
||||
// Expanded(child: MoveWindow()),
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: MinimizeWindowButton(
|
||||
animate: true,
|
||||
colors: WindowButtonColors(
|
||||
iconNormal: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary))),
|
||||
SizedBox(
|
||||
height: 72,
|
||||
child: MaximizeWindowButton(
|
||||
animate: true,
|
||||
colors: WindowButtonColors(
|
||||
iconNormal: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary))),
|
||||
SizedBox(
|
||||
height: 72,
|
||||
child: CloseWindowButton(
|
||||
animate: true,
|
||||
colors: WindowButtonColors(
|
||||
iconNormal: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary))),
|
||||
],
|
||||
)))
|
||||
]
|
||||
: null,
|
||||
leading: (Navigator.of(context).canPop())
|
||||
? null
|
||||
: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
if (hostLoading) return;
|
||||
Navigator.of(context).pushReplacement(
|
||||
// PageRouteBuilder(
|
||||
// pageBuilder: (context, animation,
|
||||
// secondaryAnimation) =>
|
||||
// const MainApp(),
|
||||
// // transitionDuration: const Duration(seconds: 1),
|
||||
// transitionsBuilder: (context, animation,
|
||||
// secondaryAnimation, child) {
|
||||
// return FadeTransition(
|
||||
// opacity: animation, child: child);
|
||||
// })
|
||||
MaterialPageRoute(
|
||||
builder: (context) => const MainApp()));
|
||||
})),
|
||||
title: Row(children: [
|
||||
Text(AppLocalizations.of(context)!.optionSettings),
|
||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||
]),
|
||||
actions:
|
||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)
|
||||
? [
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: WindowTitleBarBox(
|
||||
child: Row(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: MinimizeWindowButton(
|
||||
animate: true,
|
||||
colors: WindowButtonColors(
|
||||
iconNormal: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary))),
|
||||
SizedBox(
|
||||
height: 72,
|
||||
child: MaximizeWindowButton(
|
||||
animate: true,
|
||||
colors: WindowButtonColors(
|
||||
iconNormal: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary))),
|
||||
SizedBox(
|
||||
height: 72,
|
||||
child: CloseWindowButton(
|
||||
animate: true,
|
||||
colors: WindowButtonColors(
|
||||
iconNormal: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary))),
|
||||
],
|
||||
)))
|
||||
]
|
||||
: null,
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||
child: ListView(children: [
|
||||
|
@ -619,6 +694,77 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
|||
.settingsImportChats))
|
||||
])),
|
||||
title(AppLocalizations.of(context)!.settingsTitleContact),
|
||||
(updateStatus == "notAvailable")
|
||||
? const SizedBox.shrink()
|
||||
: InkWell(
|
||||
onTap: () {
|
||||
if (updateLoading) return;
|
||||
if ((Version.parse(latestVersion ?? "1.0.0") >
|
||||
Version.parse(currentVersion ?? "2.0.0")) &&
|
||||
(updateStatus == "ok")) {
|
||||
launchUrl(
|
||||
mode: LaunchMode.inAppBrowserView,
|
||||
Uri.parse(updateUrl!));
|
||||
} else {
|
||||
checkUpdate();
|
||||
return;
|
||||
}
|
||||
},
|
||||
child: Row(children: [
|
||||
updateLoading
|
||||
? SizedBox(
|
||||
width: 24,
|
||||
height: 24,
|
||||
child: Transform.scale(
|
||||
scale: 0.5,
|
||||
child:
|
||||
const CircularProgressIndicator()),
|
||||
)
|
||||
: Icon((updateStatus != "ok")
|
||||
? Icons.warning_rounded
|
||||
: (Version.parse(latestVersion ?? "1.0.0") >
|
||||
Version.parse(
|
||||
currentVersion ?? "2.0.0"))
|
||||
? Icons.info_outline_rounded
|
||||
: Icons.update_rounded),
|
||||
const SizedBox(width: 16, height: 42),
|
||||
Expanded(
|
||||
child: Text(!updateChecked
|
||||
? AppLocalizations.of(context)!
|
||||
.settingsUpdateCheck
|
||||
: updateLoading
|
||||
? AppLocalizations.of(context)!
|
||||
.settingsUpdateChecking
|
||||
: (updateStatus == "rateLimit")
|
||||
? AppLocalizations.of(context)!
|
||||
.settingsUpdateRateLimit
|
||||
: (updateStatus != "ok")
|
||||
? AppLocalizations.of(context)!
|
||||
.settingsUpdateIssue
|
||||
: (Version.parse(
|
||||
latestVersion ??
|
||||
"1.0.0") >
|
||||
Version.parse(
|
||||
currentVersion ??
|
||||
"2.0.0"))
|
||||
? AppLocalizations.of(
|
||||
context)!
|
||||
.settingsUpdateAvailable(
|
||||
latestVersion!)
|
||||
: AppLocalizations.of(
|
||||
context)!
|
||||
.settingsUpdateLatest))
|
||||
])),
|
||||
(updateStatus == "notAvailable")
|
||||
? const SizedBox.shrink()
|
||||
: toggle(
|
||||
AppLocalizations.of(context)!.settingsCheckForUpdates,
|
||||
(prefs!.getBool("checkUpdateOnSettingsOpen") ??
|
||||
false), (value) {
|
||||
HapticFeedback.selectionClick();
|
||||
prefs!.setBool("checkUpdateOnSettingsOpen", value);
|
||||
setState(() {});
|
||||
}),
|
||||
InkWell(
|
||||
onTap: () {
|
||||
launchUrl(
|
||||
|
|
|
@ -64,139 +64,161 @@ void setModel(BuildContext context, Function setState) {
|
|||
|
||||
if (useModel) return;
|
||||
HapticFeedback.selectionClick();
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return StatefulBuilder(builder: (context, setLocalState) {
|
||||
setModalState = setLocalState;
|
||||
return PopScope(
|
||||
canPop: loaded,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!loaded) return;
|
||||
if (usedIndex >= 0 &&
|
||||
modelsReal[usedIndex] != model &&
|
||||
(prefs!.getBool("resetOnModelSelect") ?? true)) {
|
||||
messages = [];
|
||||
}
|
||||
model = (usedIndex >= 0) ? modelsReal[usedIndex] : null;
|
||||
chatAllowed = !(model == null);
|
||||
multimodal = (usedIndex >= 0) ? modal[usedIndex] : false;
|
||||
if (model != null) {
|
||||
prefs?.setString("model", model!);
|
||||
} else {
|
||||
prefs?.remove("model");
|
||||
}
|
||||
prefs?.setBool("multimodal", multimodal);
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
width: double.infinity,
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom: (Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS)
|
||||
? 16
|
||||
: 0),
|
||||
child: (!loaded)
|
||||
? const LinearProgressIndicator()
|
||||
: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
constraints: BoxConstraints(
|
||||
maxHeight:
|
||||
MediaQuery.of(context).size.height * 0.4),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Wrap(
|
||||
spacing: 5.0,
|
||||
runSpacing: 5.0,
|
||||
alignment: WrapAlignment.center,
|
||||
children: List<Widget>.generate(
|
||||
models.length,
|
||||
(int index) {
|
||||
return ChoiceChip(
|
||||
label: Text(
|
||||
(prefs!.getBool("modelTags") ??
|
||||
false)
|
||||
? modelsReal[index]
|
||||
: models[index]),
|
||||
selected: usedIndex == index,
|
||||
avatar: (usedIndex == index)
|
||||
? null
|
||||
: (addIndex == index)
|
||||
? const Icon(
|
||||
Icons.add_rounded)
|
||||
: ((recommendedModels
|
||||
.contains(
|
||||
models[index]))
|
||||
? const Icon(
|
||||
Icons.star_rounded)
|
||||
: ((modal[index])
|
||||
? const Icon(Icons
|
||||
.collections_rounded)
|
||||
: null)),
|
||||
checkmarkColor: (usedIndex == index)
|
||||
? ((MediaQuery.of(context)
|
||||
.platformBrightness ==
|
||||
Brightness.light)
|
||||
? (theme ?? ThemeData())
|
||||
.colorScheme
|
||||
.secondary
|
||||
: (themeDark ??
|
||||
ThemeData.dark())
|
||||
.colorScheme
|
||||
.secondary)
|
||||
: null,
|
||||
labelStyle: (usedIndex == index)
|
||||
? TextStyle(
|
||||
color: (MediaQuery.of(context)
|
||||
.platformBrightness ==
|
||||
Brightness.light)
|
||||
? (theme ?? ThemeData())
|
||||
.colorScheme
|
||||
.secondary
|
||||
: (themeDark ??
|
||||
ThemeData.dark())
|
||||
.colorScheme
|
||||
.secondary)
|
||||
: null,
|
||||
selectedColor: (MediaQuery.of(context)
|
||||
.platformBrightness ==
|
||||
Brightness.light)
|
||||
? (theme ?? ThemeData())
|
||||
.colorScheme
|
||||
.primary
|
||||
: (themeDark ?? ThemeData.dark())
|
||||
.colorScheme
|
||||
.primary,
|
||||
onSelected: (bool selected) {
|
||||
if (addIndex == index) {
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.modelDialogAddSteps),
|
||||
showCloseIcon: true));
|
||||
}
|
||||
if (!chatAllowed && model != null) {
|
||||
return;
|
||||
}
|
||||
setLocalState(() {
|
||||
usedIndex = selected ? index : -1;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
)))
|
||||
])));
|
||||
|
||||
var content = StatefulBuilder(builder: (context, setLocalState) {
|
||||
setModalState = setLocalState;
|
||||
return PopScope(
|
||||
canPop: loaded,
|
||||
onPopInvoked: (didPop) {
|
||||
if (!loaded) return;
|
||||
if (usedIndex >= 0 &&
|
||||
modelsReal[usedIndex] != model &&
|
||||
(prefs!.getBool("resetOnModelSelect") ?? true)) {
|
||||
messages = [];
|
||||
}
|
||||
model = (usedIndex >= 0) ? modelsReal[usedIndex] : null;
|
||||
chatAllowed = !(model == null);
|
||||
multimodal = (usedIndex >= 0) ? modal[usedIndex] : false;
|
||||
if (model != null) {
|
||||
prefs?.setString("model", model!);
|
||||
} else {
|
||||
prefs?.remove("model");
|
||||
}
|
||||
prefs?.setBool("multimodal", multimodal);
|
||||
setState(() {});
|
||||
},
|
||||
child: Container(
|
||||
width:
|
||||
((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000)
|
||||
? null
|
||||
: double.infinity,
|
||||
padding: EdgeInsets.only(
|
||||
left: 16,
|
||||
right: 16,
|
||||
top: 16,
|
||||
bottom:
|
||||
(Platform.isWindows || Platform.isLinux || Platform.isMacOS)
|
||||
? 16
|
||||
: 0),
|
||||
child: (!loaded)
|
||||
? const LinearProgressIndicator()
|
||||
: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||
Container(
|
||||
width: ((Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000)
|
||||
? 300
|
||||
: double.infinity,
|
||||
constraints: BoxConstraints(
|
||||
maxHeight:
|
||||
MediaQuery.of(context).size.height * 0.4),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.vertical,
|
||||
child: Wrap(
|
||||
spacing: ((Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000)
|
||||
? 10.0
|
||||
: 5.0,
|
||||
runSpacing: (Platform.isWindows ||
|
||||
Platform.isLinux ||
|
||||
Platform.isMacOS)
|
||||
? (MediaQuery.of(context).size.width >= 1000)
|
||||
? 10.0
|
||||
: 5.0
|
||||
: 0.0,
|
||||
alignment: WrapAlignment.center,
|
||||
children: List<Widget>.generate(
|
||||
models.length,
|
||||
(int index) {
|
||||
return ChoiceChip(
|
||||
label: Text(
|
||||
(prefs!.getBool("modelTags") ?? false)
|
||||
? modelsReal[index]
|
||||
: models[index]),
|
||||
selected: usedIndex == index,
|
||||
avatar: (usedIndex == index)
|
||||
? null
|
||||
: (addIndex == index)
|
||||
? const Icon(Icons.add_rounded)
|
||||
: ((recommendedModels
|
||||
.contains(models[index]))
|
||||
? const Icon(Icons.star_rounded)
|
||||
: ((modal[index])
|
||||
? const Icon(Icons
|
||||
.collections_rounded)
|
||||
: null)),
|
||||
checkmarkColor: (usedIndex == index)
|
||||
? ((MediaQuery.of(context)
|
||||
.platformBrightness ==
|
||||
Brightness.light)
|
||||
? (theme ?? ThemeData())
|
||||
.colorScheme
|
||||
.secondary
|
||||
: (themeDark ?? ThemeData.dark())
|
||||
.colorScheme
|
||||
.secondary)
|
||||
: null,
|
||||
labelStyle: (usedIndex == index)
|
||||
? TextStyle(
|
||||
color: (MediaQuery.of(context)
|
||||
.platformBrightness ==
|
||||
Brightness.light)
|
||||
? (theme ?? ThemeData())
|
||||
.colorScheme
|
||||
.secondary
|
||||
: (themeDark ??
|
||||
ThemeData.dark())
|
||||
.colorScheme
|
||||
.secondary)
|
||||
: null,
|
||||
selectedColor: (MediaQuery.of(context)
|
||||
.platformBrightness ==
|
||||
Brightness.light)
|
||||
? (theme ?? ThemeData())
|
||||
.colorScheme
|
||||
.primary
|
||||
: (themeDark ?? ThemeData.dark())
|
||||
.colorScheme
|
||||
.primary,
|
||||
onSelected: (bool selected) {
|
||||
if (addIndex == index) {
|
||||
Navigator.of(context).pop();
|
||||
ScaffoldMessenger.of(context)
|
||||
.showSnackBar(SnackBar(
|
||||
content: Text(
|
||||
AppLocalizations.of(
|
||||
context)!
|
||||
.modelDialogAddSteps),
|
||||
showCloseIcon: true));
|
||||
}
|
||||
if (!chatAllowed && model != null) {
|
||||
return;
|
||||
}
|
||||
setLocalState(() {
|
||||
usedIndex = selected ? index : -1;
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
)))
|
||||
])));
|
||||
});
|
||||
|
||||
if ((Platform.isWindows || Platform.isLinux || Platform.isMacOS) &&
|
||||
MediaQuery.of(context).size.width >= 1000) {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(alignment: Alignment.topCenter, child: content);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
showModalBottomSheet(context: context, builder: (context) => content);
|
||||
}
|
||||
}
|
||||
|
||||
void saveChat(String uuid, Function setState) async {
|
||||
|
|
32
pubspec.lock
32
pubspec.lock
|
@ -421,6 +421,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+1"
|
||||
install_referrer:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: install_referrer
|
||||
sha256: "901c56d24ee3c3010dfd0bbebf305ed6b4b0f3fe969192081c167590a64cd78b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -526,6 +534,22 @@ packages:
|
|||
url: "https://github.com/davidmigloz/langchain_dart.git"
|
||||
source: git
|
||||
version: "0.1.0+1"
|
||||
package_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: b93d8b4d624b4ea19b0a5a208b2d6eff06004bc3ce74c06040b120eeadd00ce0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus_platform_interface
|
||||
sha256: f49918f3433a3146047372f9d4f1f847511f2acd5cd030e1f44fe5a50036b70e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -835,6 +859,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: version
|
||||
sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
visibility_detector:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
19
pubspec.yaml
19
pubspec.yaml
|
@ -9,22 +9,22 @@ environment:
|
|||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
ollama_dart:
|
||||
git:
|
||||
url: https://github.com/davidmigloz/langchain_dart.git
|
||||
path: packages/ollama_dart
|
||||
ref: ce2ef30c9a9a0dfe8f3059988b7007c94c45b9bd
|
||||
intl: any
|
||||
shared_preferences: ^2.2.3
|
||||
flutter_chat_ui: ^1.6.13
|
||||
uuid: ^4.4.0
|
||||
animated_text_kit: ^4.2.2
|
||||
image_picker: ^1.1.1
|
||||
visibility_detector: ^0.4.0+2
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
http: ^1.2.1
|
||||
dartx: ^1.2.0
|
||||
ollama_dart:
|
||||
git:
|
||||
url: https://github.com/davidmigloz/langchain_dart.git
|
||||
path: packages/ollama_dart
|
||||
ref: ce2ef30c9a9a0dfe8f3059988b7007c94c45b9bd
|
||||
smooth_page_indicator: ^1.1.0
|
||||
transparent_image: ^2.0.1
|
||||
simple_icons: ^10.1.3
|
||||
|
@ -33,6 +33,9 @@ dependencies:
|
|||
flutter_markdown: ^0.7.1
|
||||
file_picker: ^8.0.3
|
||||
bitsdojo_window: ^0.1.6
|
||||
install_referrer: ^1.2.1
|
||||
package_info_plus: ^8.0.0
|
||||
version: ^3.0.2
|
||||
flutter_displaymode: ^0.6.0
|
||||
|
||||
dev_dependencies:
|
||||
|
|
Loading…
Reference in New Issue