diff --git a/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart b/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart index ab73c6212..2c817cf01 100644 --- a/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart +++ b/lib/features/composer/presentation/extensions/ai_scribe/handle_ai_scribe_in_composer_extension.dart @@ -245,7 +245,7 @@ extension HandleAiScribeInComposerExtension on ComposerController { final fullText = await _getTextOnlyContentInEditor(); - await AiScribeModalManager.showAIScribeMenuModal( + await AiScribeModalManager.showAIScribeModal( imagePaths: imagePaths, availableCategories: AIScribeMenuCategory.values, buttonPosition: position, diff --git a/scribe/lib/scribe.dart b/scribe/lib/scribe.dart index 9e9327048..bd891d41c 100644 --- a/scribe/lib/scribe.dart +++ b/scribe/lib/scribe.dart @@ -40,12 +40,14 @@ export 'scribe/ai/presentation/widgets/items/ai_scribe_menu_submenu_icon.dart'; export 'scribe/ai/presentation/widgets/items/ai_scribe_menu_text.dart'; export 'scribe/ai/presentation/widgets/modal/ai_scribe_modal_widget.dart'; export 'scribe/ai/presentation/widgets/modal/ai_scribe_suggestion_widget.dart'; +export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_improve_button.dart'; export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_error.dart'; export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_header.dart'; export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_loading.dart'; export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success.dart'; export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_list_actions.dart'; export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_toolbar.dart'; +export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_actions.dart'; export 'scribe/ai/presentation/widgets/overlay/ai_selection_overlay.dart'; export 'scribe/ai/presentation/widgets/search/ai_scribe_bar.dart'; export 'scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart'; diff --git a/scribe/lib/scribe/ai/l10n/intl_ar.arb b/scribe/lib/scribe/ai/l10n/intl_ar.arb index 22d04af50..48566aa08 100644 --- a/scribe/lib/scribe/ai/l10n/intl_ar.arb +++ b/scribe/lib/scribe/ai/l10n/intl_ar.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "تحسين", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "إنشاء الاستجابة", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_de.arb b/scribe/lib/scribe/ai/l10n/intl_de.arb index 3fac7b8d3..f2c4f1274 100644 --- a/scribe/lib/scribe/ai/l10n/intl_de.arb +++ b/scribe/lib/scribe/ai/l10n/intl_de.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Verbessern", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "Antwort wird generiert", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_en.arb b/scribe/lib/scribe/ai/l10n/intl_en.arb index 539732622..c2db969ee 100644 --- a/scribe/lib/scribe/ai/l10n/intl_en.arb +++ b/scribe/lib/scribe/ai/l10n/intl_en.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Improve", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "Generating response", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_fr.arb b/scribe/lib/scribe/ai/l10n/intl_fr.arb index 0537deb0a..94d2737e3 100644 --- a/scribe/lib/scribe/ai/l10n/intl_fr.arb +++ b/scribe/lib/scribe/ai/l10n/intl_fr.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Améliorer", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "Génération de la réponse", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_it.arb b/scribe/lib/scribe/ai/l10n/intl_it.arb index 0ab3e93d4..85a9edd3b 100644 --- a/scribe/lib/scribe/ai/l10n/intl_it.arb +++ b/scribe/lib/scribe/ai/l10n/intl_it.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Migliora", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "Generazione della risposta", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_messages.arb b/scribe/lib/scribe/ai/l10n/intl_messages.arb index 6ec7af566..a7dac6e9b 100644 --- a/scribe/lib/scribe/ai/l10n/intl_messages.arb +++ b/scribe/lib/scribe/ai/l10n/intl_messages.arb @@ -1,5 +1,5 @@ { - "@@last_modified": "2026-02-04T15:15:31.191557", + "@@last_modified": "2026-02-09T16:58:15.727654", "categoryCorrectGrammar": "Correct", "@categoryCorrectGrammar": { "type": "text", @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Improve", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "aiAssistant": "AI assistant", "@aiAssistant": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_ru.arb b/scribe/lib/scribe/ai/l10n/intl_ru.arb index eb54a66f4..bde115066 100644 --- a/scribe/lib/scribe/ai/l10n/intl_ru.arb +++ b/scribe/lib/scribe/ai/l10n/intl_ru.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Улучшить", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "Генерация ответа", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/l10n/intl_vi.arb b/scribe/lib/scribe/ai/l10n/intl_vi.arb index 7d520cca9..3c5f6eb4f 100644 --- a/scribe/lib/scribe/ai/l10n/intl_vi.arb +++ b/scribe/lib/scribe/ai/l10n/intl_vi.arb @@ -102,6 +102,12 @@ "placeholders_order": [], "placeholders": {} }, + "improve": "Cải thiện", + "@improve": { + "type": "text", + "placeholders_order": [], + "placeholders": {} + }, "generatingResponse": "Tạo phản hồi", "@generatingResponse": { "type": "text", diff --git a/scribe/lib/scribe/ai/localizations/scribe_localizations.dart b/scribe/lib/scribe/ai/localizations/scribe_localizations.dart index 36efcbdb1..c7b684754 100644 --- a/scribe/lib/scribe/ai/localizations/scribe_localizations.dart +++ b/scribe/lib/scribe/ai/localizations/scribe_localizations.dart @@ -150,6 +150,13 @@ class ScribeLocalizations { ); } + String get improve { + return Intl.message( + 'Improve', + name: 'improve', + ); + } + String get aiAssistant { return Intl.message( 'AI assistant', diff --git a/scribe/lib/scribe/ai/presentation/styles/ai_scribe_styles.dart b/scribe/lib/scribe/ai/presentation/styles/ai_scribe_styles.dart index 87fa7ba59..c8848ff90 100644 --- a/scribe/lib/scribe/ai/presentation/styles/ai_scribe_styles.dart +++ b/scribe/lib/scribe/ai/presentation/styles/ai_scribe_styles.dart @@ -130,6 +130,7 @@ abstract final class AIScribeSizes { static const double scribeButtonRadius = 100; static const double aiAssistantIconRadius = 8; static const double contentCardRadius = 16; + static const double improveButtonBorderRadius = 4; // Width / height static const double menuItemHeight = 40; @@ -162,6 +163,7 @@ abstract final class AIScribeSizes { static const double modalSpacing = 26; static const double modalWithoutContentSpacing = 12; static const double keyboardSpacing = 12; + static const double successSpacing = 12; // Elevation static const double dialogElevation = 8; @@ -173,6 +175,10 @@ abstract final class AIScribeSizes { static const double aiAssistantIcon = 24; static const double bottomsheetIcon = 20; + // Button sizes + static const double minButtonWidth = 72; + static const double buttonHeight = 36; + // Padding static const EdgeInsetsGeometry menuItemPadding = EdgeInsetsDirectional.only(start: 16, end: 10); @@ -218,4 +224,7 @@ abstract final class AIScribeSizes { static const EdgeInsetsGeometry contentCardPadding = EdgeInsetsDirectional.all(16); + + static const EdgeInsetsGeometry improveButtonPadding = + EdgeInsetsDirectional.only(start: 14, top: 6, end: 10, bottom: 6); } diff --git a/scribe/lib/scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart b/scribe/lib/scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart index c032faedd..0b7ac1d48 100644 --- a/scribe/lib/scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart +++ b/scribe/lib/scribe/ai/presentation/utils/context_menu/context_submenu_controller.dart @@ -56,7 +56,7 @@ class ContextSubmenuController { final clampedLeft = finalLeft .clamp(0.0, math.max(0.0, screenWidth - submenuWidth)) .toDouble(); - final availableHeight = math.max(0.0, screenHeight - anchor.top); + final availableHeight = math.max(0.0, screenHeight - bottom); final finalHeight = math.min(submenuMaxHeight, availableHeight); _submenuEntry = OverlayEntry( diff --git a/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart b/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart index 677ec68ff..cbcba6979 100644 --- a/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart +++ b/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_modal_manager.dart @@ -6,7 +6,7 @@ import 'package:scribe/scribe.dart'; class AiScribeModalManager { AiScribeModalManager._(); - static Future showAIScribeMenuModal({ + static Future showAIScribeModal({ required bool isScribeMobile, required ImagePaths imagePaths, required List availableCategories, @@ -17,31 +17,16 @@ class AiScribeModalManager { ModalPlacement? preferredPlacement, ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center, }) async { - final AIAction? aiAction; - - if (isScribeMobile) { - aiAction = await showMobileAIScribeMenuModal( + final AIAction? aiAction = await showAIScribeMenuModal( + isScribeMobile: isScribeMobile, imagePaths: imagePaths, content: content, availableCategories: availableCategories, + buttonPosition: buttonPosition, + buttonSize: buttonSize, + preferredPlacement: preferredPlacement, + crossAxisAlignment: crossAxisAlignment, ); - } else { - final ContextSubmenuController submenuController = ContextSubmenuController(); - - aiAction = await Get.dialog( - AiScribeModalWidget( - imagePaths: imagePaths, - content: content, - availableCategories: availableCategories, - buttonPosition: buttonPosition, - buttonSize: buttonSize, - preferredPlacement: preferredPlacement, - crossAxisAlignment: crossAxisAlignment, - submenuController: submenuController, - ), - barrierColor: AIScribeColors.dialogBarrier, - ).whenComplete(submenuController.dispose); - } if (aiAction != null) { await showAIScribeSuggestionModal( @@ -58,6 +43,38 @@ class AiScribeModalManager { } } + static Future showAIScribeMenuModal({ + required bool isScribeMobile, + required ImagePaths imagePaths, + required List availableCategories, + String? content, + Offset? buttonPosition, + Size? buttonSize, + ModalPlacement? preferredPlacement, + ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center, + bool showCustomPromptBar = true, + }) async { + if (isScribeMobile) { + return await showMobileAIScribeMenuModal( + imagePaths: imagePaths, + content: content, + availableCategories: availableCategories, + showCustomPromptBar: showCustomPromptBar, + ); + } else { + return await showDesktopAIScribeMenuModal( + imagePaths: imagePaths, + content: content, + availableCategories: availableCategories, + buttonPosition: buttonPosition, + buttonSize: buttonSize, + preferredPlacement: preferredPlacement, + crossAxisAlignment: crossAxisAlignment, + showCustomPromptBar: showCustomPromptBar, + ); + } + } + static Future showAIScribeSuggestionModal({ required AIAction aiAction, required bool isScribeMobile, @@ -77,26 +94,52 @@ class AiScribeModalManager { onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction, ); } else { - await Get.dialog( - AiScribeSuggestionWidget( - aiAction: aiAction, + await showDesktopAIScribeSuggestionModal( + aiAction: aiAction, + imagePaths: imagePaths, + content: content, + buttonPosition: buttonPosition, + buttonSize: buttonSize, + preferredPlacement: preferredPlacement, + crossAxisAlignment: crossAxisAlignment, + onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction, + ); + } + } + + static Future showDesktopAIScribeMenuModal({ + required ImagePaths imagePaths, + required List availableCategories, + String? content, + Offset? buttonPosition, + Size? buttonSize, + ModalPlacement? preferredPlacement, + ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center, + bool showCustomPromptBar = true, + }) async { + final ContextSubmenuController submenuController = ContextSubmenuController(); + + return await Get.dialog( + AiScribeModalWidget( imagePaths: imagePaths, content: content, + availableCategories: availableCategories, buttonPosition: buttonPosition, buttonSize: buttonSize, preferredPlacement: preferredPlacement, crossAxisAlignment: crossAxisAlignment, - onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction, + submenuController: submenuController, + showCustomPromptBar: showCustomPromptBar, ), barrierColor: AIScribeColors.dialogBarrier, - ); - } + ).whenComplete(submenuController.dispose); } static Future showMobileAIScribeMenuModal({ required ImagePaths imagePaths, required List availableCategories, String? content, + bool showCustomPromptBar = true, }) async { final context = Get.context; @@ -110,10 +153,36 @@ class AiScribeModalManager { imagePaths: imagePaths, availableCategories: availableCategories, content: content, + showCustomPromptBar: showCustomPromptBar, ), ); } + static Future showDesktopAIScribeSuggestionModal({ + required AIAction aiAction, + required ImagePaths imagePaths, + required OnSelectAiScribeSuggestionAction onSelectAiScribeSuggestionAction, + String? content, + Offset? buttonPosition, + Size? buttonSize, + ModalPlacement? preferredPlacement, + ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center, + }) async { + await Get.dialog( + AiScribeSuggestionWidget( + aiAction: aiAction, + imagePaths: imagePaths, + content: content, + buttonPosition: buttonPosition, + buttonSize: buttonSize, + preferredPlacement: preferredPlacement, + crossAxisAlignment: crossAxisAlignment, + onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction, + ), + barrierColor: AIScribeColors.dialogBarrier, + ); + } + static Future showMobileAIScribeSuggestionModal({ required AIAction aiAction, required ImagePaths imagePaths, diff --git a/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_suggestion_state_mixin.dart b/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_suggestion_state_mixin.dart index 078874867..882daf35a 100644 --- a/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_suggestion_state_mixin.dart +++ b/scribe/lib/scribe/ai/presentation/utils/modal/ai_scribe_suggestion_state_mixin.dart @@ -7,6 +7,8 @@ import 'package:get/get.dart'; import 'package:scribe/scribe.dart'; import 'package:scribe/scribe/ai/data/network/ai_api_exception.dart'; +typedef OnLoadSuggestion = Future Function([AIAction? aiAction, String? content]); + mixin AiScribeSuggestionStateMixin on State { GenerateAITextInteractor? get interactor => _interactor; GenerateAITextInteractor? _interactor; @@ -37,7 +39,10 @@ mixin AiScribeSuggestionStateMixin on State { loadSuggestion(); } - Future loadSuggestion() async { + Future loadSuggestion([AIAction? newAiAction, String? newContent]) async { + final aiActionToSend = newAiAction ?? aiAction; + final contentToSend = newContent ?? content; + _suggestionState.value = dartz.Right(GenerateAITextLoading()); if (_interactor == null) { @@ -50,8 +55,8 @@ mixin AiScribeSuggestionStateMixin on State { } final result = await _interactor!.execute( - aiAction, - content, + aiActionToSend, + contentToSend, ); if (!mounted) return; diff --git a/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart b/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart index ee4c71222..d42d68605 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/button/inline_ai_assist_button.dart @@ -49,7 +49,7 @@ class InlineAiAssistButton extends StatelessWidget { final isScribeMobile = AiScribeMobileUtils.isScribeInMobileMode(context); - await AiScribeModalManager.showAIScribeMenuModal( + await AiScribeModalManager.showAIScribeModal( isScribeMobile: isScribeMobile, imagePaths: imagePaths, availableCategories: AIScribeMenuCategory.values, diff --git a/scribe/lib/scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart b/scribe/lib/scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart index f03dacb86..d6d5886b7 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart @@ -9,12 +9,14 @@ class AiScribeMobileActionsBottomSheet extends StatefulWidget { final ImagePaths imagePaths; final List availableCategories; final String? content; + final bool showCustomPromptBar; const AiScribeMobileActionsBottomSheet({ super.key, required this.imagePaths, required this.availableCategories, this.content, + this.showCustomPromptBar = true, }); @override @@ -153,6 +155,10 @@ class _AiScribeMobileActionsBottomSheetState } Widget _buildBottomBar(BuildContext context) { + if (!widget.showCustomPromptBar) { + return const SizedBox.shrink(); + } + return Column( mainAxisSize: MainAxisSize.min, children: [ diff --git a/scribe/lib/scribe/ai/presentation/widgets/modal/ai_scribe_modal_widget.dart b/scribe/lib/scribe/ai/presentation/widgets/modal/ai_scribe_modal_widget.dart index 04d48d42b..c51d98ca1 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/modal/ai_scribe_modal_widget.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/modal/ai_scribe_modal_widget.dart @@ -14,6 +14,7 @@ class AiScribeModalWidget extends StatelessWidget { final ModalPlacement? preferredPlacement; final ModalCrossAxisAlignment crossAxisAlignment; final ContextSubmenuController? submenuController; + final bool showCustomPromptBar; const AiScribeModalWidget({ super.key, @@ -25,6 +26,7 @@ class AiScribeModalWidget extends StatelessWidget { this.preferredPlacement, this.crossAxisAlignment = ModalCrossAxisAlignment.center, this.submenuController, + this.showCustomPromptBar = true, }); @override @@ -57,16 +59,17 @@ class AiScribeModalWidget extends StatelessWidget { ), ), ), - MouseRegion( - onEnter: (_) => submenuController?.hide(), - child: AIScribeBar( - imagePaths: imagePaths, - onCustomPrompt: (customPrompt) { - Navigator.of(context).pop(CustomPromptAction(customPrompt)); - submenuController?.hide(); - }, + if (showCustomPromptBar) + MouseRegion( + onEnter: (_) => submenuController?.hide(), + child: AIScribeBar( + imagePaths: imagePaths, + onCustomPrompt: (customPrompt) { + Navigator.of(context).pop(CustomPromptAction(customPrompt)); + submenuController?.hide(); + }, + ), ), - ), ], ), ); diff --git a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_improve_button.dart b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_improve_button.dart new file mode 100644 index 000000000..1a0e4f136 --- /dev/null +++ b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_improve_button.dart @@ -0,0 +1,69 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; +import 'package:flutter/material.dart'; +import 'package:scribe/scribe.dart'; + +class AiScribeImproveButton extends StatefulWidget { + final ImagePaths imagePaths; + final String suggestionText; + final OnLoadSuggestion onLoadSuggestion; + + const AiScribeImproveButton({ + super.key, + required this.imagePaths, + required this.suggestionText, + required this.onLoadSuggestion, + }); + + @override + State createState() => _AiScribeImproveButtonState(); +} + +class _AiScribeImproveButtonState extends State { + final GlobalKey _improveButtonKey = GlobalKey(); + + @override + Widget build(BuildContext context) { + return TMailButtonWidget( + key: _improveButtonKey, + text: ScribeLocalizations.of(context).improve, + padding: AIScribeSizes.improveButtonPadding, + borderRadius: AIScribeSizes.improveButtonBorderRadius, + backgroundColor: AppColor.colorBackgroundTagFilter.withValues(alpha: 0.08), + icon: widget.imagePaths.icChevronDown, + iconColor: AppColor.colorTextButtonHeaderThread, + iconAlignment: TextDirection.rtl, + onTapActionCallback: _handleImproveButtonTap, + ); + } + + Future _handleImproveButtonTap() async { + final renderBox = _improveButtonKey.currentContext?.findRenderObject(); + Offset? position; + Size? size; + + if (renderBox != null && renderBox is RenderBox) { + position = renderBox.localToGlobal(Offset.zero); + size = renderBox.size; + } + + final AIAction? aiAction = await AiScribeModalManager.showAIScribeMenuModal( + isScribeMobile: AiScribeMobileUtils.isScribeInMobileMode(context), + imagePaths: widget.imagePaths, + availableCategories: AIScribeMenuCategory.values, + content: widget.suggestionText, + buttonPosition: position, + buttonSize: size, + showCustomPromptBar: false, + ); + + if (!mounted) { + return; + } + + if (aiAction != null) { + widget.onLoadSuggestion(aiAction, widget.suggestionText); + } + } +} diff --git a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success.dart b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success.dart index 5eed49f3a..8969b83ad 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success.dart @@ -7,7 +7,7 @@ class AiScribeSuggestionSuccess extends StatelessWidget { final String suggestionText; final bool hasContent; final OnSelectAiScribeSuggestionAction onSelectAction; - final VoidCallback onLoadSuggestion; + final OnLoadSuggestion onLoadSuggestion; const AiScribeSuggestionSuccess({ super.key, diff --git a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_actions.dart b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_actions.dart new file mode 100644 index 000000000..45df35bf3 --- /dev/null +++ b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_actions.dart @@ -0,0 +1,89 @@ +import 'package:core/presentation/extensions/color_extension.dart'; +import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/dialog/confirm_dialog_button.dart'; +import 'package:flutter/material.dart'; +import 'package:scribe/scribe.dart'; + +class AiScribeSuggestionSuccessActions extends StatelessWidget { + final ImagePaths imagePaths; + final String suggestionText; + final bool hasContent; + final OnSelectAiScribeSuggestionAction onSelectAction; + final OnLoadSuggestion onLoadSuggestion; + + const AiScribeSuggestionSuccessActions({ + super.key, + required this.imagePaths, + required this.suggestionText, + required this.onSelectAction, + required this.onLoadSuggestion, + this.hasContent = false, + }); + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + AiScribeImproveButton( + imagePaths: imagePaths, + suggestionText: suggestionText, + onLoadSuggestion: onLoadSuggestion, + ), + Flexible( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (hasContent) _buildReplaceButton(context), + _buildInsertButton(context), + ], + ), + ), + ], + ); + } + + Widget _buildReplaceButton(BuildContext context) { + final localizations = ScribeLocalizations.of(context); + return Flexible( + child: Container( + constraints: const BoxConstraints(minWidth: AIScribeSizes.minButtonWidth), + height: AIScribeSizes.buttonHeight, + child: ConfirmDialogButton( + label: AiScribeSuggestionActions.replace.getLabel(localizations), + textColor: AppColor.primaryMain, + onTapAction: () { + Navigator.of(context).pop(); + onSelectAction( + AiScribeSuggestionActions.replace, + suggestionText, + ); + }, + ), + ), + ); + } + + Widget _buildInsertButton(BuildContext context) { + final localizations = ScribeLocalizations.of(context); + return Flexible( + child: Container( + constraints: const BoxConstraints(minWidth: AIScribeSizes.minButtonWidth), + height: AIScribeSizes.buttonHeight, + child: ConfirmDialogButton( + label: AiScribeSuggestionActions.insert.getLabel(localizations), + backgroundColor: AppColor.blueD2E9FF, + textColor: AppColor.primaryMain, + onTapAction: () { + Navigator.of(context).pop(); + onSelectAction( + AiScribeSuggestionActions.insert, + suggestionText, + ); + }, + ), + ), + ); + } +} \ No newline at end of file diff --git a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_list_actions.dart b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_list_actions.dart index 7b4873c47..eed93c773 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_list_actions.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_list_actions.dart @@ -1,6 +1,4 @@ -import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; -import 'package:core/presentation/views/dialog/confirm_dialog_button.dart'; import 'package:flutter/material.dart'; import 'package:scribe/scribe.dart'; @@ -14,7 +12,7 @@ class AiScribeSuggestionSuccessListActions extends StatelessWidget { final String suggestionText; final bool hasContent; final OnSelectAiScribeSuggestionAction onSelectAction; - final VoidCallback onLoadSuggestion; + final OnLoadSuggestion onLoadSuggestion; const AiScribeSuggestionSuccessListActions({ super.key, @@ -27,52 +25,16 @@ class AiScribeSuggestionSuccessListActions extends StatelessWidget { @override Widget build(BuildContext context) { - final localizations = ScribeLocalizations.of(context); - return Column( + spacing: AIScribeSizes.successSpacing, children: [ AiScribeSuggestionSuccessToolbar(suggestionText: suggestionText, onLoadSuggestion: onLoadSuggestion, imagePaths: imagePaths), - Row( - mainAxisAlignment: MainAxisAlignment.end, - spacing: 8, - children: [ - if (hasContent) - Flexible( - child: Container( - constraints: const BoxConstraints(minWidth: 67), - height: 36, - child: ConfirmDialogButton( - label: AiScribeSuggestionActions.replace.getLabel(localizations), - textColor: AppColor.primaryMain, - onTapAction: () { - Navigator.of(context).pop(); - onSelectAction( - AiScribeSuggestionActions.replace, - suggestionText, - ); - }, - ), - ), - ), - Flexible( - child: Container( - constraints: const BoxConstraints(minWidth: 72), - height: 36, - child: ConfirmDialogButton( - label: AiScribeSuggestionActions.insert.getLabel(localizations), - backgroundColor: AppColor.blueD2E9FF, - textColor: AppColor.primaryMain, - onTapAction: () { - Navigator.of(context).pop(); - onSelectAction( - AiScribeSuggestionActions.insert, - suggestionText, - ); - }, - ), - ), - ), - ], + AiScribeSuggestionSuccessActions( + suggestionText: suggestionText, + onLoadSuggestion: onLoadSuggestion, + imagePaths: imagePaths, + hasContent: hasContent, + onSelectAction: onSelectAction, ), ], ); diff --git a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_toolbar.dart b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_toolbar.dart index bd7cf62e2..ad85bd695 100644 --- a/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_toolbar.dart +++ b/scribe/lib/scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_success_toolbar.dart @@ -6,7 +6,7 @@ import 'package:core/presentation/resources/image_paths.dart'; class AiScribeSuggestionSuccessToolbar extends StatelessWidget { final String suggestionText; - final VoidCallback onLoadSuggestion; + final OnLoadSuggestion onLoadSuggestion; final ImagePaths imagePaths; const AiScribeSuggestionSuccessToolbar({