Add an animated AI icon for Scribe in desktop web

I moved the previous static icon in its own component and created a component that manage the loading of the appropriate icon: static or dynamic.
This commit is contained in:
Théo Poizat
2026-01-27 17:04:24 +01:00
committed by Dat H. Pham
parent 34e1dffa0a
commit b6959a5ab6
5 changed files with 123 additions and 13 deletions
File diff suppressed because one or more lines are too long
@@ -247,6 +247,7 @@ class ImagePaths {
String get icFileDownload => _getImagePath('ic_file_download.svg');
String get icTwakeWorkplace => _getIconPath('icon_twp.png');
String get animLottieTmail => _getAnimationPath('lottie-tmail.json');
String get animLottieScribe => _getAnimationPath('lottie-scribe.json');
String get icExpandArrows => _getImagePath('ic_expand_arrows.svg');
String get icPremium => _getImagePath('ic_premium.svg');
String get icCloud => _getImagePath('ic_cloud.svg');
@@ -0,0 +1,70 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/container/tmail_container_widget.dart';
import 'package:flutter/material.dart';
import 'package:lottie/lottie.dart';
import 'package:scribe/scribe.dart';
class AiAssistantAnimatedButton extends StatefulWidget {
final ImagePaths imagePaths;
final void Function() onTapActionCallback;
final EdgeInsetsGeometry? margin;
const AiAssistantAnimatedButton({
super.key,
required this.imagePaths,
required this.onTapActionCallback,
this.margin,
});
@override
State<AiAssistantAnimatedButton> createState() =>
_AiAssistantAnimatedButtonState();
}
class _AiAssistantAnimatedButtonState extends State<AiAssistantAnimatedButton>
with TickerProviderStateMixin {
late AnimationController _animationController;
@override
void initState() {
super.initState();
_animationController = AnimationController(vsync: this);
}
@override
void dispose() {
_animationController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return TMailContainerWidget(
margin: widget.margin,
padding: AIScribeSizes.aiAssistantIconPadding,
backgroundColor: Colors.transparent,
borderRadius: AIScribeSizes.aiAssistantIconRadius,
tooltipMessage: ScribeLocalizations.of(context).aiAssistant,
onTapActionCallback: widget.onTapActionCallback,
child: MouseRegion(
cursor: SystemMouseCursors.click,
onEnter: (event) {
_animationController.forward();
},
onExit: (event) {
_animationController.reset();
},
child: Lottie.asset(
widget.imagePaths.animLottieScribe,
width: AIScribeSizes.aiAssistantIcon,
height: AIScribeSizes.aiAssistantIcon,
fit: BoxFit.fill,
controller: _animationController,
onLoaded: (composition) {
_animationController.duration = composition.duration;
},
),
),
);
}
}
@@ -1,7 +1,8 @@
import 'package:core/presentation/resources/image_paths.dart';
import 'package:core/presentation/views/button/tmail_button_widget.dart';
import 'package:core/utils/platform_info.dart';
import 'package:flutter/material.dart';
import 'package:scribe/scribe.dart';
import 'ai_assistant_animated_button.dart';
import 'ai_assistant_static_button.dart';
typedef OnOpenAiAssistantModal = void Function(
Offset? position,
@@ -24,17 +25,20 @@ class AiAssistantButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return TMailButtonWidget.fromIcon(
icon: imagePaths.icGradientSparkle,
padding: AIScribeSizes.aiAssistantIconPadding,
backgroundColor: Colors.transparent,
iconSize: AIScribeSizes.aiAssistantIcon,
iconColor: iconColor,
margin: margin,
borderRadius: AIScribeSizes.aiAssistantIconRadius,
tooltipMessage: ScribeLocalizations.of(context).aiAssistant,
onTapActionCallback: () => _onTapActionCallback(context),
);
if (PlatformInfo.isWebDesktop) {
return AiAssistantAnimatedButton(
imagePaths: imagePaths,
onTapActionCallback: () => _onTapActionCallback(context),
margin: margin,
);
} else {
return AiAssistantStaticButton(
imagePaths: imagePaths,
onTapActionCallback: () => _onTapActionCallback(context),
iconColor: iconColor,
margin: margin,
);
}
}
void _onTapActionCallback(BuildContext context) {
@@ -0,0 +1,34 @@
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 AiAssistantStaticButton extends StatelessWidget {
final ImagePaths imagePaths;
final void Function() onTapActionCallback;
final Color? iconColor;
final EdgeInsetsGeometry? margin;
const AiAssistantStaticButton({
super.key,
required this.imagePaths,
required this.onTapActionCallback,
this.iconColor,
this.margin,
});
@override
Widget build(BuildContext context) {
return TMailButtonWidget.fromIcon(
icon: imagePaths.icGradientSparkle,
padding: AIScribeSizes.aiAssistantIconPadding,
backgroundColor: Colors.transparent,
iconSize: AIScribeSizes.aiAssistantIcon,
iconColor: iconColor,
margin: margin,
borderRadius: AIScribeSizes.aiAssistantIconRadius,
tooltipMessage: ScribeLocalizations.of(context).aiAssistant,
onTapActionCallback: onTapActionCallback,
);
}
}