feat: add settings sidebar back to chat action
This commit is contained in:
parent
dbe1cc5d58
commit
dd90c66f35
@ -379,6 +379,11 @@ class _AppShellState extends State<AppShell> {
|
||||
controller,
|
||||
visibleExecutionTargets,
|
||||
),
|
||||
onReturnToAssistant: () {
|
||||
controller.navigateTo(
|
||||
WorkspaceDestination.assistant,
|
||||
);
|
||||
},
|
||||
onSelectTask: (sessionKey) async {
|
||||
controller.navigateTo(
|
||||
WorkspaceDestination.assistant,
|
||||
|
||||
@ -46,6 +46,7 @@ class SidebarNavigation extends StatelessWidget {
|
||||
this.assistantSkillCount = 0,
|
||||
this.onRefreshTasks,
|
||||
this.onCreateTask,
|
||||
this.onReturnToAssistant,
|
||||
this.onSelectTask,
|
||||
this.onArchiveTask,
|
||||
this.onRenameTask,
|
||||
@ -81,6 +82,7 @@ class SidebarNavigation extends StatelessWidget {
|
||||
final int assistantSkillCount;
|
||||
final Future<void> Function()? onRefreshTasks;
|
||||
final Future<void> Function()? onCreateTask;
|
||||
final VoidCallback? onReturnToAssistant;
|
||||
final Future<void> Function(String sessionKey)? onSelectTask;
|
||||
final Future<void> Function(String sessionKey)? onArchiveTask;
|
||||
final Future<void> Function(String sessionKey, String title)? onRenameTask;
|
||||
@ -123,6 +125,7 @@ class SidebarNavigation extends StatelessWidget {
|
||||
if (!isCollapsed)
|
||||
Expanded(
|
||||
child: SidebarTaskSection(
|
||||
currentSection: currentSection,
|
||||
items: taskItems,
|
||||
visibleExecutionTargets: visibleExecutionTargets,
|
||||
skillCount: assistantSkillCount,
|
||||
@ -130,6 +133,7 @@ class SidebarNavigation extends StatelessWidget {
|
||||
onCycleSidebarState: onCycleSidebarState,
|
||||
onRefreshTasks: onRefreshTasks,
|
||||
onCreateTask: onCreateTask,
|
||||
onReturnToAssistant: onReturnToAssistant,
|
||||
onSelectTask: onSelectTask,
|
||||
onArchiveTask: onArchiveTask,
|
||||
onRenameTask: onRenameTask,
|
||||
|
||||
@ -25,6 +25,7 @@ class SidebarTaskItem {
|
||||
class SidebarTaskSection extends StatefulWidget {
|
||||
const SidebarTaskSection({
|
||||
super.key,
|
||||
required this.currentSection,
|
||||
required this.items,
|
||||
required this.visibleExecutionTargets,
|
||||
required this.skillCount,
|
||||
@ -32,11 +33,13 @@ class SidebarTaskSection extends StatefulWidget {
|
||||
required this.onCycleSidebarState,
|
||||
this.onRefreshTasks,
|
||||
this.onCreateTask,
|
||||
this.onReturnToAssistant,
|
||||
this.onSelectTask,
|
||||
this.onArchiveTask,
|
||||
this.onRenameTask,
|
||||
});
|
||||
|
||||
final WorkspaceDestination currentSection;
|
||||
final List<SidebarTaskItem> items;
|
||||
final List<AssistantExecutionTarget> visibleExecutionTargets;
|
||||
final int skillCount;
|
||||
@ -44,6 +47,7 @@ class SidebarTaskSection extends StatefulWidget {
|
||||
final VoidCallback onCycleSidebarState;
|
||||
final Future<void> Function()? onRefreshTasks;
|
||||
final Future<void> Function()? onCreateTask;
|
||||
final VoidCallback? onReturnToAssistant;
|
||||
final Future<void> Function(String sessionKey)? onSelectTask;
|
||||
final Future<void> Function(String sessionKey)? onArchiveTask;
|
||||
final Future<void> Function(String sessionKey, String title)? onRenameTask;
|
||||
@ -157,22 +161,26 @@ class _SidebarTaskSectionState extends State<SidebarTaskSection> {
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 0, 4, 8),
|
||||
child: FilledButton.tonalIcon(
|
||||
key: const Key('workspace-sidebar-new-task-button'),
|
||||
onPressed: widget.onCreateTask == null
|
||||
? null
|
||||
: () async {
|
||||
await widget.onCreateTask!();
|
||||
},
|
||||
icon: const Icon(Icons.edit_note_rounded),
|
||||
label: Text(appText('新对话', 'New conversation')),
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size(0, 40),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: widget.currentSection == WorkspaceDestination.settings
|
||||
? _SidebarBackToAssistantButton(
|
||||
onPressed: widget.onReturnToAssistant,
|
||||
)
|
||||
: FilledButton.tonalIcon(
|
||||
key: const Key('workspace-sidebar-new-task-button'),
|
||||
onPressed: widget.onCreateTask == null
|
||||
? null
|
||||
: () async {
|
||||
await widget.onCreateTask!();
|
||||
},
|
||||
icon: const Icon(Icons.edit_note_rounded),
|
||||
label: Text(appText('新对话', 'New conversation')),
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size(0, 40),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(4, 4, 4, 6),
|
||||
@ -341,6 +349,66 @@ class _SidebarTaskSectionState extends State<SidebarTaskSection> {
|
||||
}
|
||||
}
|
||||
|
||||
class _SidebarBackToAssistantButton extends StatelessWidget {
|
||||
const _SidebarBackToAssistantButton({required this.onPressed});
|
||||
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final palette = context.palette;
|
||||
final theme = Theme.of(context);
|
||||
return Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
key: const Key('workspace-sidebar-back-to-chat-button'),
|
||||
onTap: onPressed,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
child: Ink(
|
||||
height: 56,
|
||||
decoration: BoxDecoration(
|
||||
color: palette.surfaceSecondary,
|
||||
borderRadius: BorderRadius.circular(18),
|
||||
border: Border.all(color: palette.strokeSoft),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 36,
|
||||
height: 36,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: palette.textPrimary, width: 1.8),
|
||||
),
|
||||
child: Icon(
|
||||
Icons.arrow_back_rounded,
|
||||
size: 24,
|
||||
color: palette.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
appText('返回聊天', 'Back to chat'),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: palette.textPrimary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _SidebarTaskGroup {
|
||||
const _SidebarTaskGroup({required this.executionTarget, required this.items});
|
||||
|
||||
|
||||
BIN
test/golden/goldens/sidebar_navigation_settings_back_to_chat.png
Normal file
BIN
test/golden/goldens/sidebar_navigation_settings_back_to_chat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
@ -0,0 +1,50 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:golden_toolkit/golden_toolkit.dart';
|
||||
import 'package:xworkmate/i18n/app_language.dart';
|
||||
import 'package:xworkmate/models/app_models.dart';
|
||||
import 'package:xworkmate/widgets/sidebar_navigation.dart';
|
||||
|
||||
import '../helpers/golden_test_bootstrap.dart';
|
||||
|
||||
void main() {
|
||||
setUpAll(() async {
|
||||
await loadGoldenFonts();
|
||||
});
|
||||
|
||||
testGoldens('settings sidebar shows back to chat action', (tester) async {
|
||||
await pumpGoldenApp(
|
||||
tester,
|
||||
Align(
|
||||
alignment: Alignment.topLeft,
|
||||
child: SizedBox(
|
||||
width: 344,
|
||||
height: 920,
|
||||
child: SidebarNavigation(
|
||||
currentSection: WorkspaceDestination.settings,
|
||||
sidebarState: AppSidebarState.expanded,
|
||||
appLanguage: AppLanguage.zh,
|
||||
themeMode: ThemeMode.light,
|
||||
onSectionChanged: (_) {},
|
||||
onToggleLanguage: () {},
|
||||
onCycleSidebarState: () {},
|
||||
onExpandFromCollapsed: () {},
|
||||
onOpenHome: () {},
|
||||
onOpenAccount: () {},
|
||||
onOpenThemeToggle: () {},
|
||||
onReturnToAssistant: () {},
|
||||
accountName: 'Tester',
|
||||
accountSubtitle: 'Workspace',
|
||||
onToggleAccountWorkspaceFollowed: () async {},
|
||||
),
|
||||
),
|
||||
),
|
||||
size: const Size(400, 960),
|
||||
);
|
||||
|
||||
await screenMatchesGolden(
|
||||
tester,
|
||||
'sidebar_navigation_settings_back_to_chat',
|
||||
);
|
||||
});
|
||||
}
|
||||
@ -256,6 +256,10 @@ void main() {
|
||||
find.byKey(const Key('workspace-sidebar-new-task-button')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(
|
||||
find.byKey(const Key('workspace-sidebar-back-to-chat-button')),
|
||||
findsNothing,
|
||||
);
|
||||
expect(find.text('任务列表'), findsOneWidget);
|
||||
expect(find.text('自动化'), findsNothing);
|
||||
expect(find.text('MCP Hub'), findsNothing);
|
||||
@ -269,6 +273,55 @@ void main() {
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('SidebarNavigation shows back to chat action on settings page', (
|
||||
WidgetTester tester,
|
||||
) async {
|
||||
var returnedToAssistant = 0;
|
||||
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: AppTheme.light(),
|
||||
home: Scaffold(
|
||||
body: SidebarNavigation(
|
||||
currentSection: WorkspaceDestination.settings,
|
||||
sidebarState: AppSidebarState.expanded,
|
||||
appLanguage: AppLanguage.zh,
|
||||
themeMode: ThemeMode.light,
|
||||
onSectionChanged: (_) {},
|
||||
onToggleLanguage: () {},
|
||||
onCycleSidebarState: () {},
|
||||
onExpandFromCollapsed: () {},
|
||||
onOpenHome: () {},
|
||||
onOpenAccount: () {},
|
||||
onOpenThemeToggle: () {},
|
||||
accountName: 'Tester',
|
||||
accountSubtitle: 'Workspace',
|
||||
onToggleAccountWorkspaceFollowed: () async {},
|
||||
onReturnToAssistant: () => returnedToAssistant++,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(
|
||||
find.byKey(const Key('workspace-sidebar-back-to-chat-button')),
|
||||
findsOneWidget,
|
||||
);
|
||||
expect(find.text('返回聊天'), findsOneWidget);
|
||||
expect(
|
||||
find.byKey(const Key('workspace-sidebar-new-task-button')),
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
await tester.tap(
|
||||
find.byKey(const Key('workspace-sidebar-back-to-chat-button')),
|
||||
);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(returnedToAssistant, 1);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'SidebarNavigation merges local and remote tasks into one gateway group',
|
||||
(WidgetTester tester) async {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user