Skip to content

Commit

Permalink
session tile (#431)
Browse files Browse the repository at this point in the history
* add ability to add character

* character browser tile

* session tile
  • Loading branch information
danemadsen authored Mar 13, 2024
1 parent ad103a1 commit cc5c900
Show file tree
Hide file tree
Showing 6 changed files with 239 additions and 119 deletions.
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
Loading

0 comments on commit cc5c900

Please sign in to comment.