Skip to content

Commit

Permalink
Merge pull request #2 from 1runeberg/fix-phone-layout
Browse files Browse the repository at this point in the history
Polish text input for both desktop & mobile versions
  • Loading branch information
1runeberg authored Aug 19, 2024
2 parents 066a275 + 9f8d0ed commit 870309d
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 22 deletions.
2 changes: 1 addition & 1 deletion confichat/lib/interfaces.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ abstract class LlmApi {
List<String> stopSequences = [];

String systemPrompt = '';
String summaryPrompt = 'Create a title or subject heading of our conversation so far in one sentence, with a maximum of 100 characters. Only use alphanumeric characters, spaces, and dashes when appropraite. Do not add a period, slash, colon, and do not add any comments, just the title/subject heading as requested.';
String summaryPrompt = 'Create a title or subject heading of our conversation so far in one sentence, with a maximum of 100 characters. Only use alphanumeric characters, spaces, and dashes when appropriate. Do not add any special characters specially a period, slash, colon, quotes, and do not add any comments, just the title/subject heading as requested.';

bool isProcessing = false;
String responseData = 'No data';
Expand Down
2 changes: 1 addition & 1 deletion confichat/lib/ui_app_bar.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class CCAppBarState extends State<CCAppBar> {
super.initState();
_switchProvider(AiProvider.ollama);
_populateModelList(true);

widget.appData.callbackSwitchProvider = _switchProvider;
}

Expand Down
122 changes: 102 additions & 20 deletions confichat/lib/ui_canvass.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:confichat/interfaces.dart';
import 'package:confichat/ui_advanced_options.dart';
import 'package:confichat/ui_save_session.dart';
import 'package:confichat/ui_widgets.dart';
import 'package:flutter/services.dart';

import 'package:provider/provider.dart';
import 'package:path_provider/path_provider.dart';
Expand Down Expand Up @@ -248,17 +249,7 @@ class CanvassState extends State<Canvass> {
onDragExited: (details) {
// todo: Provide feedback for drag and drop
},
child: TextFormField(
controller: _promptController,
focusNode: _focusNodePrompt,
decoration: InputDecoration(
labelText: deviceType == UserDeviceType.desktop ? 'Prompt (you can drag-and-drop files below)' : 'Prompt',
alignLabelWithHint: true,
),
maxLines: 5,
onFieldSubmitted: _sendPrompt,
textInputAction: TextInputAction.send, // for soft keyboard
),
child: ShiftEnterTextFormField(parentContext: context, focusNode: _focusNodePrompt, promptController: _promptController, sendFunction: _sendPrompt),
),

if( deviceType != UserDeviceType.desktop )
Expand All @@ -269,10 +260,11 @@ class CanvassState extends State<Canvass> {
labelText: 'Prompt (you can drag-and-drop files below)',
alignLabelWithHint: true,
),
minLines: 1,
maxLines: 5,
onFieldSubmitted: _sendPrompt,
textInputAction: TextInputAction.send, // for soft keyboard
),
onFieldSubmitted: _sendPromptMobile,
textInputAction: TextInputAction.newline, // for soft keyboard
),

// (2.4) User actions bar
const SizedBox(height:5),
Expand Down Expand Up @@ -571,10 +563,18 @@ class CanvassState extends State<Canvass> {
);
}

Future<void> _sendPrompt(String? s) async {
Future<void> _sendPromptMobile(String? s) async {
await _sendPrompt(s, null);
}

Future<void> _sendPrompt(String? s, FocusNode? f) async {

// Set prompt text
String promptText = _promptController.text.trim();
if(s!= null && s.isNotEmpty) { promptText = s.trim(); }

// Early exit if there's no prompt value
if( _promptController.text.trim().isEmpty ) {
if( promptText.isEmpty) {
_promptController.clear();
return;
}
Expand All @@ -594,7 +594,7 @@ class CanvassState extends State<Canvass> {
setState(() {
chatData.add({
"role": "user",
"content": _promptController.text,
"content": promptText,
"images": base64Images.isNotEmpty ? List<String>.from(base64Images) : null
});

Expand All @@ -608,12 +608,17 @@ class CanvassState extends State<Canvass> {

// ignore: use_build_context_synchronously
FocusScope.of(context).requestFocus(_focusNodePrompt);
if( f!= null ) {
FocusScope.of(context).requestFocus(f);
}

// Send prompt with history to provider
await _sendPromptWithHistory();

// Clear prompt
_promptController.clear();
// Clear prompt
setState(() {
_promptController.clear();
});

}

Expand Down Expand Up @@ -836,7 +841,7 @@ class CanvassState extends State<Canvass> {
elevation: 3.0,
hoverElevation: 3.0,
tooltip: 'Send prompt',
onPressed: () => _sendPrompt(_promptController.text),
onPressed: () => _sendPrompt(_promptController.text, null),
child: const Icon(Icons.send_sharp),
),
];
Expand Down Expand Up @@ -931,3 +936,80 @@ class DecryptDialog {
);
}
}

class ShiftEnterTextFormField extends StatefulWidget {
final BuildContext parentContext;
final FocusNode focusNode;
final TextEditingController promptController;
final Future<void> Function(String?, FocusNode?) sendFunction;
const ShiftEnterTextFormField({super.key, required this.parentContext, required this.focusNode, required this.promptController, required this.sendFunction} );

@override
ShiftEnterTextFormFieldState createState() => ShiftEnterTextFormFieldState();
}

class ShiftEnterTextFormFieldState extends State<ShiftEnterTextFormField> {
final FocusNode focusKeyEvents = FocusNode();

@override
void dispose() {
focusKeyEvents.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return KeyboardListener(
focusNode: focusKeyEvents,
onKeyEvent: (KeyEvent event) {
if (event is KeyDownEvent) {
if (HardwareKeyboard.instance.isShiftPressed && event.logicalKey == LogicalKeyboardKey.enter) {

// Insert a newline at the current cursor position
final currentText = widget.promptController.text;
final cursorPosition = widget.promptController.selection.baseOffset;
final newText = '${currentText.substring(0, cursorPosition)}\n${currentText.substring(cursorPosition)}';

setState(() {
widget.promptController.text = newText;
widget.promptController.selection = TextSelection.fromPosition(
TextPosition(offset: cursorPosition + 1),
);

});

} else if (event.logicalKey == LogicalKeyboardKey.enter) {
sendPromptKeyEvent();
}
}
},
child: TextFormField(
controller: widget.promptController,
focusNode: widget.focusNode,
decoration: const InputDecoration(
labelText: 'Prompt (you can drag-and-drop files below)',
alignLabelWithHint: true,
),
maxLines: 5,
autofocus: true,
//textInputAction: TextInputAction.send,
));
}

void sendPromptKeyEvent() async {

final String promptText = widget.promptController.text.toString(); // copy

// Clear prompt
setState(() {
widget.promptController.clear();
});

await widget.sendFunction(promptText, focusKeyEvents);

// ignore: use_build_context_synchronously
FocusScope.of(context).requestFocus(widget.focusNode);

}

}

0 comments on commit 870309d

Please sign in to comment.