Skip to content

Commit

Permalink
Merge pull request #4 from 1runeberg/stage-0.3.0
Browse files Browse the repository at this point in the history
Quality of life changes
  • Loading branch information
1runeberg authored Aug 20, 2024
2 parents 1396911 + 1a71a77 commit be9c985
Show file tree
Hide file tree
Showing 10 changed files with 331 additions and 144 deletions.
3 changes: 3 additions & 0 deletions confichat/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
analyzer:
errors:
unused_field: ignore
include: package:flutter_lints/flutter.yaml

linter:
Expand Down
5 changes: 4 additions & 1 deletion confichat/lib/app_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import 'dart:io';

import 'package:confichat/ui_widgets.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

Expand Down Expand Up @@ -42,9 +43,11 @@ class AppData {
LlmApi api = LlmApiFactory.create(AiProvider.ollama.name);
bool clearMessagesOnModelSwitch = true;
bool filterHistoryByModel = false;
bool haveUnsavedMessages = false;
int appScrollDurationInms = 100;
double windowWidth = 1024;
double windowHeight = 1024;
AiProvider defaultProvider = AiProvider.ollama;
String rootPath = '';

void defaultCallback(AiProvider? provider) {
Expand Down Expand Up @@ -161,7 +164,7 @@ class ShowErrorDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(title),
title: DialogTitle(title: title),
content: Text(content),
actions: <Widget>[
TextButton(
Expand Down
87 changes: 81 additions & 6 deletions confichat/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@
* SPDX-License-Identifier: Apache-2.0
*/

import 'dart:ui';

import 'package:confichat/ui_widgets.dart';
import 'package:flutter/material.dart';
import 'package:confichat/themes.dart';
import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart';
import 'package:path_provider/path_provider.dart';
import 'package:desktop_window/desktop_window.dart';
Expand Down Expand Up @@ -60,9 +64,33 @@ class ConfiChat extends StatelessWidget {
// Set theme
if (context.mounted) {
final themeProvider = Provider.of<ThemeProvider>(context, listen: false);
final selectedTheme = jsonContent['app']['selectedTheme'] ?? 'Light';
final selectedTheme = jsonContent['app']['selectedTheme'] ?? 'Onyx';
themeProvider.setTheme(selectedTheme);
}

// Set default provider
if(context.mounted){
final defaultProvider = jsonContent['app']['selectedDefaultProvider'] ?? 'Ollama';

AiProvider selectedProvider;
switch (defaultProvider.toLowerCase()) {
case 'ollama':
selectedProvider = AiProvider.ollama;
break;
case 'openai':
selectedProvider = AiProvider.openai;
break;
case 'llamacpp':
selectedProvider = AiProvider.llamacpp;
break;
default:
selectedProvider = AiProvider.ollama; // Fallback to Ollama if the string doesn't match
break;
}

AppData.instance.defaultProvider = selectedProvider;
}

}
}
}
Expand Down Expand Up @@ -117,21 +145,68 @@ class HomePage extends StatefulWidget {
State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
class _HomePageState extends State<HomePage> {
final ChatSessionSelectedNotifier chatSessionSelectedNotifier = ChatSessionSelectedNotifier();
TextEditingController providerController = TextEditingController();
AiProvider? selectedProvider;

TextEditingController providerModel = TextEditingController();
ModelItem? selectedModel;

late final AppLifecycleListener _lifecycleListener;
late AppLifecycleState? _lifecycleState;

@override
void initState() {
super.initState();
super.initState();

if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
DesktopWindow.setWindowSize(Size(widget.appData.windowWidth, widget.appData.windowHeight));
}
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
DesktopWindow.setWindowSize(Size(widget.appData.windowWidth, widget.appData.windowHeight));
}

_lifecycleState = SchedulerBinding.instance.lifecycleState;
_lifecycleListener = AppLifecycleListener(
onExitRequested: _checkForUnsavedChat
);
}

Future<AppExitResponse> _checkForUnsavedChat() async {

// If there are no unsaved changes, proceed to exit
if(!widget.appData.haveUnsavedMessages) { return AppExitResponse.exit;}

bool shouldExit = false;
await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const DialogTitle(title: 'Warning', isError: true),
content: Text(
'There are unsaved messages in the current chat window - they will be lost. Proceed?',
style: Theme.of(context).textTheme.bodyLarge,
),
actions: [
ElevatedButton(
child: const Text('Yes'),
onPressed: () {
shouldExit = true;
Navigator.of(context).pop();
},
),
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
shouldExit = false;
Navigator.of(context).pop();
},
),
],
);
},
);

