[Scribe] Add Improve button (#4335)

This commit is contained in:
Théo Poizat
2026-02-27 08:12:58 +01:00
committed by Dat H. Pham
parent c8253551b1
commit 2ab6e0df02
23 changed files with 361 additions and 92 deletions
@@ -245,7 +245,7 @@ extension HandleAiScribeInComposerExtension on ComposerController {
final fullText = await _getTextOnlyContentInEditor(); final fullText = await _getTextOnlyContentInEditor();
await AiScribeModalManager.showAIScribeMenuModal( await AiScribeModalManager.showAIScribeModal(
imagePaths: imagePaths, imagePaths: imagePaths,
availableCategories: AIScribeMenuCategory.values, availableCategories: AIScribeMenuCategory.values,
buttonPosition: position, buttonPosition: position,
+2
View File
@@ -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/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_modal_widget.dart';
export 'scribe/ai/presentation/widgets/modal/ai_scribe_suggestion_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_error.dart';
export 'scribe/ai/presentation/widgets/modal/suggestion/ai_scribe_suggestion_header.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_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.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_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_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/overlay/ai_selection_overlay.dart';
export 'scribe/ai/presentation/widgets/search/ai_scribe_bar.dart'; export 'scribe/ai/presentation/widgets/search/ai_scribe_bar.dart';
export 'scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart'; export 'scribe/ai/presentation/widgets/mobile/ai_scribe_mobile_actions_bottom_sheet.dart';
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "تحسين",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "إنشاء الاستجابة", "generatingResponse": "إنشاء الاستجابة",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Verbessern",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "Antwort wird generiert", "generatingResponse": "Antwort wird generiert",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Improve",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "Generating response", "generatingResponse": "Generating response",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Améliorer",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "Génération de la réponse", "generatingResponse": "Génération de la réponse",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Migliora",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "Generazione della risposta", "generatingResponse": "Generazione della risposta",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
+7 -1
View File
@@ -1,5 +1,5 @@
{ {
"@@last_modified": "2026-02-04T15:15:31.191557", "@@last_modified": "2026-02-09T16:58:15.727654",
"categoryCorrectGrammar": "Correct", "categoryCorrectGrammar": "Correct",
"@categoryCorrectGrammar": { "@categoryCorrectGrammar": {
"type": "text", "type": "text",
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Improve",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"aiAssistant": "AI assistant", "aiAssistant": "AI assistant",
"@aiAssistant": { "@aiAssistant": {
"type": "text", "type": "text",
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Улучшить",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "Генерация ответа", "generatingResponse": "Генерация ответа",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
+6
View File
@@ -102,6 +102,12 @@
"placeholders_order": [], "placeholders_order": [],
"placeholders": {} "placeholders": {}
}, },
"improve": "Cải thiện",
"@improve": {
"type": "text",
"placeholders_order": [],
"placeholders": {}
},
"generatingResponse": "Tạo phản hồi", "generatingResponse": "Tạo phản hồi",
"@generatingResponse": { "@generatingResponse": {
"type": "text", "type": "text",
@@ -150,6 +150,13 @@ class ScribeLocalizations {
); );
} }
String get improve {
return Intl.message(
'Improve',
name: 'improve',
);
}
String get aiAssistant { String get aiAssistant {
return Intl.message( return Intl.message(
'AI assistant', 'AI assistant',
@@ -130,6 +130,7 @@ abstract final class AIScribeSizes {
static const double scribeButtonRadius = 100; static const double scribeButtonRadius = 100;
static const double aiAssistantIconRadius = 8; static const double aiAssistantIconRadius = 8;
static const double contentCardRadius = 16; static const double contentCardRadius = 16;
static const double improveButtonBorderRadius = 4;
// Width / height // Width / height
static const double menuItemHeight = 40; static const double menuItemHeight = 40;
@@ -162,6 +163,7 @@ abstract final class AIScribeSizes {
static const double modalSpacing = 26; static const double modalSpacing = 26;
static const double modalWithoutContentSpacing = 12; static const double modalWithoutContentSpacing = 12;
static const double keyboardSpacing = 12; static const double keyboardSpacing = 12;
static const double successSpacing = 12;
// Elevation // Elevation
static const double dialogElevation = 8; static const double dialogElevation = 8;
@@ -173,6 +175,10 @@ abstract final class AIScribeSizes {
static const double aiAssistantIcon = 24; static const double aiAssistantIcon = 24;
static const double bottomsheetIcon = 20; static const double bottomsheetIcon = 20;
// Button sizes
static const double minButtonWidth = 72;
static const double buttonHeight = 36;
// Padding // Padding
static const EdgeInsetsGeometry menuItemPadding = static const EdgeInsetsGeometry menuItemPadding =
EdgeInsetsDirectional.only(start: 16, end: 10); EdgeInsetsDirectional.only(start: 16, end: 10);
@@ -218,4 +224,7 @@ abstract final class AIScribeSizes {
static const EdgeInsetsGeometry contentCardPadding = static const EdgeInsetsGeometry contentCardPadding =
EdgeInsetsDirectional.all(16); EdgeInsetsDirectional.all(16);
static const EdgeInsetsGeometry improveButtonPadding =
EdgeInsetsDirectional.only(start: 14, top: 6, end: 10, bottom: 6);
} }
@@ -56,7 +56,7 @@ class ContextSubmenuController {
final clampedLeft = finalLeft final clampedLeft = finalLeft
.clamp(0.0, math.max(0.0, screenWidth - submenuWidth)) .clamp(0.0, math.max(0.0, screenWidth - submenuWidth))
.toDouble(); .toDouble();
final availableHeight = math.max(0.0, screenHeight - anchor.top); final availableHeight = math.max(0.0, screenHeight - bottom);
final finalHeight = math.min(submenuMaxHeight, availableHeight); final finalHeight = math.min(submenuMaxHeight, availableHeight);
_submenuEntry = OverlayEntry( _submenuEntry = OverlayEntry(
@@ -6,7 +6,7 @@ import 'package:scribe/scribe.dart';
class AiScribeModalManager { class AiScribeModalManager {
AiScribeModalManager._(); AiScribeModalManager._();
static Future<void> showAIScribeMenuModal({ static Future<void> showAIScribeModal({
required bool isScribeMobile, required bool isScribeMobile,
required ImagePaths imagePaths, required ImagePaths imagePaths,
required List<AIScribeMenuCategory> availableCategories, required List<AIScribeMenuCategory> availableCategories,
@@ -17,31 +17,16 @@ class AiScribeModalManager {
ModalPlacement? preferredPlacement, ModalPlacement? preferredPlacement,
ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center, ModalCrossAxisAlignment crossAxisAlignment = ModalCrossAxisAlignment.center,
}) async { }) async {
final AIAction? aiAction; final AIAction? aiAction = await showAIScribeMenuModal(
isScribeMobile: isScribeMobile,
if (isScribeMobile) {
aiAction = await showMobileAIScribeMenuModal(
imagePaths: imagePaths, imagePaths: imagePaths,
content: content, content: content,
availableCategories: availableCategories, availableCategories: availableCategories,
buttonPosition: buttonPosition,
buttonSize: buttonSize,
preferredPlacement: preferredPlacement,
crossAxisAlignment: crossAxisAlignment,
); );
} else {
final ContextSubmenuController submenuController = ContextSubmenuController();
aiAction = await Get.dialog<AIAction>(
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) { if (aiAction != null) {
await showAIScribeSuggestionModal( await showAIScribeSuggestionModal(
@@ -58,6 +43,38 @@ class AiScribeModalManager {
} }
} }
static Future<AIAction?> showAIScribeMenuModal({
required bool isScribeMobile,
required ImagePaths imagePaths,
required List<AIScribeMenuCategory> 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<void> showAIScribeSuggestionModal({ static Future<void> showAIScribeSuggestionModal({
required AIAction aiAction, required AIAction aiAction,
required bool isScribeMobile, required bool isScribeMobile,
@@ -77,26 +94,52 @@ class AiScribeModalManager {
onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction, onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction,
); );
} else { } else {
await Get.dialog<String?>( await showDesktopAIScribeSuggestionModal(
AiScribeSuggestionWidget( aiAction: aiAction,
aiAction: aiAction, imagePaths: imagePaths,
content: content,
buttonPosition: buttonPosition,
buttonSize: buttonSize,
preferredPlacement: preferredPlacement,
crossAxisAlignment: crossAxisAlignment,
onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction,
);
}
}
static Future<AIAction?> showDesktopAIScribeMenuModal({
required ImagePaths imagePaths,
required List<AIScribeMenuCategory> 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<AIAction>(
AiScribeModalWidget(
imagePaths: imagePaths, imagePaths: imagePaths,
content: content, content: content,
availableCategories: availableCategories,
buttonPosition: buttonPosition, buttonPosition: buttonPosition,
buttonSize: buttonSize, buttonSize: buttonSize,
preferredPlacement: preferredPlacement, preferredPlacement: preferredPlacement,
crossAxisAlignment: crossAxisAlignment, crossAxisAlignment: crossAxisAlignment,
onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction, submenuController: submenuController,
showCustomPromptBar: showCustomPromptBar,
), ),
barrierColor: AIScribeColors.dialogBarrier, barrierColor: AIScribeColors.dialogBarrier,
); ).whenComplete(submenuController.dispose);
}
} }
static Future<AIAction?> showMobileAIScribeMenuModal({ static Future<AIAction?> showMobileAIScribeMenuModal({
required ImagePaths imagePaths, required ImagePaths imagePaths,
required List<AIScribeMenuCategory> availableCategories, required List<AIScribeMenuCategory> availableCategories,
String? content, String? content,
bool showCustomPromptBar = true,
}) async { }) async {
final context = Get.context; final context = Get.context;
@@ -110,10 +153,36 @@ class AiScribeModalManager {
imagePaths: imagePaths, imagePaths: imagePaths,
availableCategories: availableCategories, availableCategories: availableCategories,
content: content, content: content,
showCustomPromptBar: showCustomPromptBar,
), ),
); );
} }
static Future<void> 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<void>(
AiScribeSuggestionWidget(
aiAction: aiAction,
imagePaths: imagePaths,
content: content,
buttonPosition: buttonPosition,
buttonSize: buttonSize,
preferredPlacement: preferredPlacement,
crossAxisAlignment: crossAxisAlignment,
onSelectAiScribeSuggestionAction: onSelectAiScribeSuggestionAction,
),
barrierColor: AIScribeColors.dialogBarrier,
);
}
static Future<void> showMobileAIScribeSuggestionModal({ static Future<void> showMobileAIScribeSuggestionModal({
required AIAction aiAction, required AIAction aiAction,
required ImagePaths imagePaths, required ImagePaths imagePaths,
@@ -7,6 +7,8 @@ import 'package:get/get.dart';
import 'package:scribe/scribe.dart'; import 'package:scribe/scribe.dart';
import 'package:scribe/scribe/ai/data/network/ai_api_exception.dart'; import 'package:scribe/scribe/ai/data/network/ai_api_exception.dart';
typedef OnLoadSuggestion = Future<void> Function([AIAction? aiAction, String? content]);
mixin AiScribeSuggestionStateMixin<T extends StatefulWidget> on State<T> { mixin AiScribeSuggestionStateMixin<T extends StatefulWidget> on State<T> {
GenerateAITextInteractor? get interactor => _interactor; GenerateAITextInteractor? get interactor => _interactor;
GenerateAITextInteractor? _interactor; GenerateAITextInteractor? _interactor;
@@ -37,7 +39,10 @@ mixin AiScribeSuggestionStateMixin<T extends StatefulWidget> on State<T> {
loadSuggestion(); loadSuggestion();
} }
Future<void> loadSuggestion() async { Future<void> loadSuggestion([AIAction? newAiAction, String? newContent]) async {
final aiActionToSend = newAiAction ?? aiAction;
final contentToSend = newContent ?? content;
_suggestionState.value = dartz.Right(GenerateAITextLoading()); _suggestionState.value = dartz.Right(GenerateAITextLoading());
if (_interactor == null) { if (_interactor == null) {
@@ -50,8 +55,8 @@ mixin AiScribeSuggestionStateMixin<T extends StatefulWidget> on State<T> {
} }
final result = await _interactor!.execute( final result = await _interactor!.execute(
aiAction, aiActionToSend,
content, contentToSend,
); );
if (!mounted) return; if (!mounted) return;
@@ -49,7 +49,7 @@ class InlineAiAssistButton extends StatelessWidget {
final isScribeMobile = AiScribeMobileUtils.isScribeInMobileMode(context); final isScribeMobile = AiScribeMobileUtils.isScribeInMobileMode(context);
await AiScribeModalManager.showAIScribeMenuModal( await AiScribeModalManager.showAIScribeModal(
isScribeMobile: isScribeMobile, isScribeMobile: isScribeMobile,
imagePaths: imagePaths, imagePaths: imagePaths,
availableCategories: AIScribeMenuCategory.values, availableCategories: AIScribeMenuCategory.values,
@@ -9,12 +9,14 @@ class AiScribeMobileActionsBottomSheet extends StatefulWidget {
final ImagePaths imagePaths; final ImagePaths imagePaths;
final List<AIScribeMenuCategory> availableCategories; final List<AIScribeMenuCategory> availableCategories;
final String? content; final String? content;
final bool showCustomPromptBar;
const AiScribeMobileActionsBottomSheet({ const AiScribeMobileActionsBottomSheet({
super.key, super.key,
required this.imagePaths, required this.imagePaths,
required this.availableCategories, required this.availableCategories,
this.content, this.content,
this.showCustomPromptBar = true,
}); });
@override @override
@@ -153,6 +155,10 @@ class _AiScribeMobileActionsBottomSheetState
} }
Widget _buildBottomBar(BuildContext context) { Widget _buildBottomBar(BuildContext context) {
if (!widget.showCustomPromptBar) {
return const SizedBox.shrink();
}
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
@@ -14,6 +14,7 @@ class AiScribeModalWidget extends StatelessWidget {
final ModalPlacement? preferredPlacement; final ModalPlacement? preferredPlacement;
final ModalCrossAxisAlignment crossAxisAlignment; final ModalCrossAxisAlignment crossAxisAlignment;
final ContextSubmenuController? submenuController; final ContextSubmenuController? submenuController;
final bool showCustomPromptBar;
const AiScribeModalWidget({ const AiScribeModalWidget({
super.key, super.key,
@@ -25,6 +26,7 @@ class AiScribeModalWidget extends StatelessWidget {
this.preferredPlacement, this.preferredPlacement,
this.crossAxisAlignment = ModalCrossAxisAlignment.center, this.crossAxisAlignment = ModalCrossAxisAlignment.center,
this.submenuController, this.submenuController,
this.showCustomPromptBar = true,
}); });
@override @override
@@ -57,16 +59,17 @@ class AiScribeModalWidget extends StatelessWidget {
), ),
), ),
), ),
MouseRegion( if (showCustomPromptBar)
onEnter: (_) => submenuController?.hide(), MouseRegion(
child: AIScribeBar( onEnter: (_) => submenuController?.hide(),
imagePaths: imagePaths, child: AIScribeBar(
onCustomPrompt: (customPrompt) { imagePaths: imagePaths,
Navigator.of(context).pop(CustomPromptAction(customPrompt)); onCustomPrompt: (customPrompt) {
submenuController?.hide(); Navigator.of(context).pop(CustomPromptAction(customPrompt));
}, submenuController?.hide();
},
),
), ),
),
], ],
), ),
); );
@@ -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<AiScribeImproveButton> createState() => _AiScribeImproveButtonState();
}
class _AiScribeImproveButtonState extends State<AiScribeImproveButton> {
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<void> _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);
}
}
}
@@ -7,7 +7,7 @@ class AiScribeSuggestionSuccess extends StatelessWidget {
final String suggestionText; final String suggestionText;
final bool hasContent; final bool hasContent;
final OnSelectAiScribeSuggestionAction onSelectAction; final OnSelectAiScribeSuggestionAction onSelectAction;
final VoidCallback onLoadSuggestion; final OnLoadSuggestion onLoadSuggestion;
const AiScribeSuggestionSuccess({ const AiScribeSuggestionSuccess({
super.key, super.key,
@@ -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,
);
},
),
),
);
}
}
@@ -1,6 +1,4 @@
import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.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:flutter/material.dart';
import 'package:scribe/scribe.dart'; import 'package:scribe/scribe.dart';
@@ -14,7 +12,7 @@ class AiScribeSuggestionSuccessListActions extends StatelessWidget {
final String suggestionText; final String suggestionText;
final bool hasContent; final bool hasContent;
final OnSelectAiScribeSuggestionAction onSelectAction; final OnSelectAiScribeSuggestionAction onSelectAction;
final VoidCallback onLoadSuggestion; final OnLoadSuggestion onLoadSuggestion;
const AiScribeSuggestionSuccessListActions({ const AiScribeSuggestionSuccessListActions({
super.key, super.key,
@@ -27,52 +25,16 @@ class AiScribeSuggestionSuccessListActions extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final localizations = ScribeLocalizations.of(context);
return Column( return Column(
spacing: AIScribeSizes.successSpacing,
children: [ children: [
AiScribeSuggestionSuccessToolbar(suggestionText: suggestionText, onLoadSuggestion: onLoadSuggestion, imagePaths: imagePaths), AiScribeSuggestionSuccessToolbar(suggestionText: suggestionText, onLoadSuggestion: onLoadSuggestion, imagePaths: imagePaths),
Row( AiScribeSuggestionSuccessActions(
mainAxisAlignment: MainAxisAlignment.end, suggestionText: suggestionText,
spacing: 8, onLoadSuggestion: onLoadSuggestion,
children: [ imagePaths: imagePaths,
if (hasContent) hasContent: hasContent,
Flexible( onSelectAction: onSelectAction,
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,
);
},
),
),
),
],
), ),
], ],
); );
@@ -6,7 +6,7 @@ import 'package:core/presentation/resources/image_paths.dart';
class AiScribeSuggestionSuccessToolbar extends StatelessWidget { class AiScribeSuggestionSuccessToolbar extends StatelessWidget {
final String suggestionText; final String suggestionText;
final VoidCallback onLoadSuggestion; final OnLoadSuggestion onLoadSuggestion;
final ImagePaths imagePaths; final ImagePaths imagePaths;
const AiScribeSuggestionSuccessToolbar({ const AiScribeSuggestionSuccessToolbar({