Fix assistant skill picker loading

This commit is contained in:
Haitao Pan 2026-05-28 11:03:50 +08:00
parent e29f56dd15
commit 5ed1100edb
3 changed files with 97 additions and 2 deletions

View File

@ -179,6 +179,7 @@ class ComposerBarStateInternal extends State<ComposerBarInternal> {
}
setState(resetSkillPickerSearchInternal);
skillPickerPortalControllerInternal.show();
refreshSkillsForPickerInternal();
WidgetsBinding.instance.addPostFrameCallback((_) {
if (!mounted || !skillPickerPortalControllerInternal.isShowing) {
return;
@ -187,6 +188,19 @@ class ComposerBarStateInternal extends State<ComposerBarInternal> {
});
}
void refreshSkillsForPickerInternal() {
final skillsController = widget.controller.skillsController;
if (widget.controller.skills.isNotEmpty || skillsController.loading) {
return;
}
final selectedAgentId = widget.controller.selectedAgentId.trim();
unawaited(
skillsController.refresh(
agentId: selectedAgentId.isEmpty ? null : selectedAgentId,
),
);
}
@override
void dispose() {
widget.controller.removeListener(handleControllerChangedInternal);

View File

@ -94,7 +94,7 @@ Widget buildSkillPickerOverlayForInternal(
searchFocusNode: state.skillPickerSearchFocusNodeInternal,
selectedSkillKeys: state.widget.selectedSkillKeys,
filteredSkills: state.filteredSkillOptionsInternal(),
isLoading: false,
isLoading: state.widget.controller.skillsController.loading,
hasQuery: state.skillPickerQueryInternal.trim().isNotEmpty,
onQueryChanged: state.setSkillPickerQueryInternal,
onToggleSkill: (skillKey) => state.widget.onToggleSkill(skillKey),

View File

@ -481,6 +481,85 @@ void main() {
expect(sendCount, 1);
});
testWidgets('skill picker shows loading state while skills refresh', (
tester,
) async {
final controller = AppController(
environmentOverride: const <String, String>{},
);
addTearDown(controller.dispose);
controller.skillsController.loadingInternal = true;
await controller.sessionsController.switchSession('unit-fixture-task-a');
await tester.pumpWidget(
_buildTestApp(child: _buildLowerPane(controller: controller)),
);
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('assistant-skill-picker-button')));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(find.text('正在加载技能…'), findsOneWidget);
expect(find.text('当前没有已加载技能。'), findsNothing);
});
testWidgets('skill picker displays refreshed skills and filters them', (
tester,
) async {
final controller = AppController(
environmentOverride: const <String, String>{},
);
addTearDown(controller.dispose);
await controller.sessionsController.switchSession('unit-fixture-task-a');
await tester.pumpWidget(
_buildTestApp(
child: _buildLowerPane(
controller: controller,
availableSkills: const <ComposerSkillOptionInternal>[
ComposerSkillOptionInternal(
key: 'browser-automation',
label: 'Browser Automation',
description: 'Automate browsers',
sourceLabel: 'agents-skills-personal',
groupLabel: 'Agent Skills',
groupSortOrder: 1,
icon: Icons.key_rounded,
),
ComposerSkillOptionInternal(
key: 'pdf',
label: 'PDF Writer',
description: 'Create PDF files',
sourceLabel: 'openclaw-workspace',
groupLabel: 'Workspace Skills',
groupSortOrder: 0,
icon: Icons.key_rounded,
),
],
),
),
);
await tester.pumpAndSettle();
await tester.tap(find.byKey(const Key('assistant-skill-picker-button')));
await tester.pumpAndSettle();
expect(find.text('Browser Automation'), findsOneWidget);
expect(find.text('PDF Writer'), findsOneWidget);
await tester.enterText(
find.byKey(const Key('assistant-skill-picker-search')),
'browser',
);
await tester.pumpAndSettle();
expect(find.text('Browser Automation'), findsOneWidget);
expect(find.text('PDF Writer'), findsNothing);
});
testWidgets('keeps bottom action row visible when pane height is reduced', (
tester,
) async {
@ -657,6 +736,8 @@ Widget _buildLowerPane({
required AppController controller,
TextEditingController? inputController,
Future<void> Function()? onSend,
List<ComposerSkillOptionInternal> availableSkills =
const <ComposerSkillOptionInternal>[],
}) {
final composerController = inputController ?? TextEditingController();
return SurfaceCard(
@ -670,7 +751,7 @@ Widget _buildLowerPane({
modelLabel: 'gpt-5.4',
modelOptions: const <String>[],
attachments: const <ComposerAttachmentInternal>[],
availableSkills: const <ComposerSkillOptionInternal>[],
availableSkills: availableSkills,
selectedSkillKeys: const <String>[],
onRemoveAttachment: (_) {},
onToggleSkill: (_) {},