desktop improvements, no error on empty responses, no redirect in host
This commit is contained in:
parent
fcb20e7931
commit
edcbf74295
|
@ -50,6 +50,16 @@
|
||||||
"description": "Fifth tip displayed in the sidebar",
|
"description": "Fifth tip displayed in the sidebar",
|
||||||
"context": "Visible in the sidebar"
|
"context": "Visible in the sidebar"
|
||||||
},
|
},
|
||||||
|
"deleteChat": "Delete",
|
||||||
|
"@deleteChat": {
|
||||||
|
"description": "Text displayed for delete chat option",
|
||||||
|
"context": "Visible in the chat view, desktop only"
|
||||||
|
},
|
||||||
|
"renameChat": "Rename",
|
||||||
|
"@renameChat": {
|
||||||
|
"description": "Text displayed for rename chat option",
|
||||||
|
"context": "Visible in the chat view, desktop only"
|
||||||
|
},
|
||||||
"takeImage": "Take Image",
|
"takeImage": "Take Image",
|
||||||
"@takeImage": {
|
"@takeImage": {
|
||||||
"description": "Text displayed for take image button",
|
"description": "Text displayed for take image button",
|
||||||
|
|
511
lib/main.dart
511
lib/main.dart
|
@ -39,7 +39,8 @@ import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
// use host or not, if false dialog is shown
|
// use host or not, if false dialog is shown
|
||||||
const useHost = false;
|
const useHost = false;
|
||||||
// host of ollama, must be accessible from the client, without trailing slash, will always be accepted as valid
|
// host of ollama, must be accessible from the client, without trailing slash
|
||||||
|
// ! will always be accepted as valid, even if [useHost] is false
|
||||||
const fixedHost = "http://example.com:11434";
|
const fixedHost = "http://example.com:11434";
|
||||||
// use model or not, if false selector is shown
|
// use model or not, if false selector is shown
|
||||||
const useModel = false;
|
const useModel = false;
|
||||||
|
@ -66,6 +67,7 @@ bool multimodal = false;
|
||||||
List<types.Message> messages = [];
|
List<types.Message> messages = [];
|
||||||
String? chatUuid;
|
String? chatUuid;
|
||||||
bool chatAllowed = true;
|
bool chatAllowed = true;
|
||||||
|
String hoveredChat = "";
|
||||||
|
|
||||||
final user = types.User(id: const Uuid().v4());
|
final user = types.User(id: const Uuid().v4());
|
||||||
final assistant = types.User(id: const Uuid().v4());
|
final assistant = types.User(id: const Uuid().v4());
|
||||||
|
@ -283,9 +285,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
),
|
),
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
]))))),
|
]))))),
|
||||||
desktopLayoutNotRequired(context)
|
(desktopLayoutNotRequired(context) ||
|
||||||
? const SizedBox.shrink()
|
(!allowMultipleChats && !allowSettings))
|
||||||
: (!allowMultipleChats && !allowSettings)
|
|
||||||
? const SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Divider(
|
: Divider(
|
||||||
color: desktopLayout(context)
|
color: desktopLayout(context)
|
||||||
|
@ -360,7 +361,10 @@ class _MainAppState extends State<MainApp> {
|
||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
])))))
|
])))))
|
||||||
: const SizedBox.shrink(),
|
: const SizedBox.shrink(),
|
||||||
Divider(
|
(desktopLayoutNotRequired(context) &&
|
||||||
|
(!allowMultipleChats && !allowSettings))
|
||||||
|
? const SizedBox.shrink()
|
||||||
|
: Divider(
|
||||||
color: desktopLayout(context)
|
color: desktopLayout(context)
|
||||||
? Theme.of(context).colorScheme.onSurface.withAlpha(20)
|
? Theme.of(context).colorScheme.onSurface.withAlpha(20)
|
||||||
: null),
|
: null),
|
||||||
|
@ -444,7 +448,394 @@ class _MainAppState extends State<MainApp> {
|
||||||
}),
|
}),
|
||||||
])
|
])
|
||||||
..addAll((prefs?.getStringList("chats") ?? []).map((item) {
|
..addAll((prefs?.getStringList("chats") ?? []).map((item) {
|
||||||
return Dismissible(
|
var child = Padding(
|
||||||
|
padding: padding,
|
||||||
|
child: InkWell(
|
||||||
|
customBorder: const RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.all(Radius.circular(50))),
|
||||||
|
onTap: () {
|
||||||
|
selectionHaptic();
|
||||||
|
if (!desktopLayoutRequired(context)) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
if (!chatAllowed) return;
|
||||||
|
if (chatUuid == jsonDecode(item)["uuid"]) return;
|
||||||
|
loadChat(jsonDecode(item)["uuid"], setState);
|
||||||
|
chatUuid = jsonDecode(item)["uuid"];
|
||||||
|
},
|
||||||
|
onHover: (value) {
|
||||||
|
setState(() {
|
||||||
|
if (value) {
|
||||||
|
hoveredChat = jsonDecode(item)["uuid"];
|
||||||
|
} else {
|
||||||
|
hoveredChat = "";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onLongPress: desktopFeature()
|
||||||
|
? null
|
||||||
|
: () async {
|
||||||
|
selectionHaptic();
|
||||||
|
if (!chatAllowed) return;
|
||||||
|
if (!allowSettings) return;
|
||||||
|
String oldTitle = jsonDecode(item)["title"];
|
||||||
|
var newTitle = await prompt(context,
|
||||||
|
title: AppLocalizations.of(context)!
|
||||||
|
.dialogEnterNewTitle,
|
||||||
|
value: oldTitle,
|
||||||
|
uuid: jsonDecode(item)["uuid"]);
|
||||||
|
var tmp = (prefs!.getStringList("chats") ?? []);
|
||||||
|
for (var i = 0; i < tmp.length; i++) {
|
||||||
|
if (jsonDecode((prefs!.getStringList("chats") ??
|
||||||
|
[])[i])["uuid"] ==
|
||||||
|
jsonDecode(item)["uuid"]) {
|
||||||
|
var tmp2 = jsonDecode(tmp[i]);
|
||||||
|
tmp2["title"] = newTitle;
|
||||||
|
tmp[i] = jsonEncode(tmp2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefs!.setStringList("chats", tmp);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 16, bottom: 16),
|
||||||
|
child: Row(children: [
|
||||||
|
allowMultipleChats
|
||||||
|
? Padding(
|
||||||
|
padding:
|
||||||
|
const EdgeInsets.only(left: 16, right: 16),
|
||||||
|
child: Icon((chatUuid == jsonDecode(item)["uuid"])
|
||||||
|
? Icons.location_on_rounded
|
||||||
|
: Icons.restore_rounded))
|
||||||
|
: const SizedBox(width: 16),
|
||||||
|
Expanded(
|
||||||
|
child: Text(jsonDecode(item)["title"],
|
||||||
|
softWrap: false,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.fade,
|
||||||
|
style:
|
||||||
|
const TextStyle(fontWeight: FontWeight.w500)),
|
||||||
|
),
|
||||||
|
AnimatedSwitcher(
|
||||||
|
duration: const Duration(milliseconds: 100),
|
||||||
|
child:
|
||||||
|
((desktopFeature() &&
|
||||||
|
(hoveredChat ==
|
||||||
|
jsonDecode(item)["uuid"])) ||
|
||||||
|
!allowMultipleChats)
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16, right: 16),
|
||||||
|
child: SizedBox(
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
child: IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (!allowMultipleChats) {
|
||||||
|
for (var i = 0;
|
||||||
|
i <
|
||||||
|
(prefs!.getStringList(
|
||||||
|
"chats") ??
|
||||||
|
[])
|
||||||
|
.length;
|
||||||
|
i++) {
|
||||||
|
if (jsonDecode((prefs!
|
||||||
|
.getStringList(
|
||||||
|
"chats") ??
|
||||||
|
[])[i])["uuid"] ==
|
||||||
|
jsonDecode(item)["uuid"]) {
|
||||||
|
List<String> tmp = prefs!
|
||||||
|
.getStringList("chats")!;
|
||||||
|
tmp.removeAt(i);
|
||||||
|
prefs!.setStringList(
|
||||||
|
"chats", tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
messages = [];
|
||||||
|
chatUuid = null;
|
||||||
|
if (!desktopLayoutRequired(
|
||||||
|
context)) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!allowSettings) {
|
||||||
|
if (prefs!.getBool(
|
||||||
|
"askBeforeDeletion") ??
|
||||||
|
false) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(
|
||||||
|
builder: (context,
|
||||||
|
setLocalState) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.deleteDialogTitle),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize
|
||||||
|
.min,
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.deleteDialogDescription),
|
||||||
|
]),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed:
|
||||||
|
() {
|
||||||
|
Navigator.of(
|
||||||
|
context)
|
||||||
|
.pop();
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.deleteDialogCancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed:
|
||||||
|
() {
|
||||||
|
Navigator.of(
|
||||||
|
context)
|
||||||
|
.pop();
|
||||||
|
for (var i =
|
||||||
|
0;
|
||||||
|
i < (prefs!.getStringList("chats") ?? []).length;
|
||||||
|
i++) {
|
||||||
|
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])[
|
||||||
|
"uuid"] ==
|
||||||
|
jsonDecode(
|
||||||
|
item)["uuid"]) {
|
||||||
|
List<String>
|
||||||
|
tmp =
|
||||||
|
prefs!.getStringList("chats")!;
|
||||||
|
tmp.removeAt(
|
||||||
|
i);
|
||||||
|
prefs!.setStringList(
|
||||||
|
"chats",
|
||||||
|
tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chatUuid ==
|
||||||
|
jsonDecode(
|
||||||
|
item)["uuid"]) {
|
||||||
|
messages =
|
||||||
|
[];
|
||||||
|
chatUuid =
|
||||||
|
null;
|
||||||
|
if (!desktopLayoutRequired(
|
||||||
|
context)) {
|
||||||
|
Navigator.of(context)
|
||||||
|
.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(
|
||||||
|
() {});
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
|
.deleteDialogDelete))
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (var i = 0;
|
||||||
|
i <
|
||||||
|
(prefs!.getStringList(
|
||||||
|
"chats") ??
|
||||||
|
[])
|
||||||
|
.length;
|
||||||
|
i++) {
|
||||||
|
if (jsonDecode((prefs!
|
||||||
|
.getStringList(
|
||||||
|
"chats") ??
|
||||||
|
[])[i])["uuid"] ==
|
||||||
|
jsonDecode(
|
||||||
|
item)["uuid"]) {
|
||||||
|
List<String> tmp = prefs!
|
||||||
|
.getStringList(
|
||||||
|
"chats")!;
|
||||||
|
tmp.removeAt(i);
|
||||||
|
prefs!.setStringList(
|
||||||
|
"chats", tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chatUuid ==
|
||||||
|
jsonDecode(item)["uuid"]) {
|
||||||
|
messages = [];
|
||||||
|
chatUuid = null;
|
||||||
|
if (!desktopLayoutRequired(
|
||||||
|
context)) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!chatAllowed) return;
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return Dialog(
|
||||||
|
alignment:
|
||||||
|
Alignment.bottomLeft,
|
||||||
|
insetPadding:
|
||||||
|
const EdgeInsets.only(
|
||||||
|
left: 12,
|
||||||
|
bottom: 12),
|
||||||
|
child: Container(
|
||||||
|
width: 100,
|
||||||
|
padding:
|
||||||
|
const EdgeInsets
|
||||||
|
.only(
|
||||||
|
left: 16,
|
||||||
|
right: 16,
|
||||||
|
top: 16),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize:
|
||||||
|
MainAxisSize
|
||||||
|
.min,
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: double
|
||||||
|
.infinity,
|
||||||
|
child: OutlinedButton
|
||||||
|
.icon(
|
||||||
|
onPressed:
|
||||||
|
() {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
if (prefs!.getBool("askBeforeDeletion") ??
|
||||||
|
false) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return StatefulBuilder(builder: (context, setLocalState) {
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text(AppLocalizations.of(context)!.deleteDialogTitle),
|
||||||
|
content: Column(mainAxisSize: MainAxisSize.min, children: [
|
||||||
|
Text(AppLocalizations.of(context)!.deleteDialogDescription),
|
||||||
|
]),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(context)!.deleteDialogCancel)),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
for (var i = 0; i < (prefs!.getStringList("chats") ?? []).length; i++) {
|
||||||
|
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] == jsonDecode(item)["uuid"]) {
|
||||||
|
List<String> tmp = prefs!.getStringList("chats")!;
|
||||||
|
tmp.removeAt(i);
|
||||||
|
prefs!.setStringList("chats", tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chatUuid == jsonDecode(item)["uuid"]) {
|
||||||
|
messages = [];
|
||||||
|
chatUuid = null;
|
||||||
|
if (!desktopLayoutRequired(context)) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
child: Text(AppLocalizations.of(context)!.deleteDialogDelete))
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < (prefs!.getStringList("chats") ?? []).length; i++) {
|
||||||
|
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] == jsonDecode(item)["uuid"]) {
|
||||||
|
List<String> tmp = prefs!.getStringList("chats")!;
|
||||||
|
tmp.removeAt(i);
|
||||||
|
prefs!.setStringList("chats", tmp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chatUuid == jsonDecode(item)["uuid"]) {
|
||||||
|
messages = [];
|
||||||
|
chatUuid = null;
|
||||||
|
if (!desktopLayoutRequired(context)) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons
|
||||||
|
.delete_forever_rounded),
|
||||||
|
label:
|
||||||
|
Text(AppLocalizations.of(context)!.deleteChat))),
|
||||||
|
const SizedBox(
|
||||||
|
height: 8),
|
||||||
|
SizedBox(
|
||||||
|
width: double
|
||||||
|
.infinity,
|
||||||
|
child: OutlinedButton
|
||||||
|
.icon(
|
||||||
|
onPressed:
|
||||||
|
() async {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
String
|
||||||
|
oldTitle =
|
||||||
|
jsonDecode(item)["title"];
|
||||||
|
var newTitle = await prompt(context,
|
||||||
|
title: AppLocalizations.of(context)!.dialogEnterNewTitle,
|
||||||
|
value: oldTitle,
|
||||||
|
uuid: jsonDecode(item)["uuid"]);
|
||||||
|
var tmp =
|
||||||
|
(prefs!.getStringList("chats") ?? []);
|
||||||
|
for (var i = 0;
|
||||||
|
i < tmp.length;
|
||||||
|
i++) {
|
||||||
|
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] == jsonDecode(item)["uuid"]) {
|
||||||
|
var tmp2 = jsonDecode(tmp[i]);
|
||||||
|
tmp2["title"] = newTitle;
|
||||||
|
tmp[i] = jsonEncode(tmp2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
prefs!.setStringList("chats",
|
||||||
|
tmp);
|
||||||
|
setState(() {});
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons
|
||||||
|
.edit_rounded),
|
||||||
|
label:
|
||||||
|
Text(AppLocalizations.of(context)!.renameChat))),
|
||||||
|
const SizedBox(
|
||||||
|
height: 16)
|
||||||
|
])),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
hoverColor: Colors.transparent,
|
||||||
|
highlightColor: Colors.transparent,
|
||||||
|
icon: Transform.translate(
|
||||||
|
offset: const Offset(-8, -8),
|
||||||
|
child: const Icon(allowMultipleChats
|
||||||
|
? allowSettings
|
||||||
|
? Icons.more_horiz_rounded
|
||||||
|
: Icons.close_rounded
|
||||||
|
: Icons.restart_alt_rounded),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
))
|
||||||
|
: const SizedBox(width: 16)),
|
||||||
|
]))));
|
||||||
|
return desktopFeature() || !allowMultipleChats
|
||||||
|
? child
|
||||||
|
: Dismissible(
|
||||||
key: Key(jsonDecode(item)["uuid"]),
|
key: Key(jsonDecode(item)["uuid"]),
|
||||||
direction: (chatAllowed)
|
direction: (chatAllowed)
|
||||||
? DismissDirection.startToEnd
|
? DismissDirection.startToEnd
|
||||||
|
@ -457,7 +848,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
await showDialog(
|
await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return StatefulBuilder(builder: (context, setLocalState) {
|
return StatefulBuilder(
|
||||||
|
builder: (context, setLocalState) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(AppLocalizations.of(context)!
|
title: Text(AppLocalizations.of(context)!
|
||||||
.deleteDialogTitle),
|
.deleteDialogTitle),
|
||||||
|
@ -515,64 +907,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
}
|
}
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
child: Padding(
|
child: child);
|
||||||
padding: padding,
|
|
||||||
child: InkWell(
|
|
||||||
customBorder: const RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.all(Radius.circular(50))),
|
|
||||||
onTap: () {
|
|
||||||
selectionHaptic();
|
|
||||||
if (!desktopLayoutRequired(context)) {
|
|
||||||
Navigator.of(context).pop();
|
|
||||||
}
|
|
||||||
if (!chatAllowed) return;
|
|
||||||
if (chatUuid == jsonDecode(item)["uuid"]) return;
|
|
||||||
loadChat(jsonDecode(item)["uuid"], setState);
|
|
||||||
chatUuid = jsonDecode(item)["uuid"];
|
|
||||||
},
|
|
||||||
onLongPress: () async {
|
|
||||||
selectionHaptic();
|
|
||||||
if (!chatAllowed) return;
|
|
||||||
if (!allowSettings) return;
|
|
||||||
String oldTitle = jsonDecode(item)["title"];
|
|
||||||
var newTitle = await prompt(context,
|
|
||||||
title:
|
|
||||||
AppLocalizations.of(context)!.dialogEnterNewTitle,
|
|
||||||
value: oldTitle,
|
|
||||||
uuid: jsonDecode(item)["uuid"]);
|
|
||||||
var tmp = (prefs!.getStringList("chats") ?? []);
|
|
||||||
for (var i = 0; i < tmp.length; i++) {
|
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ??
|
|
||||||
[])[i])["uuid"] ==
|
|
||||||
jsonDecode(item)["uuid"]) {
|
|
||||||
var tmp2 = jsonDecode(tmp[i]);
|
|
||||||
tmp2["title"] = newTitle;
|
|
||||||
tmp[i] = jsonEncode(tmp2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
prefs!.setStringList("chats", tmp);
|
|
||||||
setState(() {});
|
|
||||||
},
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 16, bottom: 16),
|
|
||||||
child: Row(children: [
|
|
||||||
Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.only(left: 16, right: 16),
|
|
||||||
child: Icon((chatUuid == jsonDecode(item)["uuid"])
|
|
||||||
? Icons.location_on_rounded
|
|
||||||
: Icons.restore_rounded)),
|
|
||||||
Expanded(
|
|
||||||
child: Text(jsonDecode(item)["title"],
|
|
||||||
softWrap: false,
|
|
||||||
maxLines: 1,
|
|
||||||
overflow: TextOverflow.fade,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.w500)),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
])))));
|
|
||||||
}).toList());
|
}).toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -610,7 +945,9 @@ class _MainAppState extends State<MainApp> {
|
||||||
padding: EdgeInsets.all(16),
|
padding: EdgeInsets.all(16),
|
||||||
child: Text(
|
child: Text(
|
||||||
"*Build Error:*\n\nuseHost: $useHost\nallowSettings: $allowSettings\n\nYou created this build? One of them must be set to true or the app is not functional!\n\nYou received this build by someone else? Please contact them and report the issue.",
|
"*Build Error:*\n\nuseHost: $useHost\nallowSettings: $allowSettings\n\nYou created this build? One of them must be set to true or the app is not functional!\n\nYou received this build by someone else? Please contact them and report the issue.",
|
||||||
style: TextStyle(color: Colors.red)))));
|
style: TextStyle(
|
||||||
|
color: Colors.red,
|
||||||
|
fontFamily: "monospace")))));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -726,7 +1063,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
: [Expanded(child: selector)]),
|
: [Expanded(child: selector)]),
|
||||||
actions: desktopControlsActions(context, [
|
actions: desktopControlsActions(context, [
|
||||||
const SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
IconButton(
|
allowMultipleChats
|
||||||
|
? IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
if (!chatAllowed) return;
|
if (!chatAllowed) return;
|
||||||
|
@ -740,7 +1078,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
return StatefulBuilder(
|
return StatefulBuilder(
|
||||||
builder: (context, setLocalState) {
|
builder: (context, setLocalState) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(AppLocalizations.of(context)!
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
.deleteDialogTitle),
|
.deleteDialogTitle),
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
@ -775,7 +1114,8 @@ class _MainAppState extends State<MainApp> {
|
||||||
[])[i])["uuid"] ==
|
[])[i])["uuid"] ==
|
||||||
chatUuid) {
|
chatUuid) {
|
||||||
List<String> tmp = prefs!
|
List<String> tmp = prefs!
|
||||||
.getStringList("chats")!;
|
.getStringList(
|
||||||
|
"chats")!;
|
||||||
tmp.removeAt(i);
|
tmp.removeAt(i);
|
||||||
prefs!.setStringList(
|
prefs!.setStringList(
|
||||||
"chats", tmp);
|
"chats", tmp);
|
||||||
|
@ -794,12 +1134,15 @@ class _MainAppState extends State<MainApp> {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0;
|
for (var i = 0;
|
||||||
i < (prefs!.getStringList("chats") ?? []).length;
|
i <
|
||||||
|
(prefs!.getStringList("chats") ?? [])
|
||||||
|
.length;
|
||||||
i++) {
|
i++) {
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ??
|
if (jsonDecode((prefs!.getStringList("chats") ??
|
||||||
[])[i])["uuid"] ==
|
[])[i])["uuid"] ==
|
||||||
chatUuid) {
|
chatUuid) {
|
||||||
List<String> tmp = prefs!.getStringList("chats")!;
|
List<String> tmp =
|
||||||
|
prefs!.getStringList("chats")!;
|
||||||
tmp.removeAt(i);
|
tmp.removeAt(i);
|
||||||
prefs!.setStringList("chats", tmp);
|
prefs!.setStringList("chats", tmp);
|
||||||
break;
|
break;
|
||||||
|
@ -811,6 +1154,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
setState(() {});
|
setState(() {});
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.restart_alt_rounded))
|
icon: const Icon(Icons.restart_alt_rounded))
|
||||||
|
: const SizedBox.shrink()
|
||||||
]),
|
]),
|
||||||
bottom: PreferredSize(
|
bottom: PreferredSize(
|
||||||
preferredSize: const Size.fromHeight(1),
|
preferredSize: const Size.fromHeight(1),
|
||||||
|
@ -875,6 +1219,13 @@ class _MainAppState extends State<MainApp> {
|
||||||
{required messageWidth, required showName}) {
|
{required messageWidth, required showName}) {
|
||||||
var white =
|
var white =
|
||||||
const TextStyle(color: Colors.white);
|
const TextStyle(color: Colors.white);
|
||||||
|
bool greyed = false;
|
||||||
|
String text = p0.text;
|
||||||
|
if (text.trim() == "") {
|
||||||
|
text =
|
||||||
|
"_Empty AI response, try restarting conversation_"; // Warning icon: U+26A0
|
||||||
|
greyed = true;
|
||||||
|
}
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
left: 20,
|
left: 20,
|
||||||
|
@ -889,7 +1240,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
WidgetStatePropertyAll(
|
WidgetStatePropertyAll(
|
||||||
Colors.grey))),
|
Colors.grey))),
|
||||||
child: MarkdownBody(
|
child: MarkdownBody(
|
||||||
data: p0.text,
|
data: text,
|
||||||
onTapLink: (text, href, title) async {
|
onTapLink: (text, href, title) async {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
try {
|
try {
|
||||||
|
@ -1020,8 +1371,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
Colors.white),
|
Colors.white),
|
||||||
codeblockDecoration: BoxDecoration(
|
codeblockDecoration: BoxDecoration(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
borderRadius:
|
borderRadius: BorderRadius.circular(
|
||||||
BorderRadius.circular(
|
|
||||||
8)),
|
8)),
|
||||||
h1: white,
|
h1: white,
|
||||||
h2: white,
|
h2: white,
|
||||||
|
@ -1042,8 +1392,10 @@ class _MainAppState extends State<MainApp> {
|
||||||
: (Theme.of(context).brightness ==
|
: (Theme.of(context).brightness ==
|
||||||
Brightness.light)
|
Brightness.light)
|
||||||
? MarkdownStyleSheet(
|
? MarkdownStyleSheet(
|
||||||
p: const TextStyle(
|
p: TextStyle(
|
||||||
color: Colors.black,
|
color: greyed
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.black,
|
||||||
fontSize: 16,
|
fontSize: 16,
|
||||||
fontWeight:
|
fontWeight:
|
||||||
FontWeight.w500),
|
FontWeight.w500),
|
||||||
|
@ -1056,8 +1408,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
),
|
),
|
||||||
code: const TextStyle(
|
code: const TextStyle(
|
||||||
color: Colors.white,
|
color: Colors.white,
|
||||||
backgroundColor:
|
backgroundColor: Colors.black),
|
||||||
Colors.black),
|
|
||||||
codeblockDecoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(8)),
|
codeblockDecoration: BoxDecoration(color: Colors.black, borderRadius: BorderRadius.circular(8)),
|
||||||
horizontalRuleDecoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.grey[200]!, width: 1))))
|
horizontalRuleDecoration: BoxDecoration(border: Border(top: BorderSide(color: Colors.grey[200]!, width: 1))))
|
||||||
: MarkdownStyleSheet(
|
: MarkdownStyleSheet(
|
||||||
|
@ -1476,7 +1827,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
inputTextColor: (theme ?? ThemeData()).colorScheme.onSurface,
|
inputTextColor: (theme ?? ThemeData()).colorScheme.onSurface,
|
||||||
inputBorderRadius: const BorderRadius.all(Radius.circular(64)),
|
inputBorderRadius: const BorderRadius.all(Radius.circular(64)),
|
||||||
inputPadding: const EdgeInsets.all(16),
|
inputPadding: const EdgeInsets.all(16),
|
||||||
inputMargin: EdgeInsets.only(left: 8, right: 8, bottom: (MediaQuery.of(context).viewInsets.bottom == 0.0 && !desktopFeature()) ? 0 : 8),
|
inputMargin: EdgeInsets.only(left: (MediaQuery.of(context).viewInsets.bottom == 0.0 && !desktopFeature()) ? 8 : 6, right: (MediaQuery.of(context).viewInsets.bottom == 0.0 && !desktopFeature()) ? 8 : 6, bottom: (MediaQuery.of(context).viewInsets.bottom == 0.0 && !desktopFeature()) ? 0 : 8),
|
||||||
messageMaxWidth: (MediaQuery.of(context).size.width >= 1000)
|
messageMaxWidth: (MediaQuery.of(context).size.width >= 1000)
|
||||||
? (MediaQuery.of(context).size.width >= 1600)
|
? (MediaQuery.of(context).size.width >= 1600)
|
||||||
? (MediaQuery.of(context).size.width >= 2200)
|
? (MediaQuery.of(context).size.width >= 2200)
|
||||||
|
@ -1532,7 +1883,7 @@ class _MainAppState extends State<MainApp> {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
drawerEdgeDragWidth: (prefs!.getBool("fixCodeblockScroll") ?? false)
|
drawerEdgeDragWidth: (prefs?.getBool("fixCodeblockScroll") ?? false)
|
||||||
? null
|
? null
|
||||||
: (desktopLayout(context)
|
: (desktopLayout(context)
|
||||||
? null
|
? null
|
||||||
|
|
|
@ -255,17 +255,21 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
http.Response request;
|
http.Response? request;
|
||||||
try {
|
try {
|
||||||
request = await http
|
var client = http.Client();
|
||||||
.get(
|
final requestBase = http.Request("get", Uri.parse(tmpHost))
|
||||||
Uri.parse(tmpHost),
|
..headers.addAll(
|
||||||
headers: (jsonDecode(prefs!.getString("hostHeaders") ?? "{}") as Map)
|
(jsonDecode(prefs!.getString("hostHeaders") ?? "{}") as Map)
|
||||||
.cast<String, String>(),
|
.cast<String, String>(),
|
||||||
)
|
)
|
||||||
|
..followRedirects = false;
|
||||||
|
request = await http.Response.fromStream(await requestBase
|
||||||
|
.send()
|
||||||
.timeout(const Duration(seconds: 5), onTimeout: () {
|
.timeout(const Duration(seconds: 5), onTimeout: () {
|
||||||
return http.Response("Error", 408);
|
return http.StreamedResponse(const Stream.empty(), 408);
|
||||||
});
|
}));
|
||||||
|
client.close();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
hostInvalidHost = true;
|
hostInvalidHost = true;
|
||||||
|
@ -278,7 +282,8 @@ class _ScreenSettingsState extends State<ScreenSettings> {
|
||||||
setState(() {
|
setState(() {
|
||||||
hostLoading = false;
|
hostLoading = false;
|
||||||
host = tmpHost;
|
host = tmpHost;
|
||||||
if (hostInputController.text != host!) {
|
if (hostInputController.text != host! &&
|
||||||
|
(Uri.parse(tmpHost).toString() != fixedHost)) {
|
||||||
hostInputController.text = host!;
|
hostInputController.text = host!;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -32,9 +32,7 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
Text(AppLocalizations.of(context)!.settingsTitleExport),
|
Text(AppLocalizations.of(context)!.settingsTitleExport),
|
||||||
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
Expanded(child: SizedBox(height: 200, child: MoveWindow()))
|
||||||
]),
|
]),
|
||||||
actions:
|
actions: desktopControlsActions(context)),
|
||||||
desktopControlsActions(context)
|
|
||||||
),
|
|
||||||
body: Padding(
|
body: Padding(
|
||||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||||
child: Column(children: [
|
child: Column(children: [
|
||||||
|
@ -58,7 +56,9 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
jsonEncode(prefs!.getStringList("chats") ?? []));
|
jsonEncode(prefs!.getStringList("chats") ?? []));
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
button(AppLocalizations.of(context)!.settingsImportChats,
|
allowMultipleChats
|
||||||
|
? button(
|
||||||
|
AppLocalizations.of(context)!.settingsImportChats,
|
||||||
Icons.download_rounded, () {
|
Icons.download_rounded, () {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
showDialog(
|
showDialog(
|
||||||
|
@ -67,7 +67,8 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text(AppLocalizations.of(context)!
|
title: Text(AppLocalizations.of(context)!
|
||||||
.settingsImportChatsTitle),
|
.settingsImportChatsTitle),
|
||||||
content: Text(AppLocalizations.of(context)!
|
content: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
.settingsImportChatsDescription),
|
.settingsImportChatsDescription),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
|
@ -75,24 +76,29 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context)!
|
child: Text(AppLocalizations.of(
|
||||||
|
context)!
|
||||||
.settingsImportChatsCancel)),
|
.settingsImportChatsCancel)),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
selectionHaptic();
|
selectionHaptic();
|
||||||
FilePickerResult? result =
|
FilePickerResult? result =
|
||||||
await FilePicker.platform.pickFiles(
|
await FilePicker.platform
|
||||||
|
.pickFiles(
|
||||||
type: FileType.custom,
|
type: FileType.custom,
|
||||||
allowedExtensions: ["json"]);
|
allowedExtensions: [
|
||||||
|
"json"
|
||||||
|
]);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
// ignore: use_build_context_synchronously
|
// ignore: use_build_context_synchronously
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
File file =
|
File file = File(
|
||||||
File(result.files.single.path!);
|
result.files.single.path!);
|
||||||
var content = await file.readAsString();
|
var content =
|
||||||
|
await file.readAsString();
|
||||||
List<dynamic> tmpHistory =
|
List<dynamic> tmpHistory =
|
||||||
jsonDecode(content);
|
jsonDecode(content);
|
||||||
List<String> history = [];
|
List<String> history = [];
|
||||||
|
@ -103,7 +109,8 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
history.add(tmpHistory[i]);
|
history.add(tmpHistory[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
prefs!.setStringList("chats", history);
|
prefs!.setStringList(
|
||||||
|
"chats", history);
|
||||||
|
|
||||||
messages = [];
|
messages = [];
|
||||||
chatUuid = null;
|
chatUuid = null;
|
||||||
|
@ -125,11 +132,13 @@ class _ScreenSettingsExportState extends State<ScreenSettingsExport> {
|
||||||
.settingsImportChatsSuccess),
|
.settingsImportChatsSuccess),
|
||||||
showCloseIcon: true));
|
showCloseIcon: true));
|
||||||
},
|
},
|
||||||
child: Text(AppLocalizations.of(context)!
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!
|
||||||
.settingsImportChatsImport))
|
.settingsImportChatsImport))
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
: const SizedBox.shrink()
|
||||||
]),
|
]),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
|
|
|
@ -162,7 +162,7 @@ Future<String> send(String value, BuildContext context, Function setState,
|
||||||
.cast<String, String>(),
|
.cast<String, String>(),
|
||||||
baseUrl: "$host/api");
|
baseUrl: "$host/api");
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
if ((prefs!.getString("requestType") ?? "stream") == "stream") {
|
if ((prefs!.getString("requestType") ?? "stream") == "stream") {
|
||||||
final stream = client
|
final stream = client
|
||||||
.generateChatCompletionStream(
|
.generateChatCompletionStream(
|
||||||
|
@ -182,9 +182,9 @@ Future<String> send(String value, BuildContext context, Function setState,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (chatAllowed) return "";
|
if (chatAllowed) return "";
|
||||||
if (text.trim() == "") {
|
// if (text.trim() == "") {
|
||||||
throw Exception();
|
// throw Exception();
|
||||||
}
|
// }
|
||||||
messages.insert(
|
messages.insert(
|
||||||
0, types.TextMessage(author: assistant, id: newId, text: text));
|
0, types.TextMessage(author: assistant, id: newId, text: text));
|
||||||
if (onStream != null) {
|
if (onStream != null) {
|
||||||
|
@ -204,9 +204,9 @@ Future<String> send(String value, BuildContext context, Function setState,
|
||||||
)
|
)
|
||||||
.timeout(const Duration(seconds: 30));
|
.timeout(const Duration(seconds: 30));
|
||||||
if (chatAllowed) return "";
|
if (chatAllowed) return "";
|
||||||
if (request.message!.content.trim() == "") {
|
// if (request.message!.content.trim() == "") {
|
||||||
throw Exception();
|
// throw Exception();
|
||||||
}
|
// }
|
||||||
messages.insert(
|
messages.insert(
|
||||||
0,
|
0,
|
||||||
types.TextMessage(
|
types.TextMessage(
|
||||||
|
@ -215,37 +215,37 @@ Future<String> send(String value, BuildContext context, Function setState,
|
||||||
setState(() {});
|
setState(() {});
|
||||||
heavyHaptic();
|
heavyHaptic();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
// } catch (e) {
|
||||||
for (var i = 0; i < messages.length; i++) {
|
// for (var i = 0; i < messages.length; i++) {
|
||||||
if (messages[i].id == newId) {
|
// if (messages[i].id == newId) {
|
||||||
messages.removeAt(i);
|
// messages.removeAt(i);
|
||||||
break;
|
// break;
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
setState(() {
|
// setState(() {
|
||||||
chatAllowed = true;
|
// chatAllowed = true;
|
||||||
messages.removeAt(0);
|
// messages.removeAt(0);
|
||||||
if (messages.isEmpty) {
|
// if (messages.isEmpty) {
|
||||||
var tmp = (prefs!.getStringList("chats") ?? []);
|
// var tmp = (prefs!.getStringList("chats") ?? []);
|
||||||
chatUuid = null;
|
// for (var i = 0; i < tmp.length; i++) {
|
||||||
for (var i = 0; i < tmp.length; i++) {
|
// if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] ==
|
||||||
if (jsonDecode((prefs!.getStringList("chats") ?? [])[i])["uuid"] ==
|
// chatUuid) {
|
||||||
chatUuid) {
|
// tmp.removeAt(i);
|
||||||
tmp.removeAt(i);
|
// prefs!.setStringList("chats", tmp);
|
||||||
prefs!.setStringList("chats", tmp);
|
// break;
|
||||||
break;
|
// }
|
||||||
}
|
// }
|
||||||
}
|
// chatUuid = null;
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
// ignore: use_build_context_synchronously
|
// // ignore: use_build_context_synchronously
|
||||||
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
// ScaffoldMessenger.of(context).showSnackBar(SnackBar(
|
||||||
content:
|
// content:
|
||||||
// ignore: use_build_context_synchronously
|
// // ignore: use_build_context_synchronously
|
||||||
Text(AppLocalizations.of(context)!.settingsHostInvalid("timeout")),
|
// Text(AppLocalizations.of(context)!.settingsHostInvalid("timeout")),
|
||||||
showCloseIcon: true));
|
// showCloseIcon: true));
|
||||||
return "";
|
// return "";
|
||||||
}
|
// }
|
||||||
|
|
||||||
if ((prefs!.getString("requestType") ?? "stream") == "stream") {
|
if ((prefs!.getString("requestType") ?? "stream") == "stream") {
|
||||||
if (onStream != null) {
|
if (onStream != null) {
|
||||||
|
|
|
@ -82,7 +82,8 @@ void setModel(BuildContext context, Function setState) {
|
||||||
if (!loaded) return;
|
if (!loaded) return;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
if (usedIndex >= 0 && modelsReal[usedIndex] != model) {
|
if (usedIndex >= 0 && modelsReal[usedIndex] != model) {
|
||||||
if (prefs!.getBool("resetOnModelSelect") ?? true) {
|
if (prefs!.getBool("resetOnModelSelect") ??
|
||||||
|
true && allowMultipleChats) {
|
||||||
messages = [];
|
messages = [];
|
||||||
chatUuid = null;
|
chatUuid = null;
|
||||||
}
|
}
|
||||||
|
@ -467,6 +468,24 @@ Future<String> prompt(BuildContext context,
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
keyboardType: keyboard,
|
keyboardType: keyboard,
|
||||||
maxLines: maxLines,
|
maxLines: maxLines,
|
||||||
|
onSubmitted: (value) async {
|
||||||
|
if (validator != null) {
|
||||||
|
selectionHaptic();
|
||||||
|
setLocalState(() {
|
||||||
|
error = null;
|
||||||
|
});
|
||||||
|
bool valid = await validator(controller.text);
|
||||||
|
if (!valid) {
|
||||||
|
setLocalState(() {
|
||||||
|
error = validatorError;
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
returnText = controller.text;
|
||||||
|
// ignore: use_build_context_synchronously
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
border: const OutlineInputBorder(),
|
border: const OutlineInputBorder(),
|
||||||
hintText: placeholder,
|
hintText: placeholder,
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
{
|
{
|
||||||
"de": [
|
"de": [
|
||||||
|
"deleteChat",
|
||||||
|
"renameChat",
|
||||||
"settingsDescriptionBehavior",
|
"settingsDescriptionBehavior",
|
||||||
"settingsDescriptionInterface",
|
"settingsDescriptionInterface",
|
||||||
"settingsDescriptionVoice",
|
"settingsDescriptionVoice",
|
||||||
|
@ -14,6 +16,8 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
"zh": [
|
"zh": [
|
||||||
|
"deleteChat",
|
||||||
|
"renameChat",
|
||||||
"settingsDescriptionBehavior",
|
"settingsDescriptionBehavior",
|
||||||
"settingsDescriptionInterface",
|
"settingsDescriptionInterface",
|
||||||
"settingsDescriptionVoice",
|
"settingsDescriptionVoice",
|
||||||
|
|
Loading…
Reference in New Issue