From d8758267c45381e26ddb64f77a2808f152abb4cb Mon Sep 17 00:00:00 2001 From: Levi Date: Sat, 19 Sep 2026 19:04:58 +0800 Subject: [PATCH] refactor(ui): polish extension update affordance per review - Move the update dot from the icon corner to beside the card title - Restyle the card update button to match the row's ghost buttons - Drop the detail-view dot; the existing update status chip covers it - Rename the detail update button label to just Update --- .../presentation/pages/extensions_page.dart | 54 +++++++++---------- .../widgets/extension_detail_view.dart | 34 +++--------- 2 files changed, 33 insertions(+), 55 deletions(-) diff --git a/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart b/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart index 21fe8c56..dc8fe431 100644 --- a/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart +++ b/ui/flutter/lib/features/extensions/presentation/pages/extensions_page.dart @@ -976,37 +976,32 @@ class _ExtensionCard extends ConsumerWidget { children: [ Row( children: [ - Stack( - clipBehavior: Clip.none, - children: [ - ExtensionIcon(item: item, size: 40, borderRadius: BorderRadius.circular(6), fallbackFit: BoxFit.cover), - if (canUpdate) - Positioned( - top: -3, - right: -3, - child: Container( - key: ValueKey('extension-card-update-dot-${item.id}'), - width: 11, - height: 11, - decoration: BoxDecoration( - color: palette.error, - shape: BoxShape.circle, - border: Border.all(color: palette.cardBg, width: 2), - ), - ), - ), - ], - ), + ExtensionIcon(item: item, size: 40, borderRadius: BorderRadius.circular(6), fallbackFit: BoxFit.cover), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - item.title, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: palette.textPrimary, fontSize: 14, fontWeight: FontWeight.w700), + Row( + children: [ + Flexible( + child: Text( + item.title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: palette.textPrimary, fontSize: 14, fontWeight: FontWeight.w700), + ), + ), + if (canUpdate) ...[ + const SizedBox(width: 6), + Container( + key: ValueKey('extension-card-update-dot-${item.id}'), + width: 8, + height: 8, + decoration: BoxDecoration(color: palette.error, shape: BoxShape.circle), + ), + ], + ], ), const SizedBox(height: 2), Text( @@ -1076,11 +1071,14 @@ class _ExtensionCard extends ConsumerWidget { mainAxisSize: MainAxisSize.min, children: [ if (canUpdate && installed != null) - shad.PrimaryButton( + shad.GhostButton( key: ValueKey('update-extension-${installed.identity}'), density: shad.ButtonDensity.dense, onPressed: busy ? null : () => showExtensionUpdateDialog(context, installed), - child: Text(context.l10n.extensionUpdateAction), + child: Text( + context.l10n.extensionUpdateAction, + style: TextStyle(color: palette.error, fontWeight: FontWeight.w600), + ), ), if (installed == null && item.store != null) shad.GhostButton( diff --git a/ui/flutter/lib/features/extensions/presentation/widgets/extension_detail_view.dart b/ui/flutter/lib/features/extensions/presentation/widgets/extension_detail_view.dart index 26204eef..f7337291 100644 --- a/ui/flutter/lib/features/extensions/presentation/widgets/extension_detail_view.dart +++ b/ui/flutter/lib/features/extensions/presentation/widgets/extension_detail_view.dart @@ -65,32 +65,12 @@ class ExtensionDetailView extends ConsumerWidget { key: const ValueKey('extension-details-hero'), crossAxisAlignment: CrossAxisAlignment.start, children: [ - Stack( - clipBehavior: Clip.none, - children: [ - ExtensionIcon( - item: current, - size: mobile ? 60 : 68, - borderRadius: BorderRadius.circular(14), - fallbackPadding: EdgeInsets.all((mobile ? 60 : 68) * 0.16), - fallbackBackgroundColor: palette.surfaceSoft, - ), - if (canUpdate) - Positioned( - top: -2, - right: -2, - child: Container( - key: const ValueKey('extension-details-update-dot'), - width: 13, - height: 13, - decoration: BoxDecoration( - color: palette.error, - shape: BoxShape.circle, - border: Border.all(color: palette.bg, width: 2.5), - ), - ), - ), - ], + ExtensionIcon( + item: current, + size: mobile ? 60 : 68, + borderRadius: BorderRadius.circular(14), + fallbackPadding: EdgeInsets.all((mobile ? 60 : 68) * 0.16), + fallbackBackgroundColor: palette.surfaceSoft, ), SizedBox(width: mobile ? 16 : 20), Expanded( @@ -157,7 +137,7 @@ class ExtensionDetailView extends ConsumerWidget { variant: AppLoadingButtonVariant.primary, icon: const Icon(Icons.upgrade, size: 16), onPressed: busy ? null : () => showExtensionUpdateDialog(context, installed), - child: Text(context.l10n.newVersionUpdate), + child: Text(context.l10n.extensionUpdateAction), ), if (installed != null && !canUpdate) AppLoadingButton(