return shouldExit ? AppExitResponse.exit : AppExitResponse.cancel;

}

@override
Expand Down
4 changes: 2 additions & 2 deletions confichat/lib/ui_advanced_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -274,8 +274,8 @@ class AdvancedOptionsState extends State<AdvancedOptions> {
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Unsaved changes'),
content: const Text('You have unsaved changes to the advanced options. Are you sure you want to exit?'),
title: const DialogTitle(title: 'Unsaved changes', isError: true),
content: Text('You have unsaved changes to the advanced options. Are you sure you want to exit?', style: Theme.of(context).textTheme.bodyLarge,),
actions: [
ElevatedButton(
onPressed: () {
Expand Down
32 changes: 20 additions & 12 deletions confichat/lib/ui_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class CCAppBarState extends State<CCAppBar> {
@override
void initState() {
super.initState();
_switchProvider(AiProvider.ollama);
_switchProvider(widget.appData.defaultProvider);
_populateModelList(true);

widget.appData.callbackSwitchProvider = _switchProvider;
Expand Down Expand Up @@ -78,7 +78,7 @@ class CCAppBarState extends State<CCAppBar> {
},
),
actions: [
_buildModelProviderDropdown(context, isPhone),
_buildModelProviderDropdown(context, isPhone, selectedProvider ?? AiProvider.ollama),
_buildModelDropdown(context, isPhone),
if (!isPhone) _buildConfigButton(context),
if (!isPhone) _buildAddButton(context),
Expand All @@ -88,11 +88,11 @@ class CCAppBarState extends State<CCAppBar> {
);
}

Widget _buildModelProviderDropdown(BuildContext context, bool isPhone) {
Widget _buildModelProviderDropdown(BuildContext context, bool isPhone, AiProvider selectedProvider) {
return Container(
margin: const EdgeInsets.all(10),
child: DropdownMenu<AiProvider>(
initialSelection: AiProvider.ollama,
initialSelection: selectedProvider,
controller: widget.providerController,
requestFocusOnTap: true,
textStyle: TextStyle(
Expand Down Expand Up @@ -292,26 +292,35 @@ class CCAppBarState extends State<CCAppBar> {
}
}

void _showModelChangeWarning(BuildContext context, ModelItem newModel) {
showDialog(
Future<void> _showModelChangeWarning(BuildContext context, ModelItem newModel) async {

if(!widget.appData.haveUnsavedMessages) {
_setModelItem(newModel);
return;
}

await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Warning'),
content: const Text(
'Any messages in the current chat window will be lost. Proceed?',
title: const DialogTitle(title: 'Warning', isError: true),
content: Text(
'There are unsaved messages in the current chat window - they will be lost. Proceed?',
style: Theme.of(context).textTheme.bodyLarge,
),
actions: <Widget>[
actions: [
ElevatedButton(
child: const Text('Yes'),
onPressed: () {
widget.appData.haveUnsavedMessages = false;
_setModelItem(newModel);
Navigator.of(context).pop();
},
),
ElevatedButton(
child: const Text('Cancel'),
onPressed: () {
if(mounted && selectedModel != null) {_setModelItem(selectedModel!);}
Navigator.of(context).pop();
},
),
Expand Down Expand Up @@ -339,9 +348,8 @@ class CCAppBarState extends State<CCAppBar> {
if(mounted) {
setState(() {
selectedProvider = provider;
_populateModelList(true);
});

_populateModelList(true);
}
}

Expand Down
Loading

0 comments on commit be9c985

Please sign in to comment.