diff --git a/assets/OllamaApp.zip b/assets/OllamaApp.zip index ba831ce..7662d64 100644 Binary files a/assets/OllamaApp.zip and b/assets/OllamaApp.zip differ diff --git a/assets/screenshots/img1.png b/assets/screenshots/img1.png index 4f78fdc..6a836a6 100644 Binary files a/assets/screenshots/img1.png and b/assets/screenshots/img1.png differ diff --git a/assets/screenshots/img1_framed.png b/assets/screenshots/img1_framed.png index f0f4ed2..eb1d872 100644 Binary files a/assets/screenshots/img1_framed.png and b/assets/screenshots/img1_framed.png differ diff --git a/assets/screenshots/img2.png b/assets/screenshots/img2.png index 4247482..ac33f52 100644 Binary files a/assets/screenshots/img2.png and b/assets/screenshots/img2.png differ diff --git a/assets/screenshots/img2_framed.png b/assets/screenshots/img2_framed.png index 374cc73..5004db7 100644 Binary files a/assets/screenshots/img2_framed.png and b/assets/screenshots/img2_framed.png differ diff --git a/assets/screenshots/img3.png b/assets/screenshots/img3.png index fdb9675..6832dba 100644 Binary files a/assets/screenshots/img3.png and b/assets/screenshots/img3.png differ diff --git a/assets/screenshots/img3_framed.png b/assets/screenshots/img3_framed.png index bdfd215..ef810b0 100644 Binary files a/assets/screenshots/img3_framed.png and b/assets/screenshots/img3_framed.png differ diff --git a/assets/screenshots/img4.png b/assets/screenshots/img4.png index 5c17108..04997d4 100644 Binary files a/assets/screenshots/img4.png and b/assets/screenshots/img4.png differ diff --git a/assets/screenshots/img4_framed.png b/assets/screenshots/img4_framed.png index a2f9574..623ee84 100644 Binary files a/assets/screenshots/img4_framed.png and b/assets/screenshots/img4_framed.png differ diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 89ef88e..0b17e65 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -1,73 +1,3 @@ { - "@@locale": "de", - "appTitle": "Ollama", - "@appTitle": { - "description": "Title of the application", - "context": "Visible in the side bar" - }, - "optionSettings": "Einstellungen", - "@optionSettings": { - "description": "Text displayed for settings option", - "context": "Visible in the side bar" - }, - "optionNewChat": "Neuer Chat", - "@optionNewChat": { - "description": "Text displayed for new chat option", - "context": "Visible in the side bar" - }, - "takeImage": "Bild Aufnehmen", - "@takeImage": { - "description": "Text displayed for take image button", - "context": "Visible in attachment menu" - }, - "uploadImage": "Bild Hochladen", - "@uploadImage": { - "description": "Text displayed for image upload button", - "context": "Visible in attachment menu" - }, - "messageInputPlaceholder": "Nachricht", - "@messageInputPlaceholder": { - "description": "Placeholder text for message input", - "context": "Visible in the chat view" - }, - "noModelSelected": "Kein Modell ausgewählt", - "@noModelSelected": { - "description": "Text displayed when no model is selected", - "context": "Visible in the chat view" - }, - "hostDialogTitle": "Host Festlegen", - "@hostDialogTitle": { - "description": "Title of the host dialog", - "context": "Visible in the host dialog" - }, - "hostDialogDescription": "Gebe den Host des Ollama-Servers ein. Dies wird validiert und kann später in den Einstellungen geändert werden.", - "@hostDialogDescription": { - "description": "Description of the host dialog", - "context": "Visible in the host dialog" - }, - "hostDialogErrorInvalidHost": "Der Host konnte nicht validiert werden, bitte versuche es erneut. Entweder ist er nicht erreichbar oder es handelt sich nicht um eine gültige Ollama-Serverinstanz.", - "@hostDialogErrorInvalidHost": { - "description": "Error message displayed when the host is invalid", - "context": "Visible in the host dialog" - }, - "hostDialogErrorInvalidUrl": "Die URL ist ungültig. Versuche, sie erneut zu überprüfen.", - "@hostDialogErrorInvalidUrl": { - "description": "Error message displayed when the URL is invalid", - "context": "Visible in the host dialog" - }, - "hostDialogSave": "Host Speichern", - "@hostDialogSave": { - "description": "Text displayed for save host button, should be capitalized", - "context": "Visible in the host dialog" - }, - "noSelectedModel": "", - "@noSelectedModel": { - "description": "Text displayed when no model is selected", - "context": "Visible in the chat view, opens the model dialog when clicked" - }, - "modelDialogAddModel": "Modell hinzufügen", - "@modelDialogAddModel": { - "description": "Text displayed for add model button", - "context": "Visible in the model dialog" - } + "@@locale": "de" } \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index fed12dd..ba89656 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -69,5 +69,10 @@ "@modelDialogAddModel": { "description": "Text displayed for add model button", "context": "Visible in the model dialog" + }, + "modelDialogAddSteps": "To add a new model, visit ollama.com on your computer, look for a model you like, copy the command and paste it in a new terminal window. Wait for the download to complete, this can take a while. Once it is complete, reopen this selector and you'll find your newly added model.", + "@modelDialogAddSteps": { + "description": "Steps to add a new model", + "context": "Visible in the model dialog" } } \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 1ef40b5..c760ab6 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -391,7 +391,7 @@ class _MainAppState extends State { .selectionClick(); }, icon: const Icon( - Icons.file_copy_rounded), + Icons.photo_camera_rounded), label: Text(AppLocalizations.of( context)! .takeImage))), diff --git a/lib/worker_setter.dart b/lib/worker_setter.dart index 6561cc1..7e2111b 100644 --- a/lib/worker_setter.dart +++ b/lib/worker_setter.dart @@ -8,7 +8,7 @@ import 'package:http/http.dart' as http; import 'package:dartx/dartx.dart'; import 'package:ollama_dart/ollama_dart.dart' as llama; -void setHost(BuildContext context, [String host = ""]) { +void setHost(BuildContext context) { bool loading = false; bool invalidHost = false; bool invalidUrl = false; @@ -93,7 +93,7 @@ void setHost(BuildContext context, [String host = ""]) { messages = []; setState(() {}); host = tmpHost; - prefs?.setString("host", host); + prefs?.setString("host", host!); } }, child: @@ -105,6 +105,7 @@ void setModel(BuildContext context, Function setState) { List models = []; List modal = []; int usedIndex = -1; + int addIndex = -1; bool loaded = false; Function? setModalState; void load() async { @@ -113,6 +114,10 @@ void setModel(BuildContext context, Function setState) { models.add(list.models![i].model!.split(":")[0]); modal.add((list.models![i].details!.families ?? []).contains("clip")); } + addIndex = models.length; + // ignore: use_build_context_synchronously + models.add(AppLocalizations.of(context)!.modelDialogAddModel); + modal.add(false); for (var i = 0; i < models.length; i++) { if (models[i] == model) { usedIndex = i; @@ -147,55 +152,53 @@ void setModel(BuildContext context, Function setState) { prefs?.setBool("multimodal", multimodal); setState(() {}); }, - child: SizedBox( + child: Container( width: double.infinity, + padding: const EdgeInsets.only(left: 16, right: 16, top: 16), child: (!loaded) - ? const Padding( - padding: EdgeInsets.all(16), - child: LinearProgressIndicator()) + ? const LinearProgressIndicator() : Column(mainAxisSize: MainAxisSize.min, children: [ - Padding( - padding: const EdgeInsets.only( - left: 16, right: 16, top: 16), - child: SizedBox( - width: double.infinity, - child: OutlinedButton.icon( - onPressed: () {}, - label: Text(AppLocalizations.of(context)! - .modelDialogAddModel), - icon: const Icon(Icons.add_rounded)))), - const Divider(), - Padding( - padding: - const EdgeInsets.only(left: 16, right: 16), - child: Container( - width: double.infinity, - constraints: BoxConstraints( - maxHeight: - MediaQuery.of(context).size.height * - 0.4), - child: SingleChildScrollView( - scrollDirection: Axis.vertical, - child: Wrap( - spacing: 5.0, - alignment: WrapAlignment.center, - children: List.generate( - models.length, - (int index) { - return ChoiceChip( - label: Text(models[index]), - selected: usedIndex == index, - avatar: (recommendedModels + Container( + width: double.infinity, + constraints: BoxConstraints( + maxHeight: + MediaQuery.of(context).size.height * 0.4), + child: SingleChildScrollView( + scrollDirection: Axis.vertical, + child: Wrap( + spacing: 5.0, + alignment: WrapAlignment.center, + children: List.generate( + models.length, + (int index) { + return ChoiceChip( + label: Text(models[index]), + selected: usedIndex == index, + avatar: (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) + : 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()) @@ -205,45 +208,44 @@ void setModel(BuildContext context, Function setState) { 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 (!chatAllowed) return; - setLocalState(() { - usedIndex = - selected ? index : -1; - }); - }, - ); + : 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(); + showModalBottomSheet( + context: context, + builder: (context) { + return Padding( + padding: + const EdgeInsets + .only( + left: 16, + right: 16, + top: 16), + child: Text( + AppLocalizations.of( + context)! + .modelDialogAddSteps)); + }); + } + if (!chatAllowed) return; + setLocalState(() { + usedIndex = selected ? index : -1; + }); }, - ).toList(), - )))) + ); + }, + ).toList(), + ))) ]))); }); });