From d75da5389a0297627c7cfabf43dc744a18006a88 Mon Sep 17 00:00:00 2001 From: Yasien Mac Mini Date: Fri, 7 Nov 2025 12:03:44 +0200 Subject: [PATCH] QOL: Mzansi AI Chat Look and Feel pt1 --- .../android/app/src/main/AndroidManifest.xml | 4 +- .../mih_providers/mzansi_ai_provider.dart | 223 +++++++++- .../mih_providers/ollama_provider.dart | 136 ++++++ Frontend/lib/mih_config/mih_colors.dart | 8 + Frontend/lib/mih_config/mih_theme.dart | 140 ++++-- .../lib/mih_packages/mzansi_ai/mzansi_ai.dart | 6 + .../mzansi_ai/package_tools/ai_chat.dart | 3 - .../mzansi_ai/package_tools/mih_ai_chat.dart | 286 ++++++++++++ .../flutter/generated_plugin_registrant.cc | 8 + .../linux/flutter/generated_plugins.cmake | 2 + .../Flutter/GeneratedPluginRegistrant.swift | 10 + Frontend/pubspec.lock | 416 +++++++++++++++++- Frontend/pubspec.yaml | 1 + .../flutter/generated_plugin_registrant.cc | 12 + .../windows/flutter/generated_plugins.cmake | 4 + 15 files changed, 1198 insertions(+), 61 deletions(-) create mode 100644 Frontend/lib/mih_components/mih_providers/ollama_provider.dart create mode 100644 Frontend/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart diff --git a/Frontend/android/app/src/main/AndroidManifest.xml b/Frontend/android/app/src/main/AndroidManifest.xml index 6683a646..74a51a4f 100644 --- a/Frontend/android/app/src/main/AndroidManifest.xml +++ b/Frontend/android/app/src/main/AndroidManifest.xml @@ -11,7 +11,9 @@ - + diff --git a/Frontend/lib/mih_components/mih_providers/mzansi_ai_provider.dart b/Frontend/lib/mih_components/mih_providers/mzansi_ai_provider.dart index 1ba96af5..a514acfc 100644 --- a/Frontend/lib/mih_components/mih_providers/mzansi_ai_provider.dart +++ b/Frontend/lib/mih_components/mih_providers/mzansi_ai_provider.dart @@ -1,12 +1,51 @@ import 'package:flutter/material.dart'; +import 'package:flutter_ai_toolkit/flutter_ai_toolkit.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_providers/ollama_provider.dart'; +import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:mzansi_innovation_hub/mih_config/mih_env.dart'; class MzansiAiProvider extends ChangeNotifier { int toolIndex; String? startUpQuestion; + late OllamaProvider ollamaProvider; MzansiAiProvider({ this.toolIndex = 0, - }); + }) { + ollamaProvider = OllamaProvider( + baseUrl: "${AppEnviroment.baseAiUrl}/api", + model: AppEnviroment.getEnv() == "Prod" ? 'gemma3n:e4b' : "gemma3:1b", + systemPrompt: "You are Mzansi AI, a helpful and friendly AI assistant running on the 'MIH App'.\n" + + "The MIH App was created by 'Mzansi Innovation Hub', a South African-based startup company." + + "Your primary purpose is to assist users by answering general questions and helping with creative writing tasks or any other task a user might have for you.\n" + + "Maintain a casual and friendly tone, but always remain professional.\n" + + "Strive for a balance between being empathetic and delivering factual information accurately.\n" + + "You may use lighthearted or playful language if the context is appropriate and enhances the user experience.\n" + + "You operate within the knowledge domain of the 'MIH App'.\n" + + "Here is a description of the MIH App and its features:\n" + + "MIH App Description: MIH is the first super app of Mzansi, designed to streamline both personal and business life. It's an all-in-one platform for managing professional profiles, teams, appointments, and quick calculations. \n" + + "Key Features:\n" + + "- Mzansi Profile: Central hub for managing personal and business information, including business team details." + + "- Mzansi Wallet: Digitally store loyalty cards.\n" + + "- Patient Manager (For Medical Practices): Seamless patient appointment scheduling and data management.\n" + + "- Mzansi AI: Your friendly AI assistant for quick answers and support (that's you!).\n" + + "- Mzansi Directory: A place to search and find out more about the people and businesses across Mzansi.\n" + + "- Calendar: Integrated calendar for managing personal and business appointments.\n" + + "- Calculator: Simple calculator with tip and forex calculation functionality.\n" + + "- MIH Access: Manage and view profile access security.\n" + + "**Core Rules and Guidelines:**\n" + + "- **Accuracy First:** Always prioritize providing correct information.\n" + + "- **Uncertainty Handling:** If you are unsure about an answer, politely respond with: 'Please bear with us as we are still learning and do not have all the answers.'\n" + + "- **Response Length:** Aim to keep responses under 250 words. If a more comprehensive answer is required, exceed this limit but offer to elaborate further (e.g., 'Would you like me to elaborate on this topic?').\n" + + "- **Language & Safety:** Never use offensive language or generate harmful content. If a user presses for information that is inappropriate or out of bounds, clearly state why you cannot provide it (e.g., 'I cannot assist with that request as it goes against my safety guidelines.').\n" + + "- **Out-of-Scope Questions:** - If a question is unclear, ask the user to rephrase or clarify it. - If a question is entirely out of your scope and you cannot provide a useful answer, admit you don't know. - If a user is unhappy with your response or needs further assistance beyond your capabilities, suggest they visit the 'Mzansi Innovation Hub Social Media Pages' for more direct support. Do not provide specific links, just refer to the pages generally.\n" + + "- **Target Audience:** Adapt your explanations to beginners and intermediate users, but be prepared for more complex questions from expert users. Ensure your language is clear and easy to understand.\n", + )..addListener(() { + notifyListeners(); // Forward OllamaProvider notifications + }); + } void reset() { toolIndex = 0; @@ -23,4 +62,186 @@ class MzansiAiProvider extends ChangeNotifier { startUpQuestion = question; notifyListeners(); } + + LlmChatViewStyle? getChatStyle(BuildContext context) { + return LlmChatViewStyle( + backgroundColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + progressIndicatorColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + menuColor: Colors.black, + // MihColors.getGreenColor( + // MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + disabledButtonStyle: ActionButtonStyle( + icon: MihIcons.mzansiAi, + iconColor: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + iconDecoration: BoxDecoration( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + ), + recordButtonStyle: ActionButtonStyle( + iconColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + iconDecoration: BoxDecoration( + color: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + textStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + ), + submitButtonStyle: ActionButtonStyle( + icon: Icons.send, + iconColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + iconDecoration: BoxDecoration( + color: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + textStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + ), + stopButtonStyle: ActionButtonStyle( + iconColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + iconDecoration: BoxDecoration( + color: MihColors.getRedColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + textStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + ), + actionButtonBarDecoration: BoxDecoration( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + // Mzansi AI Chat Style + llmMessageStyle: LlmMessageStyle( + icon: MihIcons.mzansiAi, + iconColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + iconDecoration: BoxDecoration( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topRight: Radius.circular(25), + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(76), + blurRadius: 8, + offset: Offset(2, 2), + ), + ], + ), + ), + // User Chat Style + userMessageStyle: UserMessageStyle( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(25), + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + color: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(76), + blurRadius: 8, + offset: Offset(2, 2), + ), + ], + ), + textStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + ), + // User Input Style + chatInputStyle: ChatInputStyle( + backgroundColor: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + decoration: BoxDecoration( + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(76), + blurRadius: 8, + offset: Offset(2, 2), + ), + ], + ), + hintStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + hintText: "Ask Mzansi AI...", + ), + // Suggestions Style + suggestionStyle: SuggestionStyle( + decoration: BoxDecoration( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(25), + bottomLeft: Radius.circular(25), + bottomRight: Radius.circular(25), + ), + color: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + boxShadow: [ + BoxShadow( + color: Colors.black.withAlpha(76), + blurRadius: 8, + offset: Offset(2, 2), + ), + ], + ), + textStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + ), + copyButtonStyle: ActionButtonStyle( + iconColor: MihColors.getSecondaryInvertedColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + editButtonStyle: ActionButtonStyle( + iconColor: MihColors.getSecondaryInvertedColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + cancelButtonStyle: ActionButtonStyle( + iconDecoration: BoxDecoration( + color: MihColors.getRedColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + borderRadius: BorderRadius.circular(25), + ), + iconColor: MihColors.getSecondaryInvertedColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + textStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + )), + ); + } } diff --git a/Frontend/lib/mih_components/mih_providers/ollama_provider.dart b/Frontend/lib/mih_components/mih_providers/ollama_provider.dart new file mode 100644 index 00000000..5b4ee268 --- /dev/null +++ b/Frontend/lib/mih_components/mih_providers/ollama_provider.dart @@ -0,0 +1,136 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_ai_toolkit/flutter_ai_toolkit.dart'; +import 'package:ollama_dart/ollama_dart.dart'; + +class OllamaProvider extends LlmProvider with ChangeNotifier { + OllamaProvider({ + String? baseUrl, + Map? headers, + Map? queryParams, + required String model, + String? systemPrompt, + }) : _client = OllamaClient( + baseUrl: baseUrl, + headers: headers, + queryParams: queryParams, + ), + _model = model, + _systemPrompt = systemPrompt, + _history = []; + final OllamaClient _client; + final String _model; + final List _history; + final String? _systemPrompt; + + @override + Stream generateStream( + String prompt, { + Iterable attachments = const [], + }) async* { + final messages = _mapToOllamaMessages([ + ChatMessage.user(prompt, attachments), + ]); + yield* _generateStream(messages); + } + + @override + Stream sendMessageStream( + String prompt, { + Iterable attachments = const [], + }) async* { + final userMessage = ChatMessage.user(prompt, attachments); + final llmMessage = ChatMessage.llm(); + _history.addAll([userMessage, llmMessage]); + notifyListeners(); + final messages = _mapToOllamaMessages(_history); + final stream = _generateStream(messages); + yield* stream.map((chunk) { + llmMessage.append(chunk); + return chunk; + }); + notifyListeners(); + } + + @override + Iterable get history => _history; + + void resetChat() { + _history.clear(); + notifyListeners(); + } + + @override + set history(Iterable history) { + _history.clear(); + _history.addAll(history); + notifyListeners(); + } + + Stream _generateStream(List messages) async* { + final allMessages = []; + if (_systemPrompt != null && _systemPrompt.isNotEmpty) { + allMessages.add(Message( + role: MessageRole.system, + content: _systemPrompt, + )); + } + allMessages.addAll(messages); + + final stream = _client.generateChatCompletionStream( + request: GenerateChatCompletionRequest( + model: _model, + messages: allMessages, + ), + ); + // final stream = _client.generateChatCompletionStream( + // request: GenerateChatCompletionRequest( + // model: _model, + // messages: messages, + // ), + // ); + + yield* stream.map((res) => res.message.content); + } + + List _mapToOllamaMessages(List messages) { + return messages.map((message) { + switch (message.origin) { + case MessageOrigin.user: + if (message.attachments.isEmpty) { + return Message( + role: MessageRole.user, + content: message.text ?? '', + ); + } + + return Message( + role: MessageRole.user, + content: message.text ?? '', + images: [ + for (final attachment in message.attachments) + if (attachment is ImageFileAttachment) + base64Encode(attachment.bytes) + else + throw LlmFailureException( + 'Unsupported attachment type: $attachment', + ), + ], + ); + + case MessageOrigin.llm: + return Message( + role: MessageRole.assistant, + content: message.text ?? '', + ); + } + }).toList(growable: false); + } + + @override + void dispose() { + _client.endSession(); + super.dispose(); + } +} diff --git a/Frontend/lib/mih_config/mih_colors.dart b/Frontend/lib/mih_config/mih_colors.dart index 724868e4..6a2bc975 100644 --- a/Frontend/lib/mih_config/mih_colors.dart +++ b/Frontend/lib/mih_config/mih_colors.dart @@ -17,6 +17,14 @@ class MihColors { } } + static Color getSecondaryInvertedColor(bool darkMode) { + if (darkMode == true) { + return const Color(0XFF412301); + } else { + return const Color(0XFFc5bbab); + } + } + static Color getHighlightColor(bool darkMode) { if (darkMode == true) { return const Color(0XFF9bc7fa); diff --git a/Frontend/lib/mih_config/mih_theme.dart b/Frontend/lib/mih_config/mih_theme.dart index 22436d37..ebbf126c 100644 --- a/Frontend/lib/mih_config/mih_theme.dart +++ b/Frontend/lib/mih_config/mih_theme.dart @@ -35,48 +35,116 @@ class MihTheme { ThemeData getData(bool bool) { return ThemeData( - fontFamily: 'Segoe UI', - scaffoldBackgroundColor: MihColors.getPrimaryColor(mode == "Dark"), - // pageTransitionsTheme: PageTransitionsTheme( - // builders: Map.fromIterable( - // TargetPlatform.values, - // value: (dynamic _) => const FadeUpwardsPageTransitionsBuilder(), - // ), - // ), - colorScheme: ColorScheme( - brightness: getBritness(), - primary: MihColors.getSecondaryColor(mode == "Dark"), - onPrimary: MihColors.getPrimaryColor(mode == "Dark"), - secondary: MihColors.getPrimaryColor(mode == "Dark"), - onSecondary: MihColors.getSecondaryColor(mode == "Dark"), - error: MihColors.getRedColor(mode == "Dark"), - onError: MihColors.getPrimaryColor(mode == "Dark"), - surface: MihColors.getPrimaryColor(mode == "Dark"), - onSurface: MihColors.getSecondaryColor(mode == "Dark"), + fontFamily: 'Segoe UI', + scaffoldBackgroundColor: MihColors.getPrimaryColor(mode == "Dark"), + // pageTransitionsTheme: PageTransitionsTheme( + // builders: Map.fromIterable( + // TargetPlatform.values, + // value: (dynamic _) => const FadeUpwardsPageTransitionsBuilder(), + // ), + // ), + colorScheme: ColorScheme( + brightness: getBritness(), + primary: MihColors.getSecondaryColor(mode == "Dark"), + onPrimary: MihColors.getPrimaryColor(mode == "Dark"), + secondary: MihColors.getPrimaryColor(mode == "Dark"), + onSecondary: MihColors.getSecondaryColor(mode == "Dark"), + error: MihColors.getRedColor(mode == "Dark"), + onError: MihColors.getPrimaryColor(mode == "Dark"), + surface: MihColors.getPrimaryColor(mode == "Dark"), + onSurface: MihColors.getSecondaryColor(mode == "Dark"), + ), + datePickerTheme: DatePickerThemeData( + backgroundColor: MihColors.getPrimaryColor(mode == "Dark"), + headerBackgroundColor: MihColors.getSecondaryColor(mode == "Dark"), + headerForegroundColor: MihColors.getPrimaryColor(mode == "Dark"), + ), + appBarTheme: AppBarTheme( + color: MihColors.getSecondaryColor(mode == "Dark"), + foregroundColor: MihColors.getPrimaryColor(mode == "Dark"), + titleTextStyle: TextStyle( + color: MihColors.getPrimaryColor(mode == "Dark"), + fontSize: 25, + fontWeight: FontWeight.bold, ), - datePickerTheme: DatePickerThemeData( - backgroundColor: MihColors.getPrimaryColor(mode == "Dark"), - headerBackgroundColor: MihColors.getSecondaryColor(mode == "Dark"), - headerForegroundColor: MihColors.getPrimaryColor(mode == "Dark"), - ), - appBarTheme: AppBarTheme( + ), + floatingActionButtonTheme: FloatingActionButtonThemeData( + backgroundColor: MihColors.getSecondaryColor(mode == "Dark"), + foregroundColor: MihColors.getPrimaryColor(mode == "Dark"), + extendedTextStyle: + TextStyle(color: MihColors.getPrimaryColor(mode == "Dark")), + ), + drawerTheme: DrawerThemeData( + backgroundColor: MihColors.getPrimaryColor(mode == "Dark"), + ), + // Text selection / cursor color + textSelectionTheme: TextSelectionThemeData( + cursorColor: MihColors.getPrimaryColor(mode == "Dark"), + selectionColor: + MihColors.getPrimaryColor(mode == "Dark").withOpacity(0.25), + selectionHandleColor: MihColors.getPrimaryColor(mode == "Dark"), + ), + tooltipTheme: TooltipThemeData( + decoration: BoxDecoration( color: MihColors.getSecondaryColor(mode == "Dark"), - foregroundColor: MihColors.getPrimaryColor(mode == "Dark"), - titleTextStyle: TextStyle( + borderRadius: BorderRadius.circular(6), + border: Border.all( + width: 1.0, color: MihColors.getPrimaryColor(mode == "Dark"), - fontSize: 25, - fontWeight: FontWeight.bold, ), + boxShadow: [ + BoxShadow( + color: + MihColors.getPrimaryColor(mode == "Dark").withOpacity(0.18), + blurRadius: 6, + offset: const Offset(0, 2), + ), + ], ), - floatingActionButtonTheme: FloatingActionButtonThemeData( - backgroundColor: MihColors.getSecondaryColor(mode == "Dark"), - foregroundColor: MihColors.getPrimaryColor(mode == "Dark"), - extendedTextStyle: - TextStyle(color: MihColors.getPrimaryColor(mode == "Dark")), + textStyle: TextStyle( + color: MihColors.getPrimaryColor(mode == "Dark"), + fontSize: 13, + height: 1.2, ), - drawerTheme: DrawerThemeData( - backgroundColor: MihColors.getPrimaryColor(mode == "Dark"), - )); + waitDuration: const Duration(milliseconds: 500), + showDuration: const Duration(seconds: 3), + preferBelow: true, + verticalOffset: 24, + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), + triggerMode: TooltipTriggerMode.longPress, + ), + // // Input decoration (text fields) theme + // inputDecorationTheme: InputDecorationTheme( + // filled: true, + // fillColor: mode == "Dark" + // ? MihColors.getPrimaryColor(true).withOpacity(0.06) + // : MihColors.getPrimaryColor(false).withOpacity(0.03), + // contentPadding: + // const EdgeInsets.symmetric(horizontal: 12, vertical: 10), + // border: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // borderSide: + // BorderSide(color: MihColors.getSecondaryColor(mode == "Dark")), + // ), + // enabledBorder: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // borderSide: BorderSide( + // color: + // MihColors.getSecondaryColor(mode == "Dark").withOpacity(0.6)), + // ), + // focusedBorder: OutlineInputBorder( + // borderRadius: BorderRadius.circular(8), + // borderSide: BorderSide( + // color: MihColors.getSecondaryColor(mode == "Dark"), width: 2), + // ), + // hintStyle: TextStyle( + // color: + // MihColors.getSecondaryColor(mode == "Dark").withOpacity(0.7)), + // labelStyle: + // TextStyle(color: MihColors.getSecondaryColor(mode == "Dark")), + // errorStyle: TextStyle(color: MihColors.getRedColor(mode == "Dark")), + // ), + ); } String getPlatform() { diff --git a/Frontend/lib/mih_packages/mzansi_ai/mzansi_ai.dart b/Frontend/lib/mih_packages/mzansi_ai/mzansi_ai.dart index 1d694165..87813973 100644 --- a/Frontend/lib/mih_packages/mzansi_ai/mzansi_ai.dart +++ b/Frontend/lib/mih_packages/mzansi_ai/mzansi_ai.dart @@ -5,6 +5,7 @@ import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_ import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_ai_provider.dart'; import 'package:mzansi_innovation_hub/mih_packages/mzansi_ai/package_tools/ai_chat.dart'; import 'package:flutter/material.dart'; +import 'package:mzansi_innovation_hub/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart'; import 'package:provider/provider.dart'; class MzansiAi extends StatefulWidget { @@ -36,6 +37,9 @@ class _MzansiAiState extends State { temp[const Icon(Icons.chat)] = () { context.read().setToolIndex(0); }; + temp[const Icon(Icons.chat)] = () { + context.read().setToolIndex(1); + }; return MihPackageTools( tools: temp, @@ -46,6 +50,7 @@ class _MzansiAiState extends State { List getToolBody() { List toolBodies = [ AiChat(), + MihAiChat(), ]; return toolBodies; } @@ -53,6 +58,7 @@ class _MzansiAiState extends State { List getToolTitle() { List toolTitles = [ "Ask Mzansi", + "New Chat", ]; return toolTitles; } diff --git a/Frontend/lib/mih_packages/mzansi_ai/package_tools/ai_chat.dart b/Frontend/lib/mih_packages/mzansi_ai/package_tools/ai_chat.dart index c380456f..61c9dbc9 100644 --- a/Frontend/lib/mih_packages/mzansi_ai/package_tools/ai_chat.dart +++ b/Frontend/lib/mih_packages/mzansi_ai/package_tools/ai_chat.dart @@ -598,9 +598,6 @@ class _AiChatState extends State { _voicesString = _voices.map((_voice) => _voice["name"] as String).toList(); _voicesString.sort(); - // print( - // "=================== Voices ===================\n$_voicesString"); - setTtsVoice(_voicesString.first); }); } catch (e) { diff --git a/Frontend/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart b/Frontend/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart new file mode 100644 index 00000000..5865ff8e --- /dev/null +++ b/Frontend/lib/mih_packages/mzansi_ai/package_tools/mih_ai_chat.dart @@ -0,0 +1,286 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_ai_toolkit/flutter_ai_toolkit.dart'; +import 'package:flutter_speed_dial/flutter_speed_dial.dart'; +import 'package:flutter_tts/flutter_tts.dart'; +import 'package:ken_logger/ken_logger.dart'; +import 'package:mzansi_innovation_hub/main.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_floating_menu.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_package_components/mih_icons.dart'; +import 'package:mzansi_innovation_hub/mih_components/mih_providers/mzansi_ai_provider.dart'; +import 'package:mzansi_innovation_hub/mih_config/mih_colors.dart'; +import 'package:provider/provider.dart'; + +class MihAiChat extends StatefulWidget { + const MihAiChat({super.key}); + + @override + State createState() => _MihAiChatState(); +} + +class _MihAiChatState extends State { + bool ttsOn = false; + final FlutterTts _flutterTts = FlutterTts(); + + Widget noMessagescDisplay() { + return Center( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 10.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(height: 50), + Icon( + MihIcons.mzansiAi, + size: 165, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + const SizedBox(height: 10), + Text( + "Mzansi AI is here to help", + textAlign: TextAlign.center, + overflow: TextOverflow.visible, + style: TextStyle( + fontSize: 25, + fontWeight: FontWeight.bold, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + ), + const SizedBox(height: 25), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.normal, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + children: [ + TextSpan( + text: + "Send us a message and we'll try our best to assist you"), + ], + ), + ), + ), + Center( + child: RichText( + textAlign: TextAlign.center, + text: TextSpan( + style: TextStyle( + fontSize: 20, + fontWeight: FontWeight.normal, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == "Dark"), + ), + children: [ + TextSpan(text: "Press "), + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Icon( + Icons.menu, + size: 20, + color: MihColors.getSecondaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + ), + TextSpan(text: " to start a new chat or read last message"), + ], + ), + ), + ), + ], + ), + ), + ); + } + + void resetChat(MzansiAiProvider aiProvider) { + aiProvider.ollamaProvider.resetChat(); + } + + void speakLastMessage(MzansiAiProvider aiProvider) { + final history = aiProvider.ollamaProvider.history; + if (history.isNotEmpty) { + final historyList = history.toList(); + for (int i = historyList.length - 1; i >= 0; i--) { + if (historyList[i].origin == MessageOrigin.llm && + historyList[i].text != null && + historyList[i].text!.isNotEmpty) { + _flutterTts.speak(historyList[i].text!); + return; + } + } + } + } + + void stopTTS() { + _flutterTts.stop(); + } + + Future initTts() async { + List _voices = []; + List _voicesString = []; + // await _flutterTts.setLanguage("en-US"); + await _flutterTts.setSpeechRate(1); + _flutterTts.getVoices.then( + (data) { + try { + _voices = List.from(data); + + setState(() { + _voices = _voices + .where( + (_voice) => _voice["name"].toLowerCase().contains("en-us")) + .toList(); + _voicesString = + _voices.map((_voice) => _voice["name"] as String).toList(); + _voicesString.sort(); + _flutterTts.setVoice( + { + "name": _voicesString.first, + "locale": _voices + .where((_voice) => + _voice["name"].contains(_voicesString.first)) + .first["locale"] + }, + ); + }); + } catch (e) { + print(e); + } + }, + ); + _flutterTts.setStartHandler(() { + setState(() { + ttsOn = true; + }); + }); + + _flutterTts.setCompletionHandler(() { + setState(() { + ttsOn = false; + }); + }); + + _flutterTts.setErrorHandler((message) { + setState(() { + ttsOn = false; + }); + }); + } + + @override + void initState() { + super.initState(); + initTts(); + } + + @override + void dispose() { + _flutterTts.stop(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Consumer( + builder: (BuildContext context, MzansiAiProvider mzansiAiProvider, + Widget? child) { + bool hasHistory = mzansiAiProvider.ollamaProvider.history.isNotEmpty; + KenLogger.success("has history: $hasHistory"); + KenLogger.success( + "length: ${mzansiAiProvider.ollamaProvider.history.length}"); + return Stack( + children: [ + LlmChatView( + provider: mzansiAiProvider.ollamaProvider, + // welcomeMessage: + // "Mzansi AI is here to help. Send us a messahe and we'll try our best to assist you.", + enableAttachments: false, + enableVoiceNotes: true, + style: mzansiAiProvider.getChatStyle(context), + // suggestions: [ + // "What is mih all about?", + // "What are the features of MIH?" + // ], + ), + if (hasHistory) + Positioned( + right: 10, + bottom: 80, + child: MihFloatingMenu( + animatedIcon: AnimatedIcons.menu_close, + children: [ + SpeedDialChild( + child: Icon( + Icons.refresh, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + label: "New Chat", + labelBackgroundColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + labelStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontWeight: FontWeight.bold, + ), + backgroundColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onTap: () { + resetChat(mzansiAiProvider); + }, + ), + SpeedDialChild( + child: Icon( + !ttsOn ? Icons.volume_up : Icons.volume_off, + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + ), + label: !ttsOn ? "Read last Message" : "Stop Reading", + labelBackgroundColor: MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + labelStyle: TextStyle( + color: MihColors.getPrimaryColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + fontWeight: FontWeight.bold, + ), + backgroundColor: !ttsOn + ? MihColors.getGreenColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark") + : MihColors.getRedColor( + MzansiInnovationHub.of(context)!.theme.mode == + "Dark"), + onTap: () { + KenLogger.success("Button pressed: TtsOn - $ttsOn"); + if (!ttsOn) { + speakLastMessage(mzansiAiProvider); + } else { + stopTTS(); + } + }, + ), + ], + ), + ), + if (!hasHistory) noMessagescDisplay(), + ], + ); + }, + ); + } +} diff --git a/Frontend/linux/flutter/generated_plugin_registrant.cc b/Frontend/linux/flutter/generated_plugin_registrant.cc index 88c7a8bd..4f4c5872 100644 --- a/Frontend/linux/flutter/generated_plugin_registrant.cc +++ b/Frontend/linux/flutter/generated_plugin_registrant.cc @@ -7,16 +7,24 @@ #include "generated_plugin_registrant.h" #include +#include #include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) file_saver_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin"); file_saver_plugin_register_with_registrar(file_saver_registrar); + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); g_autoptr(FlPluginRegistrar) printing_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "PrintingPlugin"); printing_plugin_register_with_registrar(printing_registrar); + g_autoptr(FlPluginRegistrar) record_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "RecordLinuxPlugin"); + record_linux_plugin_register_with_registrar(record_linux_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/Frontend/linux/flutter/generated_plugins.cmake b/Frontend/linux/flutter/generated_plugins.cmake index 561713c0..8ee15a6f 100644 --- a/Frontend/linux/flutter/generated_plugins.cmake +++ b/Frontend/linux/flutter/generated_plugins.cmake @@ -4,7 +4,9 @@ list(APPEND FLUTTER_PLUGIN_LIST file_saver + file_selector_linux printing + record_linux url_launcher_linux ) diff --git a/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift b/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift index d79f29c2..3cef27da 100644 --- a/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/Frontend/macos/Flutter/GeneratedPluginRegistrant.swift @@ -9,6 +9,10 @@ import app_settings import device_info_plus import file_picker import file_saver +import file_selector_macos +import firebase_app_check +import firebase_auth +import firebase_core import flutter_tts import geolocator_apple import local_auth_darwin @@ -16,6 +20,7 @@ import mobile_scanner import package_info_plus import path_provider_foundation import printing +import record_macos import screen_brightness_macos import share_plus import shared_preferences_foundation @@ -29,6 +34,10 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) + FLTFirebaseAppCheckPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAppCheckPlugin")) + FLTFirebaseAuthPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAuthPlugin")) + FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin")) FlutterTtsPlugin.register(with: registry.registrar(forPlugin: "FlutterTtsPlugin")) GeolocatorPlugin.register(with: registry.registrar(forPlugin: "GeolocatorPlugin")) FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin")) @@ -36,6 +45,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) PrintingPlugin.register(with: registry.registrar(forPlugin: "PrintingPlugin")) + RecordMacOsPlugin.register(with: registry.registrar(forPlugin: "RecordMacOsPlugin")) ScreenBrightnessMacosPlugin.register(with: registry.registrar(forPlugin: "ScreenBrightnessMacosPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) diff --git a/Frontend/pubspec.lock b/Frontend/pubspec.lock index 697e5b35..e6a2d08a 100644 --- a/Frontend/pubspec.lock +++ b/Frontend/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "82.0.0" + _flutterfire_internals: + dependency: transitive + description: + name: _flutterfire_internals + sha256: "8a1f5f3020ef2a74fb93f7ab3ef127a8feea33a7a2276279113660784ee7516a" + url: "https://pub.dev" + source: hosted + version: "1.3.64" analyzer: dependency: transitive description: @@ -201,6 +209,46 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" + camera: + dependency: transitive + description: + name: camera + sha256: "87a27e0553e3432119c1c2f6e4b9a1bbf7d2c660552b910bfa59185a9facd632" + url: "https://pub.dev" + source: hosted + version: "0.11.2+1" + camera_android_camerax: + dependency: transitive + description: + name: camera_android_camerax + sha256: "58b8fe843a3c83fd1273c00cb35f5a8ae507f6cc9b2029bcf7e2abba499e28d8" + url: "https://pub.dev" + source: hosted + version: "0.6.19+1" + camera_avfoundation: + dependency: transitive + description: + name: camera_avfoundation + sha256: "951ef122d01ebba68b7a54bfe294e8b25585635a90465c311b2f875ae72c412f" + url: "https://pub.dev" + source: hosted + version: "0.9.21+2" + camera_platform_interface: + dependency: transitive + description: + name: camera_platform_interface + sha256: "98cfc9357e04bad617671b4c1f78a597f25f08003089dd94050709ae54effc63" + url: "https://pub.dev" + source: hosted + version: "2.12.0" + camera_web: + dependency: transitive + description: + name: camera_web + sha256: "595f28c89d1fb62d77c73c633193755b781c6d2e0ebcd8dc25b763b514e6ba8f" + url: "https://pub.dev" + source: hosted + version: "0.3.5" characters: dependency: transitive description: @@ -385,22 +433,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" - fetch_api: - dependency: transitive - description: - name: fetch_api - sha256: "24cbd5616f3d4008c335c197bb90bfa0eb43b9e55c6de5c60d1f805092636034" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - fetch_client: - dependency: transitive - description: - name: fetch_client - sha256: "375253f4efe64303c793fb17fe90771c591320b2ae11fb29cb5b406cc8533c00" - url: "https://pub.dev" - source: hosted - version: "1.1.4" ffi: dependency: transitive description: @@ -433,6 +465,150 @@ packages: url: "https://pub.dev" source: hosted version: "0.3.1" + file_selector: + dependency: transitive + description: + name: file_selector + sha256: "5f1d15a7f17115038f433d1b0ea57513cc9e29a9d5338d166cb0bef3fa90a7a0" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + file_selector_android: + dependency: transitive + description: + name: file_selector_android + sha256: "1ce58b609289551f8ec07265476720e77d19764339cc1d8e4df3c4d34dac6499" + url: "https://pub.dev" + source: hosted + version: "0.5.1+17" + file_selector_ios: + dependency: transitive + description: + name: file_selector_ios + sha256: fe9f52123af16bba4ad65bd7e03defbbb4b172a38a8e6aaa2a869a0c56a5f5fb + url: "https://pub.dev" + source: hosted + version: "0.5.3+2" + file_selector_linux: + dependency: transitive + description: + name: file_selector_linux + sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" + url: "https://pub.dev" + source: hosted + version: "0.9.3+2" + file_selector_macos: + dependency: transitive + description: + name: file_selector_macos + sha256: "19124ff4a3d8864fdc62072b6a2ef6c222d55a3404fe14893a3c02744907b60c" + url: "https://pub.dev" + source: hosted + version: "0.9.4+4" + file_selector_platform_interface: + dependency: transitive + description: + name: file_selector_platform_interface + sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b + url: "https://pub.dev" + source: hosted + version: "2.6.2" + file_selector_web: + dependency: transitive + description: + name: file_selector_web + sha256: c4c0ea4224d97a60a7067eca0c8fd419e708ff830e0c83b11a48faf566cec3e7 + url: "https://pub.dev" + source: hosted + version: "0.9.4+2" + file_selector_windows: + dependency: transitive + description: + name: file_selector_windows + sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" + url: "https://pub.dev" + source: hosted + version: "0.9.3+4" + firebase_ai: + dependency: transitive + description: + name: firebase_ai + sha256: "6bffa52bc4b9557b73f59de12ec36349bde76f772431d0702c7ecb4bfeeeb21b" + url: "https://pub.dev" + source: hosted + version: "3.5.0" + firebase_app_check: + dependency: transitive + description: + name: firebase_app_check + sha256: "4d00b502f510ee97cdb395e95a31a8b871fc96cb917ffc60591528d3c9735986" + url: "https://pub.dev" + source: hosted + version: "0.4.1+2" + firebase_app_check_platform_interface: + dependency: transitive + description: + name: firebase_app_check_platform_interface + sha256: "7d104d01b00e5dec367dc79184ad99bd1941f2d839b5ef41156b2330c18af13f" + url: "https://pub.dev" + source: hosted + version: "0.2.1+2" + firebase_app_check_web: + dependency: transitive + description: + name: firebase_app_check_web + sha256: "885a1a7b8e33c52afaf9c5d75eca616ae310d6ea90322e9a462f8c154ad16b64" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + firebase_auth: + dependency: transitive + description: + name: firebase_auth + sha256: e54fb3ba57de041d832574126a37726eedf0f57400869f1942b0ca8ce4a6e209 + url: "https://pub.dev" + source: hosted + version: "6.1.2" + firebase_auth_platform_interface: + dependency: transitive + description: + name: firebase_auth_platform_interface + sha256: "421f95dc553cb283ed9d4d140e719800c0331d49ed37b962e513c9d1d61b090b" + url: "https://pub.dev" + source: hosted + version: "8.1.4" + firebase_auth_web: + dependency: transitive + description: + name: firebase_auth_web + sha256: a064ffee202f7d42d62e2c01775899d4ffcb83c602af07632f206acd46a0964e + url: "https://pub.dev" + source: hosted + version: "6.1.0" + firebase_core: + dependency: transitive + description: + name: firebase_core + sha256: "1f2dfd9f535d81f8b06d7a50ecda6eac1e6922191ed42e09ca2c84bd2288927c" + url: "https://pub.dev" + source: hosted + version: "4.2.1" + firebase_core_platform_interface: + dependency: transitive + description: + name: firebase_core_platform_interface + sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + firebase_core_web: + dependency: transitive + description: + name: firebase_core_web + sha256: ff18fabb0ad0ed3595d2f2c85007ecc794aadecdff5b3bb1460b7ee47cded398 + url: "https://pub.dev" + source: hosted + version: "3.3.0" fixnum: dependency: transitive description: @@ -454,6 +630,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_ai_toolkit: + dependency: "direct main" + description: + name: flutter_ai_toolkit + sha256: b653814f9aac05d84f15db0daf1bf90e9bd9177abeba378c6f1d4a75bb2099a5 + url: "https://pub.dev" + source: hosted + version: "0.10.0" flutter_cache_manager: dependency: transitive description: @@ -478,6 +662,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.6.15" + flutter_context_menu: + dependency: transitive + description: + name: flutter_context_menu + sha256: "79fe00cd7e3ac3840a552cb3e653d8eb61d827b6c04c559e219973a1dc769165" + url: "https://pub.dev" + source: hosted + version: "0.3.0" flutter_launcher_icons: dependency: "direct main" description: @@ -510,6 +702,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + flutter_markdown_plus: + dependency: transitive + description: + name: flutter_markdown_plus + sha256: "7f349c075157816da399216a4127096108fd08e1ac931e34e72899281db4113c" + url: "https://pub.dev" + source: hosted + version: "1.0.5" flutter_math_fork: dependency: transitive description: @@ -534,6 +734,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.1" + flutter_picture_taker: + dependency: transitive + description: + name: flutter_picture_taker + sha256: d24d4c10e42324832b550bd59d1fe84129e860b75b4b2d57d6b398a41fd5dc9a + url: "https://pub.dev" + source: hosted + version: "0.2.0" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -588,10 +796,10 @@ packages: dependency: transitive description: name: freezed_annotation - sha256: c2e2d632dd9b8a2b7751117abcfc2b4888ecfe181bd9fca7170d9ef02e595fe2 + sha256: "7294967ff0a6d98638e7acb774aac3af2550777accd8149c90af5b014e6d44d8" url: "https://pub.dev" source: hosted - version: "2.4.4" + version: "3.1.0" frontend_server_client: dependency: transitive description: @@ -680,6 +888,14 @@ packages: url: "https://pub.dev" source: hosted version: "16.1.0" + google_fonts: + dependency: transitive + description: + name: google_fonts + sha256: "517b20870220c48752eafa0ba1a797a092fb22df0d89535fd9991e86ee2cdd9c" + url: "https://pub.dev" + source: hosted + version: "6.3.2" google_mobile_ads: dependency: "direct main" description: @@ -744,6 +960,70 @@ packages: url: "https://pub.dev" source: hosted version: "4.5.4" + image_picker: + dependency: transitive + description: + name: image_picker + sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + image_picker_android: + dependency: transitive + description: + name: image_picker_android + sha256: "28f3987ca0ec702d346eae1d90eda59603a2101b52f1e234ded62cff1d5cfa6e" + url: "https://pub.dev" + source: hosted + version: "0.8.13+1" + image_picker_for_web: + dependency: transitive + description: + name: image_picker_for_web + sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + image_picker_ios: + dependency: transitive + description: + name: image_picker_ios + sha256: eb06fe30bab4c4497bad449b66448f50edcc695f1c59408e78aa3a8059eb8f0e + url: "https://pub.dev" + source: hosted + version: "0.8.13" + image_picker_linux: + dependency: transitive + description: + name: image_picker_linux + sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" + url: "https://pub.dev" + source: hosted + version: "0.2.2" + image_picker_macos: + dependency: transitive + description: + name: image_picker_macos + sha256: d58cd9d67793d52beefd6585b12050af0a7663c0c2a6ece0fb110a35d6955e04 + url: "https://pub.dev" + source: hosted + version: "0.2.2" + image_picker_platform_interface: + dependency: transitive + description: + name: image_picker_platform_interface + sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" + url: "https://pub.dev" + source: hosted + version: "2.11.1" + image_picker_windows: + dependency: transitive + description: + name: image_picker_windows + sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae + url: "https://pub.dev" + source: hosted + version: "0.2.2" intl: dependency: "direct main" description: @@ -872,6 +1152,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.0" + markdown: + dependency: transitive + description: + name: markdown + sha256: "935e23e1ff3bc02d390bad4d4be001208ee92cc217cb5b5a6c19bc14aaa318c1" + url: "https://pub.dev" + source: hosted + version: "7.3.0" matcher: dependency: transitive description: @@ -956,10 +1244,10 @@ packages: dependency: "direct main" description: name: ollama_dart - sha256: "4e40bc499b6fe46ba54a004d2da601c40bd73d66e3f18cf7b03225ccf3d481a6" + sha256: "55a45e381f3cf24791df510e287f353e776d376f556d34ba49b09bc918eee319" url: "https://pub.dev" source: hosted - version: "0.2.2+1" + version: "0.2.5" os_detect: dependency: transitive description: @@ -1176,6 +1464,70 @@ packages: url: "https://pub.dev" source: hosted version: "3.0.2" + record: + dependency: transitive + description: + name: record + sha256: "6bad72fb3ea6708d724cf8b6c97c4e236cf9f43a52259b654efeb6fd9b737f1f" + url: "https://pub.dev" + source: hosted + version: "6.1.2" + record_android: + dependency: transitive + description: + name: record_android + sha256: fb54ee4e28f6829b8c580252a9ef49d9c549cfd263b0660ad7eeac0908658e9f + url: "https://pub.dev" + source: hosted + version: "1.4.4" + record_ios: + dependency: transitive + description: + name: record_ios + sha256: "765b42ac1be019b1674ddd809b811fc721fe5a93f7bb1da7803f0d16772fd6d7" + url: "https://pub.dev" + source: hosted + version: "1.1.4" + record_linux: + dependency: transitive + description: + name: record_linux + sha256: "235b1f1fb84e810f8149cc0c2c731d7d697f8d1c333b32cb820c449bf7bb72d8" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + record_macos: + dependency: transitive + description: + name: record_macos + sha256: "842ea4b7e95f4dd237aacffc686d1b0ff4277e3e5357865f8d28cd28bc18ed95" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + record_platform_interface: + dependency: transitive + description: + name: record_platform_interface + sha256: b0065fdf1ec28f5a634d676724d388a77e43ce7646fb049949f58c69f3fcb4ed + url: "https://pub.dev" + source: hosted + version: "1.4.0" + record_web: + dependency: transitive + description: + name: record_web + sha256: "20ac10d56514cb9f8cecc8f3579383084fdfb43b0d04e05a95244d0d76091d90" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + record_windows: + dependency: transitive + description: + name: record_windows + sha256: "223258060a1d25c62bae18282c16783f28581ec19401d17e56b5205b9f039d78" + url: "https://pub.dev" + source: hosted + version: "1.0.7" redacted: dependency: "direct main" description: @@ -1613,6 +1965,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.2.2" + universal_platform: + dependency: transitive + description: + name: universal_platform + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" + url: "https://pub.dev" + source: hosted + version: "1.1.0" upgrader: dependency: "direct main" description: @@ -1757,6 +2117,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + waveform_flutter: + dependency: transitive + description: + name: waveform_flutter + sha256: "08c9e98d4cf119428d8b3c083ed42c11c468623eaffdf30420ae38e36662922a" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + waveform_recorder: + dependency: transitive + description: + name: waveform_recorder + sha256: "1ca0a19b143d1bdef2adfb3d28f0627c18aee5285235c8cf81a89bf29a0420e1" + url: "https://pub.dev" + source: hosted + version: "1.8.0" web: dependency: transitive description: diff --git a/Frontend/pubspec.yaml b/Frontend/pubspec.yaml index 9969b895..243368fb 100644 --- a/Frontend/pubspec.yaml +++ b/Frontend/pubspec.yaml @@ -59,6 +59,7 @@ dependencies: screenshot: ^3.0.0 file_saver: ^0.3.1 provider: ^6.1.5+1 + flutter_ai_toolkit: ^0.10.0 dev_dependencies: flutter_test: diff --git a/Frontend/windows/flutter/generated_plugin_registrant.cc b/Frontend/windows/flutter/generated_plugin_registrant.cc index 9dbdf6de..ad71ace9 100644 --- a/Frontend/windows/flutter/generated_plugin_registrant.cc +++ b/Frontend/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,15 @@ #include "generated_plugin_registrant.h" #include +#include +#include +#include #include #include #include #include #include +#include #include #include #include @@ -20,6 +24,12 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { FileSaverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("FileSaverPlugin")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); + FirebaseAuthPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseAuthPluginCApi")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); FlDownloaderPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("FlDownloaderPluginCApi")); FlutterTtsPluginRegisterWithRegistrar( @@ -30,6 +40,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) { registry->GetRegistrarForPlugin("LocalAuthPlugin")); PrintingPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("PrintingPlugin")); + RecordWindowsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("RecordWindowsPluginCApi")); ScreenBrightnessWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenBrightnessWindowsPlugin")); SharePlusWindowsPluginCApiRegisterWithRegistrar( diff --git a/Frontend/windows/flutter/generated_plugins.cmake b/Frontend/windows/flutter/generated_plugins.cmake index 5d18711f..57f48306 100644 --- a/Frontend/windows/flutter/generated_plugins.cmake +++ b/Frontend/windows/flutter/generated_plugins.cmake @@ -4,11 +4,15 @@ list(APPEND FLUTTER_PLUGIN_LIST file_saver + file_selector_windows + firebase_auth + firebase_core fl_downloader flutter_tts geolocator_windows local_auth_windows printing + record_windows screen_brightness_windows share_plus syncfusion_pdfviewer_windows