feat: add MCP support (#1471)

This commit is contained in:
Levi authored and GitHub committed 2026-09-05 20:21:39 +08:00
1 parent 39cd9034cd
commit f2145de571
83 files changed
+3119 -363

No files matched your search

@@ -0,0 +1,59 @@
import 'package:flutter/material.dart' show Icons;
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart' as shad;
import '../../l10n/l10n.dart';
import 'app_tooltip.dart';
class AppCopyIconButton extends StatefulWidget {
const AppCopyIconButton({
super.key,
required this.text,
this.enabled = true,
this.dimension = 28,
this.iconSize = 16,
});
final String text;
final bool enabled;
final double dimension;
final double iconSize;
@override
State<AppCopyIconButton> createState() => _AppCopyIconButtonState();
}
class _AppCopyIconButtonState extends State<AppCopyIconButton> {
bool _copied = false;
@override
void didUpdateWidget(covariant AppCopyIconButton oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.text != oldWidget.text || (!widget.enabled && oldWidget.enabled)) {
_copied = false;
}
}
@override
Widget build(BuildContext context) {
return AppTooltip(
message: _copied ? context.l10n.copied : context.l10n.copy,
child: SizedBox.square(
dimension: widget.dimension,
child: shad.IconButton.ghost(
size: shad.ButtonSize.xSmall,
onPressed: widget.enabled ? _copy : null,
icon: Icon(_copied ? Icons.check : Icons.copy_outlined, size: widget.iconSize),
),
),
);
}
Future<void> _copy() async {
await Clipboard.setData(ClipboardData(text: widget.text));
if (mounted) {
setState(() => _copied = true);
}
}
}
@@ -31,20 +31,28 @@ class AppLoadingButton extends StatelessWidget {
final replaceChild = loading && icon == null;
final leading = loading && icon != null ? _LoadingIcon(variant: variant) : icon;
final effectiveChild = replaceChild ? _LoadingIcon(variant: variant) : child;
final content = leading == null
? effectiveChild
: Row(
mainAxisSize: MainAxisSize.min,
children: [
leading,
const SizedBox(width: AppDesignTokens.space8),
effectiveChild,
],
);
final effectiveOnPressed = loading ? null : onPressed;
return switch (variant) {
AppLoadingButtonVariant.secondary => shad.SecondaryButton(
onPressed: effectiveOnPressed,
leading: leading,
alignment: alignment,
child: effectiveChild,
child: content,
),
AppLoadingButtonVariant.primary => AppPrimaryButton(
onPressed: effectiveOnPressed,
leading: leading,
alignment: alignment,
child: effectiveChild,
child: content,
),
AppLoadingButtonVariant.brand => shad.ButtonStyleOverride(
decoration: (context, states, value) {
@@ -70,12 +78,7 @@ class AppLoadingButton extends StatelessWidget {
? palette.brandForeground.withValues(alpha: 0.55)
: palette.brandForeground,
),
child: shad.SecondaryButton(
onPressed: effectiveOnPressed,
leading: leading,
alignment: alignment,
child: effectiveChild,
),
child: shad.SecondaryButton(onPressed: effectiveOnPressed, alignment: alignment, child: content),
),
};
}
@@ -31,6 +31,16 @@ class AppPrimaryButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
final palette = AppPalette.of(context);
final content = leading == null && trailing == null
? child
: Row(
mainAxisSize: MainAxisSize.min,
children: [
if (leading != null) ...[leading!, const SizedBox(width: AppDesignTokens.space8)],
child,
if (trailing != null) ...[const SizedBox(width: AppDesignTokens.space8), trailing!],
],
);
return shad.ButtonStyleOverride(
decoration: (context, states, value) {
@@ -48,14 +58,12 @@ class AppPrimaryButton extends StatelessWidget {
},
child: shad.PrimaryButton(
onPressed: onPressed,
leading: leading,
trailing: trailing,
alignment: alignment,
size: size,
density: density,
shape: shape,
disableTransition: disableTransition,
child: child,
child: content,
),
);
}