Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

session tile #431

Merged
merged 3 commits into from
Mar 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 6 additions & 0 deletions lib/providers/session.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class Session extends ChangeNotifier {
notifyListeners();
}

void from(Session session) {
_root = session._root;
tail = _root.findTail();
notifyListeners();
}

void fromMap(Map<String, dynamic> inputJson) {
_root = ChatNode.fromMap(inputJson);
tail = _root.findTail();
Expand Down
69 changes: 47 additions & 22 deletions lib/ui/mobile/pages/character/character_browser_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:maid/providers/character.dart';
import 'package:maid/ui/mobile/widgets/appbars/generic_app_bar.dart';
import 'package:maid/ui/mobile/widgets/tiles/character_browser_tile.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
Expand Down Expand Up @@ -54,30 +53,56 @@ class _CharacterBrowserPageState extends State<CharacterBrowserPage> {

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: const GenericAppBar(title: "Character Browser"),
body: Consumer<Character>(
builder: (context, character, child) {
_current = character.key;
return Consumer<Character>(
builder: (context, character, child) {
_current = character.key;

if (!_characters.contains(character)) {
_characters.insert(0, character);
}
if (!_characters.contains(character)) {
_characters.insert(0, character);
}

return ListView.builder(
itemCount: _characters.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(
8.0), // Adjust the padding value as needed
child: CharacterBrowserTile(
character: _characters[index],
return Scaffold(
appBar: AppBar(
elevation: 0.0,
leading: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
Navigator.of(context).pop();
},
),
title: const Text("Character Browser"),
actions: [
const Expanded(child: SizedBox()),
IconButton(
icon: const Icon(Icons.add),
onPressed: () {
setState(() {
final newCharacter = Character();
_characters.add(newCharacter);
character.from(newCharacter);
});
},
),
);
},
);
},
),
],
),
body: ListView.builder(
itemCount: _characters.length,
itemBuilder: (context, index) {
return Padding(
padding: const EdgeInsets.all(
8.0), // Adjust the padding value as needed
child: CharacterBrowserTile(
character: _characters[index],
onDelete: () {
setState(() {
_characters.removeAt(index);
});
},
),
);
},
));
},
);
}
}
12 changes: 7 additions & 5 deletions lib/ui/mobile/pages/character/character_customization_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@ class CharacterCustomizationPage extends StatefulWidget {
const CharacterCustomizationPage({super.key});

@override
State<CharacterCustomizationPage> createState() => _CharacterCustomizationPageState();
State<CharacterCustomizationPage> createState() =>
_CharacterCustomizationPageState();
}

class _CharacterCustomizationPageState extends State<CharacterCustomizationPage> {
class _CharacterCustomizationPageState
extends State<CharacterCustomizationPage> {
late TextEditingController _nameController;
late TextEditingController _descriptionController;
late TextEditingController _personalityController;
Expand Down Expand Up @@ -51,7 +53,6 @@ class _CharacterCustomizationPageState extends State<CharacterCustomizationPage>
appBar: const GenericAppBar(title: "Character Customization"),
body: Consumer<Character>(
builder: (context, character, child) {

SharedPreferences.getInstance().then((prefs) {
prefs.setString("last_character", json.encode(character.toMap()));
});
Expand Down Expand Up @@ -82,7 +83,8 @@ class _CharacterCustomizationPageState extends State<CharacterCustomizationPage>
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => const CharacterBrowserPage()));
builder: (context) =>
const CharacterBrowserPage()));
},
child: Text(
"Switch Character",
Expand Down Expand Up @@ -167,7 +169,7 @@ class _CharacterCustomizationPageState extends State<CharacterCustomizationPage>
title: Row(
children: [
const Expanded(
child: Text("Character Name"),
child: Text("Name"),
),
Expanded(
flex: 2,
Expand Down
71 changes: 35 additions & 36 deletions lib/ui/mobile/widgets/dropdowns/ai_platform_dropdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,43 +21,42 @@ class AiPlatformDropdown extends StatelessWidget {
blendMode: BlendMode
.srcIn, // This blend mode applies the shader to the text color.
child: DropdownMenu<AiPlatformType>(
dropdownMenuEntries: const [
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.llamacpp,
label: "LlamaCPP",
dropdownMenuEntries: const [
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.llamacpp,
label: "LlamaCPP",
),
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.ollama,
label: "Ollama",
),
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.openAI,
label: "OpenAI",
),
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.mistralAI,
label: "MistralAI",
),
],
onSelected: (AiPlatformType? value) {
if (value != null) {
ai.apiType = value;
}
},
initialSelection: ai.apiType,
inputDecorationTheme: const InputDecorationTheme(
labelStyle: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.white,
fontSize: 15.0),
hintStyle: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.white,
fontSize: 15.0),
floatingLabelBehavior: FloatingLabelBehavior.never,
),
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.ollama,
label: "Ollama",
),
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.openAI,
label: "OpenAI",
),
DropdownMenuEntry<AiPlatformType>(
value: AiPlatformType.mistralAI,
label: "MistralAI",
),
],
onSelected: (AiPlatformType? value) {
if (value != null) {
ai.apiType = value;
}
},
initialSelection: ai.apiType,
inputDecorationTheme: const InputDecorationTheme(
labelStyle: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.white,
fontSize: 15.0),
hintStyle: TextStyle(
fontWeight: FontWeight.normal,
color: Colors.white,
fontSize: 15.0),
floatingLabelBehavior: FloatingLabelBehavior.never,
),
width: 175
),
width: 175),
);
});
}
Expand Down
150 changes: 122 additions & 28 deletions lib/ui/mobile/widgets/tiles/character_browser_tile.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import 'package:flutter/material.dart';
import 'package:maid/providers/character.dart';
import 'package:maid/providers/user.dart';
import 'package:maid/static/logger.dart';
import 'package:maid/static/utilities.dart';
import 'package:provider/provider.dart';

