From b72a438d01fad28e4efa4cc32c082942bad442b5 Mon Sep 17 00:00:00 2001 From: Tom Brow Date: Mon, 19 Oct 2026 17:57:05 -0700 Subject: [PATCH] Refine prototype agent list and creation form styling Signed-off-by: Tom Brow --- .../lib/features/channels/channels_page.dart | 16 ++-- .../channels_page/header_actions.dart | 12 +-- mobile/lib/prototype/agents_sheet.dart | 21 +---- mobile/lib/prototype/create_agent_sheet.dart | 81 ++++++++++++++----- mobile/test/prototype/agents_ui_test.dart | 2 +- 5 files changed, 78 insertions(+), 54 deletions(-) diff --git a/mobile/lib/features/channels/channels_page.dart b/mobile/lib/features/channels/channels_page.dart index 69036ca1fa..ba2bfc0102 100644 --- a/mobile/lib/features/channels/channels_page.dart +++ b/mobile/lib/features/channels/channels_page.dart @@ -482,14 +482,6 @@ class ChannelsPage extends HookConsumerWidget { onPressed: openCommunitySwitcher, ), nativeActions: [ - if (onSearch != null) - IosNavigationAction( - label: 'Search', - symbol: 'magnifyingglass', - sharesBackground: true, - icon: BuzzIcons.search300, - onPressed: onSearch, - ), if (onAgents != null) IosNavigationAction( label: 'Agents', @@ -498,6 +490,14 @@ class ChannelsPage extends HookConsumerWidget { icon: BuzzIcons.bot, onPressed: onAgents, ), + if (onSearch != null) + IosNavigationAction( + label: 'Search', + symbol: 'magnifyingglass', + sharesBackground: true, + icon: BuzzIcons.search300, + onPressed: onSearch, + ), if (onInbox != null) IosNavigationAction( label: 'Inbox', diff --git a/mobile/lib/features/channels/channels_page/header_actions.dart b/mobile/lib/features/channels/channels_page/header_actions.dart index 921b6aba97..5d09f870a4 100644 --- a/mobile/lib/features/channels/channels_page/header_actions.dart +++ b/mobile/lib/features/channels/channels_page/header_actions.dart @@ -24,18 +24,18 @@ class _ChannelHeaderActions extends StatelessWidget { Row( mainAxisSize: MainAxisSize.min, children: [ - if (onSearch != null) - IconButton( - tooltip: 'Search', - icon: const Icon(BuzzIcons.search300), - onPressed: onSearch, - ), if (onAgents != null) IconButton( tooltip: 'Agents', icon: const Icon(BuzzIcons.bot), onPressed: onAgents, ), + if (onSearch != null) + IconButton( + tooltip: 'Search', + icon: const Icon(BuzzIcons.search300), + onPressed: onSearch, + ), if (onInbox != null) Semantics( label: hasUnreadInbox ? 'Inbox, unread' : 'Inbox', diff --git a/mobile/lib/prototype/agents_sheet.dart b/mobile/lib/prototype/agents_sheet.dart index 0c66f47f29..503ff13ec9 100644 --- a/mobile/lib/prototype/agents_sheet.dart +++ b/mobile/lib/prototype/agents_sheet.dart @@ -5,7 +5,6 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import '../features/profile/user_profile_sheet.dart'; -import '../features/profile/presence_cache_provider.dart'; import '../shared/community/community_provider.dart'; import '../shared/crypto/nip_oa.dart'; import '../shared/identity_names/identity_names_provider.dart'; @@ -88,7 +87,6 @@ class PrototypeAgentsSheet extends HookConsumerWidget { ); keys.sort((a, b) => names.labelFor(a).compareTo(names.labelFor(b))); final profiles = ref.watch(userCacheProvider); - final presence = ref.watch(presentedPresenceProvider); final failed = directory.hasError || registered.hasError; final loading = controller.busy || @@ -109,7 +107,8 @@ class PrototypeAgentsSheet extends HookConsumerWidget { icon: const Icon(Icons.close), onPressed: () => Navigator.pop(context), ), - trailing: TextButton( + trailing: IconButton( + tooltip: 'Add agent', onPressed: community == null || controller.busy ? null : () => showBuzzModalBottomSheet( @@ -122,7 +121,7 @@ class PrototypeAgentsSheet extends HookConsumerWidget { onCreated: () => ref.invalidate(agentDirectoryProvider), ), ), - child: const Text('Add agent'), + icon: const Icon(Icons.add), ), child: RefreshIndicator( onRefresh: refresh, @@ -161,6 +160,7 @@ class PrototypeAgentsSheet extends HookConsumerWidget { key: ValueKey(key), contentPadding: const EdgeInsets.symmetric( horizontal: Grid.xxs, + vertical: Grid.xxs, ), leading: AvatarImage( imageUrl: profiles[key]?.avatarUrl, @@ -172,19 +172,6 @@ class PrototypeAgentsSheet extends HookConsumerWidget { ), ), title: Text(names.labelFor(key)), - subtitle: presence[key] == 'online' - ? Row( - children: [ - Icon( - Icons.circle, - size: Grid.xxs, - color: context.appColors.success, - ), - const SizedBox(width: Grid.half), - const Text('Online'), - ], - ) - : null, onTap: () => Navigator.pop(context, key), ), ], diff --git a/mobile/lib/prototype/create_agent_sheet.dart b/mobile/lib/prototype/create_agent_sheet.dart index 038b6f6d48..3417adb09a 100644 --- a/mobile/lib/prototype/create_agent_sheet.dart +++ b/mobile/lib/prototype/create_agent_sheet.dart @@ -36,6 +36,38 @@ class CreatePrototypeAgentSheet extends HookConsumerWidget { final active = ref.watch(activeCommunityProvider).asData?.value; final sameCommunity = active?.id == community.id; final pending = controller.pending != null; + final canCreate = + !controller.busy && sameCommunity && name.text.trim().isNotEmpty; + final actionStyle = IconButton.styleFrom( + minimumSize: const Size.square(44), + backgroundColor: context.colors.surfaceContainerHighest, + side: BorderSide(color: context.colors.outlineVariant), + shape: const CircleBorder(), + ); + InputDecoration fieldDecoration(String label, {String? hint}) => + InputDecoration( + labelText: label, + floatingLabelBehavior: FloatingLabelBehavior.always, + hintText: hint, + filled: true, + fillColor: context.colors.surfaceContainerHighest, + contentPadding: const EdgeInsets.symmetric( + horizontal: Grid.xs, + vertical: Grid.xs, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(Radii.container), + borderSide: BorderSide.none, + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(Radii.container), + borderSide: BorderSide.none, + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(Radii.container), + borderSide: BorderSide.none, + ), + ); Future create() async { error.value = null; @@ -91,16 +123,28 @@ class CreatePrototypeAgentSheet extends HookConsumerWidget { ), child: BuzzTitledSheetLayout( title: 'Create agent', - leading: TextButton( + leading: IconButton( + tooltip: 'Cancel', + style: actionStyle, onPressed: controller.busy ? null : () => Navigator.pop(context), - child: const Text('Cancel'), + icon: const Icon(Icons.close), ), - trailing: TextButton( - onPressed: - controller.busy || !sameCommunity || name.text.trim().isEmpty - ? null - : create, - child: Text(pending ? 'Resume' : 'Create'), + trailing: IconButton( + tooltip: pending ? 'Resume' : 'Create', + style: actionStyle.copyWith( + backgroundColor: WidgetStatePropertyAll( + canCreate + ? context.colors.primary + : context.colors.surfaceContainerHighest, + ), + foregroundColor: WidgetStatePropertyAll( + canCreate + ? context.colors.onPrimary + : context.colors.onSurfaceVariant, + ), + ), + onPressed: canCreate ? create : null, + icon: const Icon(Icons.check), ), child: SingleChildScrollView( padding: const EdgeInsets.all(Grid.gutter), @@ -112,28 +156,21 @@ class CreatePrototypeAgentSheet extends HookConsumerWidget { autofocus: true, enabled: !controller.busy && !pending, textCapitalization: TextCapitalization.words, - decoration: const InputDecoration( - labelText: 'Display name', - border: OutlineInputBorder(), - ), + decoration: fieldDecoration('Name'), ), const SizedBox(height: Grid.sm), TextField( controller: instructions, enabled: !controller.busy && !pending, minLines: 4, - maxLines: 7, - decoration: const InputDecoration( - labelText: 'Instructions (optional)', - hintText: 'Describe what this agent should do.', - border: OutlineInputBorder(), + maxLines: 6, + textCapitalization: TextCapitalization.sentences, + textInputAction: TextInputAction.newline, + decoration: fieldDecoration( + 'Instructions (optional)', + hint: 'Describe what this agent should do.', ), ), - const SizedBox(height: Grid.xs), - Text( - 'Runs remotely and responds to you in ${community.name}.', - style: context.textTheme.bodySmall, - ), if (controller.busy) ...[ const SizedBox(height: Grid.xs), const LinearProgressIndicator(), diff --git a/mobile/test/prototype/agents_ui_test.dart b/mobile/test/prototype/agents_ui_test.dart index 176a5de32b..fe1f800ac5 100644 --- a/mobile/test/prototype/agents_ui_test.dart +++ b/mobile/test/prototype/agents_ui_test.dart @@ -164,7 +164,7 @@ void main() { 'Reply concisely.', ); await tester.pump(); - await tester.tap(find.text('Create')); + await tester.tap(find.byTooltip('Create')); await tester.pumpAndSettle(); expect(controller.submittedName, 'Scout'); expect(controller.submittedInstructions, 'Reply concisely.');