From 355ebca27ed14bf67bbbdfbb33ef0b80aa651129 Mon Sep 17 00:00:00 2001 From: Tom Brow Date: Sat, 31 Oct 2026 10:55:41 -0700 Subject: [PATCH] fix(mobile): clip preset agent avatar previews to the squircle Catalog avatars in the picker and Create agent previews rendered unclipped, so the default create preview did not match the created agent. Let PresetAgentAvatar own the agent clip and assert the catalog state in the picker journey. Co-authored-by: studio Co-Authored-By: Claude Opus 5.5 Signed-off-by: Tom Brow --- .../shared/widgets/preset_agent_avatar.dart | 34 +++++++++++-------- .../agents/agent_avatar_picker_test.dart | 27 +++++++++------ 2 files changed, 35 insertions(+), 26 deletions(-) diff --git a/mobile/lib/shared/widgets/preset_agent_avatar.dart b/mobile/lib/shared/widgets/preset_agent_avatar.dart index c63787d6dc..1b6dea74b2 100644 --- a/mobile/lib/shared/widgets/preset_agent_avatar.dart +++ b/mobile/lib/shared/widgets/preset_agent_avatar.dart @@ -7,6 +7,7 @@ import 'package:video_player/video_player.dart'; import '../animated_avatar.dart'; import 'avatar_image.dart'; +import 'avatar_shape.dart'; import 'android_transparent_avatar.dart'; /// Plays the transparent video paired with a bundled character poster. @@ -98,21 +99,24 @@ class PresetAgentAvatar extends HookWidget { return SizedBox.square( dimension: size, - child: Transform.scale( - scale: animatedAvatarArtworkScale, - child: isAndroid && videoUrl != null - ? AndroidTransparentAvatar( - key: ValueKey(videoUrl), - url: videoUrl, - playing: playing, - fallback: AvatarImageContent( - imageUrl: poster, - fallback: fallback, - ), - ) - : controller != null && ready.value == controller - ? VideoPlayer(controller) - : AvatarImageContent(imageUrl: poster, fallback: fallback), + child: AvatarClip( + isAgent: true, + child: Transform.scale( + scale: animatedAvatarArtworkScale, + child: isAndroid && videoUrl != null + ? AndroidTransparentAvatar( + key: ValueKey(videoUrl), + url: videoUrl, + playing: playing, + fallback: AvatarImageContent( + imageUrl: poster, + fallback: fallback, + ), + ) + : controller != null && ready.value == controller + ? VideoPlayer(controller) + : AvatarImageContent(imageUrl: poster, fallback: fallback), + ), ), ); } diff --git a/mobile/test/features/agents/agent_avatar_picker_test.dart b/mobile/test/features/agents/agent_avatar_picker_test.dart index 04e487f28c..5032f2a1f0 100644 --- a/mobile/test/features/agents/agent_avatar_picker_test.dart +++ b/mobile/test/features/agents/agent_avatar_picker_test.dart @@ -67,6 +67,21 @@ void main() { await tester.pumpAndSettle(); expect(selection.url, isNot(agentAvatarCatalog.first.url)); final character = selection.url; + // Every preview uses the same squircle the agent gets once created. + void expectSquirclePreview() { + final previewClip = tester.widget( + find.descendant( + of: find.bySemanticsLabel('Selected agent avatar'), + matching: find.byType(ClipPath), + ), + ); + expect( + (previewClip.clipper! as ShapeBorderClipper).shape, + avatarShape(isAgent: true), + ); + } + + expectSquirclePreview(); await tester.runAsync(() async { rendered = Completer(); tester @@ -77,17 +92,7 @@ void main() { await tester.pumpAndSettle(); expect(selection.bytes, isNotNull); - // The preview uses the same squircle the agent gets once created. - final previewClip = tester.widget( - find.descendant( - of: find.bySemanticsLabel('Selected agent avatar'), - matching: find.byType(ClipPath), - ), - ); - expect( - (previewClip.clipper! as ShapeBorderClipper).shape, - avatarShape(isAgent: true), - ); + expectSquirclePreview(); expect( (tester .widget(