From fc2d354816f9be20c7cc5802e410eadd01f4b02a Mon Sep 17 00:00:00 2001 From: Haitao Pan Date: Sun, 15 Mar 2026 09:21:47 +0800 Subject: [PATCH] feat: refine assistant task workspace --- .../desktop_navigation_flow_test.dart | 2 +- lib/app/app_shell.dart | 2 +- lib/features/assistant/assistant_page.dart | 456 +++++++++++------- lib/widgets/sidebar_navigation.dart | 2 +- test/features/assistant_page_test.dart | 10 +- test/widget_test.dart | 4 +- 6 files changed, 280 insertions(+), 196 deletions(-) diff --git a/integration_test/desktop_navigation_flow_test.dart b/integration_test/desktop_navigation_flow_test.dart index d3a3986f..54f3b30f 100644 --- a/integration_test/desktop_navigation_flow_test.dart +++ b/integration_test/desktop_navigation_flow_test.dart @@ -12,7 +12,7 @@ void main() { ) async { await pumpDesktopApp(tester); - expect(find.text('新线程'), findsWidgets); + expect(find.text('新对话'), findsWidgets); await tester.tap(find.text('节点')); await settleIntegrationUi(tester); diff --git a/lib/app/app_shell.dart b/lib/app/app_shell.dart index 2ac0b390..a6979a00 100644 --- a/lib/app/app_shell.dart +++ b/lib/app/app_shell.dart @@ -288,7 +288,7 @@ class _AppShellState extends State { Positioned( left: 0, top: 18, - bottom: 18, + bottom: 0, child: _SidebarRevealRail( onExpand: () => controller.setSidebarState( AppSidebarState.expanded, diff --git a/lib/features/assistant/assistant_page.dart b/lib/features/assistant/assistant_page.dart index 41a13875..7f31cce9 100644 --- a/lib/features/assistant/assistant_page.dart +++ b/lib/features/assistant/assistant_page.dart @@ -73,10 +73,10 @@ class _AssistantPageState extends State { final controller = widget.controller; final messages = List.from(controller.chatMessages); final timelineItems = _buildTimelineItems(controller, messages); - final threads = _buildThreadEntries(controller); - final visibleThreads = _filterThreads(threads); - final currentThread = _resolveCurrentThread( - threads, + final tasks = _buildTaskEntries(controller); + final visibleTasks = _filterTasks(tasks); + final currentTask = _resolveCurrentTask( + tasks, controller.currentSessionKey, ); @@ -92,14 +92,14 @@ class _AssistantPageState extends State { }); return Padding( - padding: const EdgeInsets.fromLTRB(6, 6, 6, 6), + padding: const EdgeInsets.fromLTRB(6, 6, 6, 0), child: LayoutBuilder( builder: (context, constraints) { final showThreadRail = constraints.maxWidth >= 1180; final mainWorkspace = _buildMainWorkspace( controller: controller, timelineItems: timelineItems, - currentThreadTitle: currentThread.title, + currentTask: currentTask, ); if (!showThreadRail) { return mainWorkspace; @@ -116,10 +116,10 @@ class _AssistantPageState extends State { children: [ SizedBox( width: threadRailWidth, - child: _AssistantThreadRail( - key: const Key('assistant-thread-rail'), + child: _AssistantTaskRail( + key: const Key('assistant-task-rail'), controller: controller, - threads: visibleThreads, + tasks: visibleTasks, query: _threadQuery, searchController: _threadSearchController, onQueryChanged: (value) { @@ -133,15 +133,15 @@ class _AssistantPageState extends State { _threadQuery = ''; }); }, - onRefreshSessions: controller.refreshSessions, - onCreateThread: _createNewThread, + onRefreshTasks: controller.refreshSessions, + onCreateTask: _createNewThread, onOpenTasks: () { controller.navigateTo(WorkspaceDestination.tasks); }, onOpenSkills: () { controller.navigateTo(WorkspaceDestination.skills); }, - onSelectThread: (sessionKey) async { + onSelectTask: (sessionKey) async { await controller.switchSession(sessionKey); _focusComposer(); }, @@ -174,7 +174,7 @@ class _AssistantPageState extends State { Widget _buildMainWorkspace({ required AppController controller, required List<_TimelineItem> timelineItems, - required String currentThreadTitle, + required _AssistantTaskEntry currentTask, }) { return LayoutBuilder( builder: (context, constraints) { @@ -214,7 +214,7 @@ class _AssistantPageState extends State { height: conversationHeight, child: _ConversationArea( controller: controller, - currentThreadTitle: currentThreadTitle, + currentTask: currentTask, items: timelineItems, scrollController: _conversationController, onOpenDetail: widget.onOpenDetail, @@ -582,7 +582,7 @@ class _AssistantPageState extends State { _focusComposer(); } - List<_AssistantThreadEntry> _buildThreadEntries(AppController controller) { + List<_AssistantTaskEntry> _buildTaskEntries(AppController controller) { final sessions = controller.sessions.toList(growable: false) ..sort( (left, right) => @@ -590,18 +590,20 @@ class _AssistantPageState extends State { ); final entries = sessions .map( - (session) => _AssistantThreadEntry( + (session) => _AssistantTaskEntry( sessionKey: session.key, title: _sessionDisplayTitle(session), preview: _sessionPreview(session) ?? - appText('等待继续对话', 'Waiting to continue the thread'), + appText('等待继续执行这个任务', 'Waiting to continue this task'), status: _sessionStatus( session, currentSessionKey: controller.currentSessionKey, hasPendingRun: controller.chatController.hasPendingRun, ), updatedAtLabel: _sessionUpdatedAtLabel(session.updatedAtMs), + owner: controller.activeAgentName, + surface: session.surface ?? session.kind ?? 'Assistant', isCurrent: _sessionKeysMatch( session.key, controller.currentSessionKey, @@ -614,15 +616,17 @@ class _AssistantPageState extends State { )) { entries.insert( 0, - _AssistantThreadEntry( + _AssistantTaskEntry( sessionKey: controller.currentSessionKey, title: _fallbackSessionTitle(controller.currentSessionKey), preview: appText( - '等待发送第一条消息', - 'Waiting for the first message', + '等待描述这个任务的第一条消息', + 'Waiting for the first message of this task', ), status: 'queued', updatedAtLabel: appText('现在', 'Now'), + owner: controller.activeAgentName, + surface: 'Assistant', isCurrent: true, draft: true, ), @@ -631,7 +635,7 @@ class _AssistantPageState extends State { return entries; } - List<_AssistantThreadEntry> _filterThreads(List<_AssistantThreadEntry> items) { + List<_AssistantTaskEntry> _filterTasks(List<_AssistantTaskEntry> items) { final query = _threadQuery.trim().toLowerCase(); if (query.isEmpty) { return items; @@ -643,8 +647,8 @@ class _AssistantPageState extends State { }).toList(growable: false); } - _AssistantThreadEntry _resolveCurrentThread( - List<_AssistantThreadEntry> items, + _AssistantTaskEntry _resolveCurrentTask( + List<_AssistantTaskEntry> items, String sessionKey, ) { for (final item in items) { @@ -652,12 +656,14 @@ class _AssistantPageState extends State { return item; } } - return _AssistantThreadEntry( + return _AssistantTaskEntry( sessionKey: sessionKey, title: _fallbackSessionTitle(sessionKey), preview: '', status: 'queued', updatedAtLabel: appText('现在', 'Now'), + owner: widget.controller.activeAgentName, + surface: 'Assistant', isCurrent: true, draft: true, ); @@ -716,26 +722,29 @@ class _AssistantLowerPane extends StatelessWidget { @override Widget build(BuildContext context) { - return SingleChildScrollView( - physics: const ClampingScrollPhysics(), - child: _ComposerBar( - controller: controller, - inputController: inputController, - focusNode: focusNode, - mode: mode, - thinkingLabel: thinkingLabel, - modelLabel: modelLabel, - modelOptions: modelOptions, - attachments: attachments, - autoAgentLabel: autoAgentLabel, - onModeChanged: onModeChanged, - onThinkingChanged: onThinkingChanged, - onModelChanged: onModelChanged, - onRemoveAttachment: onRemoveAttachment, - onOpenGateway: onOpenGateway, - onReconnectGateway: onReconnectGateway, - onPickAttachments: onPickAttachments, - onSend: onSend, + return Align( + alignment: Alignment.bottomCenter, + child: SingleChildScrollView( + physics: const ClampingScrollPhysics(), + child: _ComposerBar( + controller: controller, + inputController: inputController, + focusNode: focusNode, + mode: mode, + thinkingLabel: thinkingLabel, + modelLabel: modelLabel, + modelOptions: modelOptions, + attachments: attachments, + autoAgentLabel: autoAgentLabel, + onModeChanged: onModeChanged, + onThinkingChanged: onThinkingChanged, + onModelChanged: onModelChanged, + onRemoveAttachment: onRemoveAttachment, + onOpenGateway: onOpenGateway, + onReconnectGateway: onReconnectGateway, + onPickAttachments: onPickAttachments, + onSend: onSend, + ), ), ); } @@ -744,7 +753,7 @@ class _AssistantLowerPane extends StatelessWidget { class _ConversationArea extends StatelessWidget { const _ConversationArea({ required this.controller, - required this.currentThreadTitle, + required this.currentTask, required this.items, required this.scrollController, required this.onOpenDetail, @@ -754,7 +763,7 @@ class _ConversationArea extends StatelessWidget { }); final AppController controller; - final String currentThreadTitle; + final _AssistantTaskEntry currentTask; final List<_TimelineItem> items; final ScrollController scrollController; final ValueChanged onOpenDetail; @@ -766,6 +775,17 @@ class _ConversationArea extends StatelessWidget { Widget build(BuildContext context) { final palette = context.palette; final theme = Theme.of(context); + final statusStyle = _pillStyleForStatus(context, currentTask.status); + final taskHint = controller.connection.status == + RuntimeConnectionStatus.connected + ? appText( + '当前对话会作为任务上下文持续执行,切换左侧任务即可回到对应会话。', + 'This conversation stays attached to the selected task. Pick another task on the left to jump back into it.', + ) + : appText( + '连接 Gateway 后,当前对话会自动作为默认任务开始执行。', + 'After connecting a gateway, this conversation starts as the default task.', + ); return SurfaceCard( borderRadius: 12, @@ -781,24 +801,37 @@ class _ConversationArea extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( - currentThreadTitle, + currentTask.title, key: const Key('assistant-conversation-title'), style: theme.textTheme.titleLarge, ), const SizedBox(height: 4), Text( - controller.connection.status == - RuntimeConnectionStatus.connected - ? appText( - '自然描述任务即可,XWorkmate 会自动路由执行。', - 'Describe the task naturally. XWorkmate will route execution.', - ) - : appText( - '连接 Gateway 后可开始对话和运行任务。', - 'Connect a gateway to start chatting and running tasks.', - ), + taskHint, style: theme.textTheme.bodySmall, ), + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _StatusPill( + label: currentTask.draft + ? appText('草稿任务', 'Draft task') + : _taskStatusLabel(currentTask.status), + backgroundColor: statusStyle.backgroundColor, + textColor: statusStyle.foregroundColor, + ), + _MetaPill( + label: currentTask.owner, + icon: Icons.smart_toy_outlined, + ), + _MetaPill( + label: currentTask.surface, + icon: Icons.forum_outlined, + ), + ], + ), ], ), ), @@ -939,38 +972,44 @@ class _ConversationArea extends StatelessWidget { } } -class _AssistantThreadRail extends StatelessWidget { - const _AssistantThreadRail({ +class _AssistantTaskRail extends StatelessWidget { + const _AssistantTaskRail({ super.key, required this.controller, - required this.threads, + required this.tasks, required this.query, required this.searchController, required this.onQueryChanged, required this.onClearQuery, - required this.onRefreshSessions, - required this.onCreateThread, + required this.onRefreshTasks, + required this.onCreateTask, required this.onOpenTasks, required this.onOpenSkills, - required this.onSelectThread, + required this.onSelectTask, }); final AppController controller; - final List<_AssistantThreadEntry> threads; + final List<_AssistantTaskEntry> tasks; final String query; final TextEditingController searchController; final ValueChanged onQueryChanged; final VoidCallback onClearQuery; - final Future Function() onRefreshSessions; - final Future Function() onCreateThread; + final Future Function() onRefreshTasks; + final Future Function() onCreateTask; final VoidCallback onOpenTasks; final VoidCallback onOpenSkills; - final Future Function(String sessionKey) onSelectThread; + final Future Function(String sessionKey) onSelectTask; @override Widget build(BuildContext context) { final theme = Theme.of(context); final palette = context.palette; + final runningCount = tasks + .where((task) => _normalizedTaskStatus(task.status) == 'running') + .length; + final completedCount = tasks + .where((task) => _normalizedTaskStatus(task.status) == 'completed') + .length; return SurfaceCard( borderRadius: 16, @@ -986,11 +1025,11 @@ class _AssistantThreadRail extends StatelessWidget { children: [ Expanded( child: TextField( - key: const Key('assistant-thread-search'), + key: const Key('assistant-task-search'), controller: searchController, onChanged: onQueryChanged, decoration: InputDecoration( - hintText: appText('搜索线程', 'Search threads'), + hintText: appText('搜索任务', 'Search tasks'), prefixIcon: const Icon(Icons.search_rounded), suffixIcon: query.isEmpty ? null @@ -1004,10 +1043,10 @@ class _AssistantThreadRail extends StatelessWidget { ), const SizedBox(width: 8), IconButton( - key: const Key('assistant-thread-refresh'), - tooltip: appText('刷新线程', 'Refresh threads'), + key: const Key('assistant-task-refresh'), + tooltip: appText('刷新任务', 'Refresh tasks'), onPressed: () async { - await onRefreshSessions(); + await onRefreshTasks(); }, icon: const Icon(Icons.refresh_rounded), ), @@ -1017,51 +1056,85 @@ class _AssistantThreadRail extends StatelessWidget { SizedBox( width: double.infinity, child: FilledButton.tonalIcon( - key: const Key('assistant-new-thread-button'), + key: const Key('assistant-new-task-button'), onPressed: () async { - await onCreateThread(); + await onCreateTask(); }, icon: const Icon(Icons.edit_note_rounded), - label: Text(appText('新线程', 'New thread')), + label: Text(appText('新对话', 'New conversation')), ), ), const SizedBox(height: 12), - Text( - appText('工作台', 'Workspace'), - style: theme.textTheme.labelLarge?.copyWith( - color: palette.textSecondary, + Container( + width: double.infinity, + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + color: palette.surfaceSecondary, + borderRadius: BorderRadius.circular(14), + border: Border.all(color: palette.strokeSoft), ), - ), - const SizedBox(height: 8), - Row( - children: [ - Expanded( - child: _AssistantRailAction( - icon: Icons.play_circle_outline_rounded, - label: appText('运行中', 'Running'), - value: '${controller.tasksController.running.length}', - onTap: onOpenTasks, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + appText('当前对话就是默认任务', 'This chat is the default task'), + style: theme.textTheme.titleSmall?.copyWith( + color: theme.colorScheme.onSurface, + fontWeight: FontWeight.w600, + ), ), - ), - const SizedBox(width: 8), - Expanded( - child: _AssistantRailAction( - icon: Icons.event_repeat_rounded, - label: appText('计划中', 'Scheduled'), - value: '${controller.tasksController.scheduled.length}', - onTap: onOpenTasks, + const SizedBox(height: 4), + Text( + appText( + '左侧选择任一任务,会直接切到这个任务对应的会话上下文。', + 'Selecting a task on the left jumps straight into that task conversation.', + ), + style: theme.textTheme.bodySmall?.copyWith( + color: palette.textSecondary, + height: 1.35, + ), ), - ), - const SizedBox(width: 8), - Expanded( - child: _AssistantRailAction( - icon: Icons.auto_awesome_rounded, - label: appText('技能', 'Skills'), - value: '${controller.skills.length}', - onTap: onOpenSkills, + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + _MetaPill( + label: + '${appText('运行中', 'Running')} $runningCount', + icon: Icons.play_circle_outline_rounded, + ), + _MetaPill( + label: + '${appText('已完成', 'Completed')} $completedCount', + icon: Icons.check_circle_outline_rounded, + ), + _MetaPill( + label: + '${appText('技能', 'Skills')} ${controller.skills.length}', + icon: Icons.auto_awesome_rounded, + ), + ], ), - ), - ], + const SizedBox(height: 10), + Wrap( + spacing: 8, + runSpacing: 8, + children: [ + TextButton.icon( + onPressed: onOpenTasks, + icon: const Icon(Icons.layers_outlined, size: 18), + label: Text(appText('打开任务页', 'Open tasks')), + ), + TextButton.icon( + onPressed: onOpenSkills, + icon: const Icon(Icons.hub_outlined, size: 18), + label: Text(appText('查看技能', 'Open skills')), + ), + ], + ), + ], + ), ), ], ), @@ -1072,12 +1145,12 @@ class _AssistantThreadRail extends StatelessWidget { child: Row( children: [ Text( - appText('线程', 'Threads'), + appText('任务列表', 'Task list'), style: theme.textTheme.titleSmall, ), const SizedBox(width: 8), Text( - '${threads.length}', + '${tasks.length}', style: theme.textTheme.bodySmall?.copyWith( color: palette.textMuted, ), @@ -1086,14 +1159,14 @@ class _AssistantThreadRail extends StatelessWidget { ), ), Expanded( - child: threads.isEmpty + child: tasks.isEmpty ? Center( child: Padding( padding: const EdgeInsets.all(16), child: Text( appText( - '没有匹配的线程,试试新建一个。', - 'No matching threads. Start a new one.', + '没有匹配的任务,试试新建一个。', + 'No matching tasks. Start a new one.', ), textAlign: TextAlign.center, style: theme.textTheme.bodyMedium?.copyWith( @@ -1104,14 +1177,14 @@ class _AssistantThreadRail extends StatelessWidget { ) : ListView.separated( padding: const EdgeInsets.fromLTRB(8, 0, 8, 8), - itemCount: threads.length, + itemCount: tasks.length, separatorBuilder: (_, _) => const SizedBox(height: 6), itemBuilder: (context, index) { - final thread = threads[index]; - return _AssistantThreadTile( - entry: thread, + final task = tasks[index]; + return _AssistantTaskTile( + entry: task, onTap: () async { - await onSelectThread(thread.sessionKey); + await onSelectTask(task.sessionKey); }, ); }, @@ -1123,64 +1196,10 @@ class _AssistantThreadRail extends StatelessWidget { } } -class _AssistantRailAction extends StatelessWidget { - const _AssistantRailAction({ - required this.icon, - required this.label, - required this.value, - required this.onTap, - }); +class _AssistantTaskTile extends StatelessWidget { + const _AssistantTaskTile({required this.entry, required this.onTap}); - final IconData icon; - final String label; - final String value; - final VoidCallback onTap; - - @override - Widget build(BuildContext context) { - final palette = context.palette; - final theme = Theme.of(context); - - return Material( - color: palette.surfaceSecondary, - borderRadius: BorderRadius.circular(12), - child: InkWell( - borderRadius: BorderRadius.circular(12), - onTap: onTap, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 10), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Icon(icon, size: 18, color: palette.textMuted), - const SizedBox(height: 8), - Text( - value, - style: theme.textTheme.titleMedium?.copyWith( - color: theme.colorScheme.onSurface, - ), - ), - const SizedBox(height: 2), - Text( - label, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.textTheme.bodySmall?.copyWith( - color: palette.textSecondary, - ), - ), - ], - ), - ), - ), - ); - } -} - -class _AssistantThreadTile extends StatelessWidget { - const _AssistantThreadTile({required this.entry, required this.onTap}); - - final _AssistantThreadEntry entry; + final _AssistantTaskEntry entry; final VoidCallback onTap; @override @@ -1195,7 +1214,7 @@ class _AssistantThreadTile extends StatelessWidget { : Colors.transparent, borderRadius: BorderRadius.circular(12), child: InkWell( - key: ValueKey('assistant-thread-${entry.sessionKey}'), + key: ValueKey('assistant-task-${entry.sessionKey}'), borderRadius: BorderRadius.circular(12), onTap: onTap, child: Container( @@ -1213,12 +1232,20 @@ class _AssistantThreadTile extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - width: 8, - height: 8, - margin: const EdgeInsets.only(top: 6), + width: 32, + height: 32, decoration: BoxDecoration( + color: statusStyle.backgroundColor, + borderRadius: BorderRadius.circular(10), + ), + child: Icon( + entry.draft + ? Icons.edit_note_rounded + : _normalizedTaskStatus(entry.status) == 'running' + ? Icons.play_arrow_rounded + : Icons.task_alt_rounded, + size: 18, color: statusStyle.foregroundColor, - shape: BoxShape.circle, ), ), const SizedBox(width: 8), @@ -1259,11 +1286,25 @@ class _AssistantThreadTile extends StatelessWidget { children: [ _StatusPill( label: entry.draft - ? appText('草稿', 'Draft') + ? appText('草稿任务', 'Draft task') : _taskStatusLabel(entry.status), backgroundColor: statusStyle.backgroundColor, textColor: statusStyle.foregroundColor, ), + const SizedBox(width: 6), + Flexible( + child: _MetaPill( + label: entry.owner, + icon: Icons.smart_toy_outlined, + ), + ), + const SizedBox(width: 6), + Flexible( + child: _MetaPill( + label: entry.surface, + icon: Icons.forum_outlined, + ), + ), const Spacer(), if (entry.isCurrent) Text( @@ -2384,13 +2425,15 @@ class _TimelineItem { final bool error; } -class _AssistantThreadEntry { - const _AssistantThreadEntry({ +class _AssistantTaskEntry { + const _AssistantTaskEntry({ required this.sessionKey, required this.title, required this.preview, required this.status, required this.updatedAtLabel, + required this.owner, + required this.surface, required this.isCurrent, this.draft = false, }); @@ -2400,6 +2443,8 @@ class _AssistantThreadEntry { final String preview; final String status; final String updatedAtLabel; + final String owner; + final String surface; final bool isCurrent; final bool draft; } @@ -2414,6 +2459,45 @@ class _PillStyle { final Color foregroundColor; } +class _MetaPill extends StatelessWidget { + const _MetaPill({required this.label, required this.icon}); + + final String label; + final IconData icon; + + @override + Widget build(BuildContext context) { + final palette = context.palette; + final theme = Theme.of(context); + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), + decoration: BoxDecoration( + color: palette.surfaceSecondary, + borderRadius: BorderRadius.circular(999), + border: Border.all(color: palette.strokeSoft), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(icon, size: 14, color: palette.textMuted), + const SizedBox(width: 6), + Flexible( + child: Text( + label, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: theme.textTheme.labelMedium?.copyWith( + color: palette.textSecondary, + ), + ), + ), + ], + ), + ); + } +} + _PillStyle _pillStyleForStatus(BuildContext context, String label) { final theme = Theme.of(context); final normalized = _normalizedTaskStatus(label); @@ -2495,16 +2579,16 @@ String _sessionDisplayTitle(GatewaySessionSummary session) { String _fallbackSessionTitle(String sessionKey) { final trimmed = sessionKey.trim(); if (trimmed == 'main' || trimmed == 'agent:main:main') { - return appText('主线程', 'Main thread'); + return appText('默认任务', 'Default task'); } if (trimmed.startsWith('draft:')) { - return appText('新线程', 'New thread'); + return appText('新对话', 'New conversation'); } final parts = trimmed.split(':'); if (parts.length >= 3 && parts.first == 'agent' && parts.last == 'main') { - return appText('主线程', 'Main thread'); + return appText('默认任务', 'Default task'); } - return trimmed.isEmpty ? appText('未命名线程', 'Untitled thread') : trimmed; + return trimmed.isEmpty ? appText('未命名对话', 'Untitled conversation') : trimmed; } String? _sessionPreview(GatewaySessionSummary session) { diff --git a/lib/widgets/sidebar_navigation.dart b/lib/widgets/sidebar_navigation.dart index 87773118..d0b47f9c 100644 --- a/lib/widgets/sidebar_navigation.dart +++ b/lib/widgets/sidebar_navigation.dart @@ -373,7 +373,7 @@ class _SidebarNavItemState extends State<_SidebarNavItem> { String _sectionLabel(WorkspaceDestination section) { return switch (section) { - WorkspaceDestination.assistant => appText('新线程', 'New thread'), + WorkspaceDestination.assistant => appText('新对话', 'New conversation'), WorkspaceDestination.tasks => appText('自动化', 'Automation'), WorkspaceDestination.skills => appText('技能', 'Skills'), WorkspaceDestination.nodes => appText('节点', 'Nodes'), diff --git a/test/features/assistant_page_test.dart b/test/features/assistant_page_test.dart index 12d30727..a9a865d1 100644 --- a/test/features/assistant_page_test.dart +++ b/test/features/assistant_page_test.dart @@ -15,20 +15,20 @@ void main() { child: AssistantPage(controller: controller, onOpenDetail: (_) {}), ); - expect(find.byKey(const Key('assistant-thread-rail')), findsOneWidget); + expect(find.byKey(const Key('assistant-task-rail')), findsOneWidget); final titleBefore = tester.widget( find.byKey(const Key('assistant-conversation-title')), ); - expect(titleBefore.data, '主线程'); + expect(titleBefore.data, '默认任务'); - await tester.tap(find.byKey(const Key('assistant-new-thread-button'))); + await tester.tap(find.byKey(const Key('assistant-new-task-button'))); await tester.pumpAndSettle(); final titleAfter = tester.widget( find.byKey(const Key('assistant-conversation-title')), ); - expect(titleAfter.data, '新线程'); + expect(titleAfter.data, '新对话'); }); testWidgets('AssistantPage narrow layout keeps existing single-pane flow', ( @@ -42,7 +42,7 @@ void main() { child: AssistantPage(controller: controller, onOpenDetail: (_) {}), ); - expect(find.byKey(const Key('assistant-thread-rail')), findsNothing); + expect(find.byKey(const Key('assistant-task-rail')), findsNothing); expect(find.byKey(const Key('assistant-conversation-title')), findsOneWidget); }); diff --git a/test/widget_test.dart b/test/widget_test.dart index 500c61e5..4ebf586a 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -15,7 +15,7 @@ void main() { await tester.pumpWidget(const XWorkmateApp()); await tester.pumpAndSettle(); - expect(find.text('新线程'), findsWidgets); - expect(find.text('连接 Gateway 后可开始对话和运行任务。'), findsOneWidget); + expect(find.text('新对话'), findsWidgets); + expect(find.text('连接 Gateway 后,当前对话会自动作为默认任务开始执行。'), findsOneWidget); }); }