chore(flutter_desktop): ai chat ui improvements (#7025)

* chore: improve hover action bar inner radius

* chore: improve ai input appearance
This commit is contained in:
Richard Shiue 2024-12-20 23:22:48 +08:00 committed by GitHub
parent 30131fd9e4
commit 7ab68dcc2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 58 additions and 39 deletions

View File

@ -114,6 +114,7 @@ class _DesktopAIPromptInputState extends State<DesktopAIPromptInput> {
color: focusNode.hasFocus
? Theme.of(context).colorScheme.primary
: Theme.of(context).colorScheme.outline,
width: focusNode.hasFocus ? 1.5 : 1.0,
),
borderRadius: DesktopAIPromptSizes.promptFrameRadius,
),
@ -138,9 +139,10 @@ class _DesktopAIPromptInputState extends State<DesktopAIPromptInput> {
Stack(
children: [
ConstrainedBox(
constraints: const BoxConstraints(
constraints: BoxConstraints(
minHeight: DesktopAIPromptSizes.textFieldMinHeight +
DesktopAIPromptSizes.actionBarHeight,
DesktopAIPromptSizes.actionBarHeight +
DesktopAIPromptSizes.actionBarPadding.vertical,
maxHeight: 300,
),
child: inputTextField(),
@ -504,11 +506,10 @@ class _PromptBottomActions extends StatelessWidget {
Widget build(BuildContext context) {
return Container(
height: DesktopAIPromptSizes.actionBarHeight,
padding: const EdgeInsets.symmetric(horizontal: 8),
margin: DesktopAIPromptSizes.actionBarPadding,
child: BlocBuilder<AIPromptInputBloc, AIPromptInputState>(
builder: (context, state) {
return Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// predefinedFormatButton(),
const Spacer(),

View File

@ -15,6 +15,7 @@ import 'package:flowy_infra_ui/style_widget/hover.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../layout_define.dart';
import 'chat_mention_page_menu.dart';
class PromptInputDesktopSelectSourcesButton extends StatefulWidget {
@ -128,13 +129,13 @@ class _IndicatorButton extends StatelessWidget {
onTap: onTap,
behavior: HitTestBehavior.opaque,
child: SizedBox(
height: 24,
height: DesktopAIPromptSizes.actionBarButtonSize,
child: FlowyHover(
style: const HoverStyle(
borderRadius: BorderRadius.all(Radius.circular(8)),
),
child: Padding(
padding: const EdgeInsetsDirectional.fromSTEB(4, 4, 2, 4),
padding: const EdgeInsetsDirectional.fromSTEB(6, 6, 4, 6),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [

View File

@ -11,7 +11,7 @@ class AIChatUILayout {
query.padding.right,
query.viewInsets.bottom + query.padding.bottom,
)
: const EdgeInsets.only(bottom: 16);
: const EdgeInsets.only(bottom: 24);
}
static EdgeInsets get messageMargin => UniversalPlatform.isMobile
@ -20,22 +20,23 @@ class AIChatUILayout {
}
class DesktopAIPromptSizes {
static const promptFrameRadius = BorderRadius.all(Radius.circular(8));
static const promptFrameRadius = BorderRadius.all(Radius.circular(12.0));
static const attachedFilesBarPadding =
EdgeInsets.only(top: 8.0, left: 8.0, right: 8.0);
static const attachedFilesPreviewHeight = 48.0;
static const attachedFilesPreviewSpacing = 12.0;
static const textFieldMinHeight = 36.0;
static const textFieldMinHeight = 40.0;
static const textFieldContentPadding =
EdgeInsetsDirectional.fromSTEB(12.0, 12.0, 12.0, 4.0);
EdgeInsetsDirectional.fromSTEB(14.0, 12.0, 14.0, 8.0);
static const actionBarHeight = 28.0;
static const actionBarButtonSize = 24.0;
static const actionBarHeight = 32.0;
static const actionBarPadding = EdgeInsetsDirectional.fromSTEB(8, 0, 8, 4);
static const actionBarButtonSize = 28.0;
static const actionBarIconSize = 16.0;
static const actionBarButtonSpacing = 4.0;
static const sendButtonSize = 20.0;
static const sendButtonSize = 24.0;
}
class MobileAIPromptSizes {
@ -60,7 +61,11 @@ class DesktopAIConvoSizes {
static const avatarAndChatBubbleSpacing = 12.0;
static const actionBarIconSize = 24.0;
static const actionBarIconSize = 28.0;
static const actionBarIconSpacing = 8.0;
static const hoverActionBarPadding = EdgeInsets.all(2.0);
static const hoverActionBarRadius = BorderRadius.all(Radius.circular(8.0));
static const hoverActionBarIconRadius =
BorderRadius.all(Radius.circular(6.0));
static const actionBarIconRadius = BorderRadius.all(Radius.circular(8.0));
}

View File

@ -109,15 +109,9 @@ class ChatAIBottomInlineActions extends StatelessWidget {
DesktopAIConvoSizes.avatarAndChatBubbleSpacing,
),
child: AIResponseActionBar(
message: message,
showDecoration: false,
children: [
CopyButton(
textMessage: message as TextMessage,
),
RegenerateButton(
onTap: () => onRegenerate?.call(message.id),
),
],
onRegenerate: onRegenerate,
),
),
const VSpace(32.0),
@ -206,23 +200,17 @@ class _ChatAIMessageHoverState extends State<ChatAIMessageHover> {
}
},
child: Container(
constraints: const BoxConstraints(
constraints: BoxConstraints(
maxWidth: 784,
maxHeight: 28,
maxHeight: DesktopAIConvoSizes.actionBarIconSize +
DesktopAIConvoSizes.hoverActionBarPadding.vertical,
),
alignment: Alignment.topLeft,
child: hoverBubble || hoverActionBar
? AIResponseActionBar(
message: widget.message,
showDecoration: true,
children: [
CopyButton(
textMessage: widget.message as TextMessage,
),
RegenerateButton(
onTap: () =>
widget.onRegenerate?.call(widget.message.id),
),
],
onRegenerate: widget.onRegenerate,
)
: null,
),
@ -285,12 +273,14 @@ class _ChatAIMessageHoverState extends State<ChatAIMessageHover> {
class AIResponseActionBar extends StatelessWidget {
const AIResponseActionBar({
super.key,
required this.message,
required this.showDecoration,
required this.children,
this.onRegenerate,
});
final List<Widget> children;
final Message message;
final bool showDecoration;
final void Function(String)? onRegenerate;
@override
Widget build(BuildContext context) {
@ -300,14 +290,14 @@ class AIResponseActionBar extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
separatorBuilder: () =>
const HSpace(DesktopAIConvoSizes.actionBarIconSpacing),
children: children,
children: _buildChildren(),
);
return showDecoration
? Container(
padding: const EdgeInsets.all(2.0),
decoration: BoxDecoration(
borderRadius: DesktopAIConvoSizes.actionBarIconRadius,
borderRadius: DesktopAIConvoSizes.hoverActionBarRadius,
border: Border.all(
color: isLightMode
? const Color(0x1F1F2329)
@ -344,13 +334,29 @@ class AIResponseActionBar extends StatelessWidget {
)
: child;
}
List<Widget> _buildChildren() {
return [
CopyButton(
isInHoverBar: showDecoration,
textMessage: message as TextMessage,
),
RegenerateButton(
isInHoverBar: showDecoration,
onTap: () => onRegenerate?.call(message.id),
),
];
}
}
class CopyButton extends StatelessWidget {
const CopyButton({
super.key,
required this.isInHoverBar,
required this.textMessage,
});
final bool isInHoverBar;
final TextMessage textMessage;
@override
@ -360,7 +366,9 @@ class CopyButton extends StatelessWidget {
child: FlowyIconButton(
width: DesktopAIConvoSizes.actionBarIconSize,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
radius: DesktopAIConvoSizes.actionBarIconRadius,
radius: isInHoverBar
? DesktopAIConvoSizes.hoverActionBarIconRadius
: DesktopAIConvoSizes.actionBarIconRadius,
icon: FlowySvg(
FlowySvgs.copy_s,
color: Theme.of(context).hintColor,
@ -389,9 +397,11 @@ class CopyButton extends StatelessWidget {
class RegenerateButton extends StatelessWidget {
const RegenerateButton({
super.key,
required this.isInHoverBar,
required this.onTap,
});
final bool isInHoverBar;
final void Function() onTap;
@override
@ -401,7 +411,9 @@ class RegenerateButton extends StatelessWidget {
child: FlowyIconButton(
width: DesktopAIConvoSizes.actionBarIconSize,
hoverColor: AFThemeExtension.of(context).lightGreyHover,
radius: DesktopAIConvoSizes.actionBarIconRadius,
radius: isInHoverBar
? DesktopAIConvoSizes.hoverActionBarIconRadius
: DesktopAIConvoSizes.actionBarIconRadius,
icon: FlowySvg(
FlowySvgs.ai_undo_s,
color: Theme.of(context).hintColor,