class CharacterBrowserTile extends StatefulWidget {
final Character character;
final void Function() onDelete;

const CharacterBrowserTile({super.key, required this.character});
const CharacterBrowserTile(
{super.key, required this.character, required this.onDelete});

@override
State<CharacterBrowserTile> createState() => _CharacterBrowserTileState();
Expand All @@ -22,37 +25,128 @@ class _CharacterBrowserTileState extends State<CharacterBrowserTile> {
builder: (context, character, user, child) {
selected = character.key == widget.character.key;

return ListTile(
tileColor: Theme.of(context).colorScheme.primary,
selectedTileColor: Theme.of(context).colorScheme.secondary.withOpacity(0.25),
textColor: Colors.white,
selectedColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
return GestureDetector(
child: ListTile(
tileColor: Theme.of(context).colorScheme.primary,
selectedTileColor:
Theme.of(context).colorScheme.secondary.withOpacity(0.25),
textColor: Colors.white,
selectedColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
// Square image with rounded corners of the character
leading: ClipRRect(
borderRadius: BorderRadius.circular(
10.0), // Adjust the corner radius here
child: Image(
image: Image.file(widget.character.profile).image,
width: 50, // Adjust the size as needed
height: 50,
fit: BoxFit
.cover, // This ensures the image covers the square area
),
),
minLeadingWidth: 60,
title: Column(children: [
Text(widget.character.name),
const SizedBox(height: 10.0),
Text(
Utilities.formatPlaceholders(widget.character.description,
user.name, widget.character.name),
style: const TextStyle(fontSize: 12.0),
),
]),
selected: selected),
onTap: () {
character.from(widget.character);
},
onSecondaryTapUp: (details) => _onSecondaryTapUp(details, context),
onLongPressStart: (details) => _onLongPressStart(details, context),
);
},
);
}

void _onSecondaryTapUp(TapUpDetails details, BuildContext context) =>
_showContextMenu(details.globalPosition, context);

void _onLongPressStart(LongPressStartDetails details, BuildContext context) =>
_showContextMenu(details.globalPosition, context);

void _showContextMenu(Offset position, BuildContext context) {
final RenderBox overlay =
Overlay.of(context).context.findRenderObject() as RenderBox;
final TextEditingController controller =
TextEditingController(text: widget.character.name);

showMenu(
context: context,
position: RelativeRect.fromRect(
position & const Size(40, 40), // smaller rect, the touch area
Offset.zero & overlay.size, // Bigger rect, the entire screen
),
items: <PopupMenuEntry>[
PopupMenuItem(
child: const Text('Delete'),
onTap: () {
Navigator.of(context).pop(); // Close the menu first
widget.onDelete();
},
),
PopupMenuItem(
child: const Text('Rename'),
onTap: () {
Navigator.of(context).pop(); // Close the menu first
// Delayed execution to allow the popup menu to close properly
WidgetsBinding.instance.addPostFrameCallback((_) {
_showRenameDialog(context, controller);
});
},
),
],
);
}

void _showRenameDialog(
BuildContext context, TextEditingController controller) {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
title: const Text(
"Rename Session",
textAlign: TextAlign.center,
),
// Square image with rounded corners of the character
leading: ClipRRect(
borderRadius: BorderRadius.circular(10.0), // Adjust the corner radius here
child: Image(
image: Image.file(widget.character.profile).image,
width: 50, // Adjust the size as needed
height: 50,
fit: BoxFit.cover, // This ensures the image covers the square area
content: TextField(
controller: controller,
decoration: const InputDecoration(
hintText: "Enter new name",
),
),
minLeadingWidth: 60,
title: Column(children: [
Text(widget.character.name),
const SizedBox(height: 10.0),
Text(
Utilities.formatPlaceholders(widget.character.description, user.name, widget.character.name),
style: const TextStyle(fontSize: 12.0),
actions: [
FilledButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(
"Cancel",
style: Theme.of(context).textTheme.labelLarge,
),
),
]),
selected: selected,
onTap: () {
character.from(widget.character);
}
FilledButton(
onPressed: () {
String oldName = widget.character.name;
Logger.log(
"Updating character $oldName ====> ${controller.text}");
widget.character.name = controller.text;
Navigator.of(context).pop();
setState(() {});
},
child: Text(
"Rename",
style: Theme.of(context).textTheme.labelLarge,
),
),
],
);
},
);
Expand Down