From 4b7dd4fa4a871d3d640e5520dbc123798a745040 Mon Sep 17 00:00:00 2001 From: limityan Date: Sun, 26 Apr 2026 09:37:07 +0800 Subject: [PATCH 1/4] style: relax chat area max-width for larger screens Increase chat content max-width from 900px to 1200px with a new 1400px breakpoint (1000px) for better utilization on high-resolution displays. Also widen input (700px -> 900px) and empty state (650px -> 800px) to stay proportional. Generated with BitFun Co-Authored-By: BitFun --- package.json | 1 + .../core/src/agentic/persistence/manager.rs | 1 + src/crates/core/src/service/session/types.rs | 11 + .../agents/components/ReviewTeamPage.scss | 176 ++++++--------- .../agents/components/ReviewTeamPage.tsx | 203 ++++++++---------- .../flow_chat/components/ChatEmptyState.scss | 2 +- .../src/flow_chat/components/ChatInput.scss | 4 +- .../components/modern/ExploreRegion.scss | 19 +- .../modern/ProcessingIndicator.scss | 13 +- .../modern/TerminalGroupRenderer.scss | 12 +- .../modern/VirtualItemRenderer.scss | 12 +- .../flow-chat-manager/EventHandlerModule.ts | 6 + .../src/flow_chat/store/FlowChatStore.ts | 1 + .../tool-cards/FileOperationToolCard.tsx | 8 +- .../src/shared/types/session-history.ts | 2 + 15 files changed, 215 insertions(+), 256 deletions(-) diff --git a/package.json b/package.json index 15f520d2d..945efa9cd 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "desktop:build:release-fast": "node scripts/desktop-tauri-build.mjs --no-bundle -- --profile release-fast", "desktop:build:exe": "node scripts/desktop-tauri-build.mjs --no-bundle", "desktop:build:nsis": "node scripts/desktop-tauri-build.mjs --bundles nsis", + "desktop:build:nsis:fast": "node scripts/desktop-tauri-build.mjs --bundles nsis -- --profile release-fast", "desktop:build:arm64": "node scripts/desktop-tauri-build.mjs --target aarch64-apple-darwin --bundles dmg", "desktop:build:x86_64": "node scripts/desktop-tauri-build.mjs --target x86_64-apple-darwin --bundles dmg", "desktop:build:linux": "node scripts/desktop-tauri-build.mjs", diff --git a/src/crates/core/src/agentic/persistence/manager.rs b/src/crates/core/src/agentic/persistence/manager.rs index 3763e0fae..eb7ec8246 100644 --- a/src/crates/core/src/agentic/persistence/manager.rs +++ b/src/crates/core/src/agentic/persistence/manager.rs @@ -661,6 +661,7 @@ impl PersistenceManager { todos: existing.and_then(|value| value.todos.clone()), workspace_path: Some(workspace_root), workspace_hostname, + unread_completion: existing.and_then(|value| value.unread_completion.clone()), } } diff --git a/src/crates/core/src/service/session/types.rs b/src/crates/core/src/service/session/types.rs index e08d98eb4..c5f458d7b 100644 --- a/src/crates/core/src/service/session/types.rs +++ b/src/crates/core/src/service/session/types.rs @@ -90,6 +90,17 @@ pub struct SessionMetadata { alias = "workspace_hostname" )] pub workspace_hostname: Option, + + /// Unread completion status for the session. + /// 'completed' → green dot, 'error' → red dot. + /// Cleared after the user switches to the session and the content renders. + #[serde( + default, + skip_serializing_if = "Option::is_none", + alias = "unread_completion", + alias = "unreadCompletion" + )] + pub unread_completion: Option, } /// Session status diff --git a/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss b/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss index dd4fbdb06..01b43c5a6 100644 --- a/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss +++ b/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.scss @@ -32,7 +32,7 @@ &__policy-panel { display: grid; - grid-template-columns: minmax(220px, 1fr) minmax(360px, 1.25fr) auto; + grid-template-columns: minmax(220px, 1fr) minmax(360px, 1.25fr); align-items: center; gap: $size-gap-4; width: 100%; @@ -79,7 +79,7 @@ &__policy-metrics { display: grid; - grid-template-columns: repeat(auto-fit, minmax(86px, 1fr)); + grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 8px; min-width: 0; } @@ -121,20 +121,6 @@ overflow: visible; } - &__policy-action { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 6px; - min-width: max-content; - padding: 7px 10px; - border-radius: 8px; - background: color-mix(in srgb, var(--color-accent-500) 10%, transparent); - color: var(--color-accent-500); - font-size: var(--font-size-xs); - font-weight: $font-weight-medium; - } - &__error-detail { max-height: 220px; overflow: auto; @@ -283,56 +269,49 @@ color: var(--color-text-muted); } - &__member-grid { + &__member-layout { display: grid; - gap: $size-gap-3; - grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: $size-gap-4; + grid-template-columns: 280px 1fr; + align-items: start; } - &__member-card { + &__member-list { display: flex; flex-direction: column; + gap: 6px; + min-width: 0; + } + + &__member-list-item { + display: flex; + align-items: center; + gap: 10px; width: 100%; - border-radius: 8px; + padding: 10px 12px; border: 1px solid var(--border-subtle); + border-radius: 8px; background: color-mix(in srgb, var(--element-bg-soft) 78%, transparent); + color: var(--color-text-primary); text-align: left; + cursor: pointer; transition: - transform $motion-fast $easing-standard, + background $motion-fast $easing-standard, border-color $motion-fast $easing-standard, box-shadow $motion-fast $easing-standard; &:hover { - transform: translateY(-1px); border-color: color-mix(in srgb, var(--member-accent, #64748b) 34%, var(--border-medium)); - box-shadow: 0 10px 22px color-mix(in srgb, var(--shadow-color, #0f172a) 10%, transparent); + background: color-mix(in srgb, var(--element-bg-soft) 100%, transparent); } &.is-selected { border-color: color-mix(in srgb, var(--member-accent, #64748b) 54%, var(--color-bg-primary)); + background: color-mix(in srgb, var(--member-accent, #64748b) 10%, var(--element-bg-soft)); box-shadow: - 0 0 0 1px color-mix(in srgb, var(--member-accent, #64748b) 30%, transparent), - 0 12px 24px color-mix(in srgb, var(--shadow-color, #0f172a) 10%, transparent); - } - - &.is-expanded { - grid-column: 1 / -1; + 0 0 0 1px color-mix(in srgb, var(--member-accent, #64748b) 24%, transparent), + inset 3px 0 0 color-mix(in srgb, var(--member-accent, #64748b) 60%, transparent); } - } - - &__member-card-header { - display: flex; - align-items: flex-start; - gap: $size-gap-3; - width: 100%; - min-height: 148px; - padding: 14px; - border: 0; - border-radius: inherit; - background: transparent; - color: var(--color-text-primary); - text-align: left; - cursor: pointer; &:focus-visible { outline: none; @@ -340,95 +319,60 @@ } } - &__member-card-icon { + &__member-list-icon { display: inline-flex; align-items: center; justify-content: center; - width: 42px; - height: 42px; - border-radius: 8px; - background: color-mix(in srgb, var(--member-accent, #64748b) 18%, transparent); + width: 32px; + height: 32px; + border-radius: 6px; + background: color-mix(in srgb, var(--member-accent, #64748b) 16%, transparent); color: color-mix(in srgb, var(--member-accent, #64748b) 78%, var(--color-text-primary)); - border: 1px solid color-mix(in srgb, var(--member-accent, #64748b) 22%, transparent); + border: 1px solid color-mix(in srgb, var(--member-accent, #64748b) 20%, transparent); flex-shrink: 0; } - &__member-card-body { + &__member-list-body { display: flex; flex-direction: column; - gap: 8px; + gap: 2px; min-width: 0; flex: 1; } - &__member-card-top { - display: flex; - align-items: flex-start; - justify-content: space-between; - gap: $size-gap-2; - } - - &__member-card-name { - font-size: var(--font-size-base); + &__member-list-name { + font-size: var(--font-size-sm); font-weight: $font-weight-semibold; color: var(--color-text-primary); line-height: 1.35; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } - &__member-card-badges { - display: inline-flex; - gap: 6px; - flex-wrap: wrap; - justify-content: flex-end; - } - - &__member-card-role { - font-size: var(--font-size-xs); - font-weight: $font-weight-medium; - color: color-mix(in srgb, var(--member-accent, #64748b) 72%, var(--color-text-primary)); - } - - &__member-card-description { - margin: 0; - font-size: var(--font-size-sm); - line-height: 1.6; - color: var(--color-text-secondary); - } - - &__member-card-model { - display: inline-flex; - align-self: flex-start; - gap: 6px; - min-height: 24px; - align-items: center; - padding: 0 10px; - border-radius: 999px; - background: rgba(15, 23, 42, 0.06); - color: var(--color-text-muted); + &__member-list-meta { font-size: 11px; - font-weight: $font-weight-medium; - } - - &__member-card-model-note { - color: color-mix(in srgb, #f59e0b 82%, var(--color-text-muted)); + color: var(--color-text-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } - &__member-card-chevron { + &__member-list-badges { display: inline-flex; - align-items: center; - align-self: flex-start; - color: var(--color-text-muted); - margin-top: 2px; + gap: 6px; + flex-shrink: 0; } - &__member-card-detail { - overflow: hidden; + &__member-detail-panel { + display: flex; + flex-direction: column; + gap: 16px; + padding: 20px; + border: 1px solid var(--border-subtle); + border-radius: 8px; + background: color-mix(in srgb, var(--element-bg-soft) 78%, transparent); animation: review-team-card-expand $motion-base $easing-standard forwards; - border-top: 1px solid var(--border-subtle); - } - - &__member-card-detail-inner { - padding: 16px; } &__detail-hero { @@ -569,7 +513,7 @@ @media (max-width: 960px) { .review-team-page { &__summary-grid, - &__member-grid, + &__member-layout, &__execution-grid, &__strategy-options, &__strategy-options--compact { @@ -582,20 +526,24 @@ } &__policy-metrics { - grid-template-columns: repeat(2, minmax(0, 1fr)); + grid-template-columns: repeat(3, minmax(0, 1fr)); } &__detail-hero, &__detail-title-row { flex-direction: column; } + + &__member-detail-panel { + animation: none; + } } } @media (max-width: 720px) { .review-team-page { - &__member-card-top { - flex-direction: column; + &__member-list-badges { + display: none; } &__add-controls { @@ -603,7 +551,7 @@ } &__policy-metrics { - grid-template-columns: 1fr; + grid-template-columns: repeat(2, minmax(0, 1fr)); } } } diff --git a/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.tsx b/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.tsx index 37a3c94a8..42060173c 100644 --- a/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.tsx +++ b/src/web-ui/src/app/scenes/agents/components/ReviewTeamPage.tsx @@ -3,7 +3,6 @@ import { ArrowLeft, BadgeCheck, Bot, - ChevronDown, Gauge, GitBranch, Lock, @@ -21,7 +20,7 @@ import { import type { AIModelConfig } from '@/infrastructure/config/types'; import { getModelDisplayName } from '@/infrastructure/config/services/modelConfigs'; import { configAPI } from '@/infrastructure/api/service-api/ConfigAPI'; -import type { SubagentSource } from '@/infrastructure/api/service-api/SubagentAPI'; + import { useNotification } from '@/shared/notification-system'; import { createLogger } from '@/shared/utils/logger'; import { useCurrentWorkspace } from '@/infrastructure/contexts/WorkspaceContext'; @@ -58,12 +57,6 @@ function getMemberIcon(member: ReviewTeamMember) { } } -function getSourceVariant(source?: SubagentSource): 'neutral' | 'info' | 'purple' { - if (source === 'user') return 'info'; - if (source === 'project') return 'purple'; - return 'neutral'; -} - function getMemberResponsibilities(member: ReviewTeamMember): string[] { return Array.isArray(member.responsibilities) ? member.responsibilities : []; } @@ -409,10 +402,7 @@ const ReviewTeamPage: React.FC = () => { - - - {t('reviewTeams.detail.openSettings', { defaultValue: 'Review settings' })} - + @@ -439,127 +429,102 @@ const ReviewTeamPage: React.FC = () => { )} > -
- {team.members.map((member) => { - const MemberIcon = getMemberIcon(member); - const isSelected = selectedMember?.id === member.id; - const responsibilities = getLocalizedResponsibilities(member); - - return ( -
+
+
+ {team.members.map((member) => { + const MemberIcon = getMemberIcon(member); + const isSelected = selectedMember?.id === member.id; + + return ( + ); + })} +
- {isSelected ? ( -
-
-
-
- -
-
-
-
-

- {getLocalizedMemberName(member)} -

-

- {member.subagentId} -

-
-
- {formatModelLabel(member.model)} - - {getStrategyLabel(member.strategyLevel)} - - {member.locked ? ( - - {t('reviewTeams.detail.memberTypes.core', { defaultValue: 'Core role' })} - - ) : null} -
-
-

- {getLocalizedMemberDescription(member)} -

-
-
- -
- - {t('reviewTeams.detail.responsibilities', { defaultValue: 'Responsibilities' })} - -
    - {responsibilities.map((item, index) => ( -
  • - {item} -
  • - ))} -
-
+ {selectedMember ? ( +
+
+
+ {(() => { + const DetailIcon = getMemberIcon(selectedMember); + return ; + })()} +
+
+
+
+

+ {getLocalizedMemberName(selectedMember)} +

+

+ {selectedMember.subagentId} +

+
+
+ {formatModelLabel(selectedMember.model)} + + {getStrategyLabel(selectedMember.strategyLevel)} + + {selectedMember.locked ? ( + + {t('reviewTeams.detail.memberTypes.core', { defaultValue: 'Core role' })} + + ) : null}
- ) : null} +

+ {getLocalizedMemberDescription(selectedMember)} +

+
- ); - })} + +
+ + {t('reviewTeams.detail.responsibilities', { defaultValue: 'Responsibilities' })} + +
    + {getLocalizedResponsibilities(selectedMember).map((item, index) => ( +
  • + {item} +
  • + ))} +
+
+
+ ) : null}
diff --git a/src/web-ui/src/flow_chat/components/ChatEmptyState.scss b/src/web-ui/src/flow_chat/components/ChatEmptyState.scss index be6c4cc91..df92467be 100644 --- a/src/web-ui/src/flow_chat/components/ChatEmptyState.scss +++ b/src/web-ui/src/flow_chat/components/ChatEmptyState.scss @@ -43,7 +43,7 @@ align-items: flex-start; justify-content: center; gap: $size-gap-8; - max-width: 650px; + max-width: 800px; width: 100%; padding: $size-gap-10 $size-gap-8; margin: 0 auto; diff --git a/src/web-ui/src/flow_chat/components/ChatInput.scss b/src/web-ui/src/flow_chat/components/ChatInput.scss index 264363164..1b1e34efe 100644 --- a/src/web-ui/src/flow_chat/components/ChatInput.scss +++ b/src/web-ui/src/flow_chat/components/ChatInput.scss @@ -11,7 +11,7 @@ left: 50%; transform: translateX(-50%); width: 100%; - max-width: 700px; + max-width: 900px; z-index: $z-overlay; display: flex; flex-direction: column; @@ -425,7 +425,7 @@ .session-file-modifications-bar { width: 100%; - max-width: 700px; + max-width: 900px; } & > * { diff --git a/src/web-ui/src/flow_chat/components/modern/ExploreRegion.scss b/src/web-ui/src/flow_chat/components/modern/ExploreRegion.scss index b437e4ded..da2a35490 100644 --- a/src/web-ui/src/flow_chat/components/modern/ExploreRegion.scss +++ b/src/web-ui/src/flow_chat/components/modern/ExploreRegion.scss @@ -3,6 +3,10 @@ .explore-region { position: relative; padding: 0 3rem; // Match model-round-item horizontal padding. + border: 1px solid var(--border-base); + border-radius: 8px; + background: var(--color-bg-flowchat); + overflow: hidden; @media (max-width: 768px) { padding: 0 1.5rem; @@ -60,18 +64,22 @@ display: flex; align-items: center; gap: 6px; - padding: 5px 0; + padding: 5px 10px; + margin: 0 -3rem; color: var(--text-tertiary, #6b7280); font-size: 12px; cursor: pointer; user-select: none; - border-radius: 6px; transition: color 0.15s ease; &:hover { background: var(--bg-hover, rgba(255, 255, 255, 0.05)); color: var(--text-secondary, #9ca3af); } + + @media (max-width: 768px) { + margin: 0 -1.5rem; + } } .explore-region__icon { @@ -103,7 +111,12 @@ .explore-region__content { position: relative; - padding: 4px 0; + padding: 4px 10px; + margin: 0 -3rem; + + @media (max-width: 768px) { + margin: 0 -1.5rem; + } &::-webkit-scrollbar { width: 4px; diff --git a/src/web-ui/src/flow_chat/components/modern/ProcessingIndicator.scss b/src/web-ui/src/flow_chat/components/modern/ProcessingIndicator.scss index c333c7e85..a1cfc4a5d 100644 --- a/src/web-ui/src/flow_chat/components/modern/ProcessingIndicator.scss +++ b/src/web-ui/src/flow_chat/components/modern/ProcessingIndicator.scss @@ -4,16 +4,21 @@ .processing-indicator { width: 100%; - max-width: 900px; /* A4-style max width */ + max-width: 1200px; /* Relaxed max width for larger screens */ margin: 0 auto 1rem auto; /* Center horizontally */ padding: 0 3rem; box-sizing: border-box; - + + // Responsive: large screens + @media (max-width: 1400px) { + max-width: 1000px; + } + // Responsive: medium screens @media (max-width: 1200px) { - max-width: 800px; + max-width: 900px; } - + // Responsive: small screens @media (max-width: 768px) { max-width: 100%; diff --git a/src/web-ui/src/flow_chat/components/modern/TerminalGroupRenderer.scss b/src/web-ui/src/flow_chat/components/modern/TerminalGroupRenderer.scss index a12d80711..b4958d8f1 100644 --- a/src/web-ui/src/flow_chat/components/modern/TerminalGroupRenderer.scss +++ b/src/web-ui/src/flow_chat/components/modern/TerminalGroupRenderer.scss @@ -2,10 +2,13 @@ .terminal-region { position: relative; - padding: 0 3rem; + border: 1px solid var(--border-base); + border-radius: 8px; + background: var(--color-bg-flowchat); + overflow: hidden; @media (max-width: 768px) { - padding: 0 1.5rem; + margin: 0 -1.5rem; } } @@ -54,12 +57,11 @@ display: flex; align-items: center; gap: 6px; - padding: 5px 0; + padding: 5px 10px; color: var(--text-tertiary, #6b7280); font-size: 12px; cursor: pointer; user-select: none; - border-radius: 6px; transition: color 0.15s ease; &:hover { @@ -123,7 +125,7 @@ .terminal-region__content { position: relative; - padding: 4px 0; + padding: 4px 10px; /* Tighter spacing for cards inside a terminal group */ .flowchat-tool-wrapper { diff --git a/src/web-ui/src/flow_chat/components/modern/VirtualItemRenderer.scss b/src/web-ui/src/flow_chat/components/modern/VirtualItemRenderer.scss index 8570e17df..ccf19ae0f 100644 --- a/src/web-ui/src/flow_chat/components/modern/VirtualItemRenderer.scss +++ b/src/web-ui/src/flow_chat/components/modern/VirtualItemRenderer.scss @@ -5,16 +5,20 @@ .virtual-item-wrapper { width: 100%; - max-width: 900px; + max-width: 1200px; margin: 0 auto; box-sizing: border-box; min-height: 1px; padding: 0; - + + @media (max-width: 1400px) { + max-width: 1000px; + } + @media (max-width: 1200px) { - max-width: 800px; + max-width: 900px; } - + @media (max-width: 768px) { max-width: 100%; } diff --git a/src/web-ui/src/flow_chat/services/flow-chat-manager/EventHandlerModule.ts b/src/web-ui/src/flow_chat/services/flow-chat-manager/EventHandlerModule.ts index 258aed8b0..f2d56ebd8 100644 --- a/src/web-ui/src/flow_chat/services/flow-chat-manager/EventHandlerModule.ts +++ b/src/web-ui/src/flow_chat/services/flow-chat-manager/EventHandlerModule.ts @@ -630,6 +630,9 @@ function finalizeTurnCompletionState( const activeSessionId = store.getState().activeSessionId; if (sessionId !== activeSessionId) { context.flowChatStore.markSessionUnreadCompletion(sessionId, 'completed'); + updateSessionMetadata(context, sessionId).catch(err => { + log.warn('Failed to persist unread completion metadata', { sessionId, err }); + }); } clearPendingTurnCompletion(context, sessionId, turnId); @@ -1755,6 +1758,9 @@ function handleDialogTurnFailed(context: FlowChatContext, event: any): void { const activeSessionIdForError = store.getState().activeSessionId; if (sessionId !== activeSessionIdForError) { context.flowChatStore.markSessionUnreadCompletion(sessionId, 'error'); + updateSessionMetadata(context, sessionId).catch(err => { + log.warn('Failed to persist unread error metadata', { sessionId, err }); + }); } } diff --git a/src/web-ui/src/flow_chat/store/FlowChatStore.ts b/src/web-ui/src/flow_chat/store/FlowChatStore.ts index 7545a0775..942207b0c 100644 --- a/src/web-ui/src/flow_chat/store/FlowChatStore.ts +++ b/src/web-ui/src/flow_chat/store/FlowChatStore.ts @@ -1602,6 +1602,7 @@ export class FlowChatStore { sessionKind: relationship.sessionKind, btwThreads: [], btwOrigin: relationship.btwOrigin, + hasUnreadCompletion: metadata.unreadCompletion, }; const newSessions = new Map(prev.sessions); diff --git a/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.tsx b/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.tsx index bdf292323..4b24f965a 100644 --- a/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.tsx +++ b/src/web-ui/src/flow_chat/tool-cards/FileOperationToolCard.tsx @@ -588,7 +588,7 @@ export const FileOperationToolCard: React.FC = ({ content={newStringContent} filePath={currentFilePath} isStreaming={isParamsStreaming} - showLineNumbers={false} + showLineNumbers={isContentExpanded} maxHeight={previewMaxHeight} autoScrollToBottom={isParamsStreaming} onLineClick={handleCodeLineClick} @@ -607,7 +607,7 @@ export const FileOperationToolCard: React.FC = ({ modifiedContent={newStringContent} filePath={currentFilePath} maxHeight={previewMaxHeight} - showLineNumbers={false} + showLineNumbers={isContentExpanded} lineNumberMode="dual" showPrefix={false} contextLines={-1} @@ -627,7 +627,7 @@ export const FileOperationToolCard: React.FC = ({ content={contentPreview} filePath={currentFilePath} isStreaming={isParamsStreaming} - showLineNumbers={false} + showLineNumbers={isContentExpanded} maxHeight={previewMaxHeight} autoScrollToBottom={isParamsStreaming} onLineClick={handleCodeLineClick} @@ -646,7 +646,7 @@ export const FileOperationToolCard: React.FC = ({ modifiedContent={contentPreview} filePath={currentFilePath} maxHeight={previewMaxHeight} - showLineNumbers={false} + showLineNumbers={isContentExpanded} lineNumberMode="single" showPrefix={true} contextLines={-1} diff --git a/src/web-ui/src/shared/types/session-history.ts b/src/web-ui/src/shared/types/session-history.ts index ce3a7c643..a73de5ec5 100644 --- a/src/web-ui/src/shared/types/session-history.ts +++ b/src/web-ui/src/shared/types/session-history.ts @@ -41,6 +41,8 @@ export interface SessionMetadata { remoteSshHost?: string; /** Backend unified workspace identity field: localhost for local, SSH host for remote. */ workspaceHostname?: string; + /** Unread completion status for the session. 'completed' → green dot, 'error' → red dot. */ + unreadCompletion?: 'completed' | 'error'; } export type SessionStatus = 'active' | 'archived' | 'completed'; From bf7f8ba89708e86e97c1f50f9789a69db3662b77 Mon Sep 17 00:00:00 2001 From: limityan Date: Sun, 26 Apr 2026 09:48:53 +0800 Subject: [PATCH 2/4] style(i18n): modify zh-CN and zh-TW locales MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify Chinese terminology by replacing all occurrences of 审查 with 审核 in both Simplified and Traditional Chinese locale files, plus related model copy strings, README, and component registry examples. --- README.zh-CN.md | 4 +- .../src/agentic/agents/prompts/cowork_mode.md | 38 +++- .../src/agentic/agents/prompts/plan_mode.md | 13 ++ .../src/agentic/coordination/coordinator.rs | 1 + .../core/src/service/i18n/model_copy.rs | 4 +- src/crates/core/src/service/session/types.rs | 1 + .../component-library/components/registry.tsx | 2 +- .../components/btw/DeepReviewActionBar.scss | 34 +++- .../components/btw/DeepReviewActionBar.tsx | 20 +- .../components/modern/SubagentItems.scss | 5 +- .../src/flow_chat/services/FlowChatManager.ts | 13 +- .../flow-chat-manager/EventHandlerModule.ts | 6 - .../src/flow_chat/store/FlowChatStore.ts | 14 +- .../tool-cards/ToolTimeoutIndicator.scss | 22 ++- .../tool-cards/ToolTimeoutIndicator.tsx | 5 + src/web-ui/src/locales/zh-CN/common.json | 4 +- src/web-ui/src/locales/zh-CN/errors.json | 2 +- src/web-ui/src/locales/zh-CN/flow-chat.json | 186 +++++++++--------- src/web-ui/src/locales/zh-CN/panels/git.json | 10 +- .../src/locales/zh-CN/scenes/agents.json | 26 +-- src/web-ui/src/locales/zh-CN/settings.json | 6 +- .../locales/zh-CN/settings/ai-features.json | 2 +- .../locales/zh-CN/settings/default-model.json | 2 +- .../src/locales/zh-CN/settings/review.json | 6 +- src/web-ui/src/locales/zh-TW/common.json | 4 +- src/web-ui/src/locales/zh-TW/errors.json | 2 +- src/web-ui/src/locales/zh-TW/flow-chat.json | 186 +++++++++--------- src/web-ui/src/locales/zh-TW/panels/git.json | 10 +- .../src/locales/zh-TW/scenes/agents.json | 24 +-- src/web-ui/src/locales/zh-TW/settings.json | 6 +- .../locales/zh-TW/settings/ai-features.json | 2 +- .../locales/zh-TW/settings/default-model.json | 2 +- .../src/locales/zh-TW/settings/review.json | 6 +- 33 files changed, 386 insertions(+), 282 deletions(-) diff --git a/README.zh-CN.md b/README.zh-CN.md index d802348ad..ff1daf285 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -57,8 +57,8 @@ Agent 领域几乎每周都有新范式出现。BitFun 的节奏是——**看 | 能力 | 说明 | | --- | --- | -| **Code Agent** | 四种模式:Agentic(自主读改跑验证)/ Plan(先规划后执行)/ Debug(插桩取证 → 根因定位)/ Review(基于仓库规范审查) | -| **深度审查** | 面向高风险代码变更的并行代码审核团队,内置专项审核员、质量把关和用户确认后的修复流程 | +| **Code Agent** | 四种模式:Agentic(自主读改跑验证)/ Plan(先规划后执行)/ Debug(插桩取证 → 根因定位)/ Review(基于仓库规范审核) | +| **深度审核** | 面向高风险代码变更的并行代码审核团队,内置专项审核员、质量把关和用户确认后的修复流程 | | **Cowork Agent** | PDF / DOCX / XLSX / PPTX 原生处理能力,可从 Skill 市场按需扩展 | | **文档协作** | 在文档里边写边问,AI 直接在段落上改写、续写、总结、排版 | | **Computer Use** | 看屏幕、动鼠标键盘,操作浏览器与任意桌面应用,把"手动点点点"交给 Agent | diff --git a/src/crates/core/src/agentic/agents/prompts/cowork_mode.md b/src/crates/core/src/agentic/agents/prompts/cowork_mode.md index 04713b480..5dad3bdb3 100644 --- a/src/crates/core/src/agentic/agents/prompts/cowork_mode.md +++ b/src/crates/core/src/agentic/agents/prompts/cowork_mode.md @@ -219,27 +219,47 @@ Tool results and user messages may include tags. These { } = store; const [showCustomInput, setShowCustomInput] = useState(false); - const [showRemediationList, setShowRemediationList] = useState(false); + const [showRemediationList, setShowRemediationList] = useState(true); const selectedCount = selectedRemediationIds.size; const totalCount = remediationItems.length; @@ -328,7 +328,7 @@ export const ReviewActionBar: React.FC = () => { )}
- {/* Remediation selection (only when review completed) */} + {/* Remediation selection (only when review completed and has items) */} {phase === 'review_completed' && remediationItems.length > 0 && (
)} + {/* Friendly message when review completed with no remediation items */} + {phase === 'review_completed' && remediationItems.length === 0 && ( +
+ + + {t('reviewActionBar.noIssuesFound', { + defaultValue: 'No issues found. Great job!', + })} + +
+ )} + {/* Custom instructions input */} - {phase === 'review_completed' && ( + {phase === 'review_completed' && remediationItems.length > 0 && (
{isPopoverOpen && ( diff --git a/src/web-ui/src/locales/zh-CN/common.json b/src/web-ui/src/locales/zh-CN/common.json index af0a47daf..9afe8ce41 100644 --- a/src/web-ui/src/locales/zh-CN/common.json +++ b/src/web-ui/src/locales/zh-CN/common.json @@ -179,8 +179,8 @@ "parentSession": "父会话", "childSourceWithoutTurn": "来自 {{parentTitle}}", "childSourceWithTurn": "来自 {{parentTitle}} · 第 {{turnIndex}} 轮", - "reviewRunning": "审查中", - "deepReviewRunning": "审查中", + "reviewRunning": "审核中", + "deepReviewRunning": "审核中", "unreadCompleted": "已完成 — 未读", "unreadError": "执行失败 — 未读", "needProjectWorkspaceForSession": "请先打开或添加项目工作区。编码与工作(Cowork)会话不能在助理区域创建。" diff --git a/src/web-ui/src/locales/zh-CN/errors.json b/src/web-ui/src/locales/zh-CN/errors.json index 13a3874d7..2ffce3423 100644 --- a/src/web-ui/src/locales/zh-CN/errors.json +++ b/src/web-ui/src/locales/zh-CN/errors.json @@ -92,7 +92,7 @@ }, "contentPolicy": { "title": "模型安全策略已拦截", - "message": "模型服务商因策略原因拦截了输入或输出。请调整提示词或审查范围后再试。" + "message": "模型服务商因策略原因拦截了输入或输出。请调整提示词或审核范围后再试。" }, "actions": { "retry": "重试", diff --git a/src/web-ui/src/locales/zh-CN/flow-chat.json b/src/web-ui/src/locales/zh-CN/flow-chat.json index 491565de9..ab9420642 100644 --- a/src/web-ui/src/locales/zh-CN/flow-chat.json +++ b/src/web-ui/src/locales/zh-CN/flow-chat.json @@ -159,7 +159,7 @@ "select": "选择智能体", "default": "默认助手", "coding": "编程助手", - "review": "代码审查", + "review": "代码审核", "architect": "架构师", "custom": "自定义智能体" }, @@ -235,14 +235,14 @@ "initUsage": "请直接使用 /init,不要附加额外参数。", "initFailed": "初始化 AGENTS.md 失败", "initPrompt": "请根据当前项目内容生成或更新 AGENTS.md", - "deepreviewAction": "深度审查", - "deepreviewNoSession": "当前没有可用于深度审查的会话", - "deepreviewUsage": "请使用 /DeepReview,可附加聚焦说明,例如 /DeepReview 审查提交 abc123 的安全风险。", - "deepreviewFailed": "深度审查失败", - "deepreviewInfo": "深度审查会拉起并行代码审核团队,可能会比普通审查耗时更长且 Token 消耗更高。", - "deepreviewBusy": "当前会话已有审查正在进行,请停止或等待完成后再开始新的深度审查。", - "deepreviewNestedDisabled": "深度审查只能从主会话启动。", - "deepreviewThreadTitle": "深度审查", + "deepreviewAction": "深度审核", + "deepreviewNoSession": "当前没有可用于深度审核的会话", + "deepreviewUsage": "请使用 /DeepReview,可附加聚焦说明,例如 /DeepReview 审核提交 abc123 的安全风险。", + "deepreviewFailed": "深度审核失败", + "deepreviewInfo": "深度审核会拉起并行代码审核团队,可能会比普通审核耗时更长且 Token 消耗更高。", + "deepreviewBusy": "当前会话已有审核正在进行,请停止或等待完成后再开始新的深度审核。", + "deepreviewNestedDisabled": "深度审核只能从主会话启动。", + "deepreviewThreadTitle": "深度审核", "currentMode": "当前模式: {{mode}}", "noMatchingMode": "没有匹配的模式", "noMatchingCommand": "没有匹配的命令", @@ -348,9 +348,9 @@ "noSession": "当前没有可用于 /btw 的会话" }, "childSession": { - "stopReview": "中止审查", - "stoppingReview": "正在中止审查...", - "stopReviewFailed": "中止审查会话失败。", + "stopReview": "中止审核", + "stoppingReview": "正在中止审核...", + "stopReviewFailed": "中止审核会话失败。", "kinds": { "btw": { "short": "侧问", @@ -358,24 +358,24 @@ "origin": "来自" }, "review": { - "short": "审查", - "title": "代码审查", - "origin": "审查来源" + "short": "审核", + "title": "代码审核", + "origin": "审核来源" }, "deep_review": { "short": "深审", - "title": "深度审查", - "origin": "审查来源" + "title": "深度审核", + "origin": "审核来源" } } }, "deepReviewActionBar": { - "reviewCompleted": "深度审查已完成", + "reviewCompleted": "深度审核已完成", "fixRunning": "正在修复...", "fixCompleted": "修复完成", "fixFailed": "修复失败", "fixTimeout": "修复超时", - "reviewError": "审查出错", + "reviewError": "审核出错", "dismiss": "关闭", "close": "关闭", "showCustomInput": "补充说明", @@ -383,49 +383,49 @@ "customInstructionsPlaceholder": "描述修复的额外要求或上下文信息...", "fillBackInput": "填入输入框", "fillBackInputHint": "将选中的修复计划填入输入框,供手动编辑", - "reviewInterrupted": "深度审查已中断", + "reviewInterrupted": "深度审核已中断", "resumeBlocked": "继续前需要处理问题", - "resumeRunning": "正在继续审查...", + "resumeRunning": "正在继续审核...", "resumeFailed": "继续失败", - "resumeBlockedHint": "请先处理模型配置或额度问题,再继续审查。", - "resumeBlockedConfirmTitle": "继续审查?", - "resumeBlockedConfirmMessage": "导致审查中断的错误尚未解决,继续审查可能会再次失败。是否仍要继续?", + "resumeBlockedHint": "请先处理模型配置或额度问题,再继续审核。", + "resumeBlockedConfirmTitle": "继续审核?", + "resumeBlockedConfirmMessage": "导致审核中断的错误尚未解决,继续审核可能会再次失败。是否仍要继续?", "resumeBlockedConfirmAction": "仍然继续", - "resumeReview": "继续审查", - "resumeRequestDisplay": "继续中断的深度审查", - "resumeFailedMessage": "无法继续深度审查。请检查模型设置,或稍后重试。", + "resumeReview": "继续审核", + "resumeRequestDisplay": "继续中断的深度审核", + "resumeFailedMessage": "无法继续深度审核。请检查模型设置,或稍后重试。", "openModelSettings": "打开模型设置", "copyDiagnostics": "复制诊断信息", "diagnosticsCopied": "诊断信息已复制", - "diagnosticsTitle": "=== 深度审查中断诊断信息 ===", + "diagnosticsTitle": "=== 深度审核中断诊断信息 ===", "diagnosticsErrorType": "错误类型", "diagnosticsDescription": "错误描述", "diagnosticsSuggestedActions": "建议操作", "diagnosticsTechnicalDetails": "技术详情" }, "reviewActionBar": { - "reviewCompletedStandard": "审查已完成", - "reviewCompletedDeep": "深度审查已完成", - "fixRequestDisplayStandard": "开始修复代码审查问题", - "fixAndReviewRequestDisplayStandard": "修复代码审查问题并重新审查", - "fixRequestDisplayDeep": "开始修复深度审查问题", - "fixAndReviewRequestDisplayDeep": "修复深度审查问题并重新审查", + "reviewCompletedStandard": "审核已完成", + "reviewCompletedDeep": "深度审核已完成", + "fixRequestDisplayStandard": "开始修复代码审核问题", + "fixAndReviewRequestDisplayStandard": "修复代码审核问题并重新审核", + "fixRequestDisplayDeep": "开始修复深度审核问题", + "fixAndReviewRequestDisplayDeep": "修复深度审核问题并重新审核", "needsDecisionTag": "需决策" }, "deepReviewConsent": { - "windowTitle": "深度审查", + "windowTitle": "深度审核", "eyebrow": "代码审核团队", - "title": "开始深度审查?", - "body": "深度审查会启动并行代码审核团队,通常比普通审查耗时更久,也会消耗更多 Token。", + "title": "开始深度审核?", + "body": "深度审核会启动并行代码审核团队,通常比普通审核耗时更久,也会消耗更多 Token。", "readonlyLabel": "首次只读", - "readonly": "首次审查默认只读:它会先输出问题和修复计划,不会直接修改代码。", + "readonly": "首次审核默认只读:它会先输出问题和修复计划,不会直接修改代码。", "costLabel": "Token 消耗更高", - "cost": "预期消耗:多个审查角色加质量门禁,运行时间和 Token 可能达到普通审查的数倍。", + "cost": "预期消耗:多个审核角色加质量门禁,运行时间和 Token 可能达到普通审核的数倍。", "timeLabel": "耗时更久", - "time": "运行期间可以继续工作;只有整个深度审查在后台完成时才会通知你。", + "time": "运行期间可以继续工作;只有整个深度审核在后台完成时才会通知你。", "dontShowAgain": "下次不再提示", "cancel": "取消", - "confirm": "开始深度审查" + "confirm": "开始深度审核" }, "flowChatHeader": { "turnList": "轮次列表", @@ -450,41 +450,41 @@ }, "sessionFilesBadge": { "files": "文件", - "reviewLabel": "审查", - "reviewAll": "全部审查", - "reviewMenuLabel": "审查", - "reviewMenuHint": "选择审查深度", - "reviewModeStandard": "普通审查", - "reviewModeDeep": "深度审查", - "reviewRunningStandard": "审查中", - "reviewRunningDeep": "审查中", - "reviewRunningHint": "请等待当前审查完成,或在审查页面中止它。", + "reviewLabel": "审核", + "reviewAll": "全部审核", + "reviewMenuLabel": "审核", + "reviewMenuHint": "选择审核深度", + "reviewModeStandard": "普通审核", + "reviewModeDeep": "深度审核", + "reviewRunningStandard": "审核中", + "reviewRunningDeep": "审核中", + "reviewRunningHint": "请等待当前审核完成,或在审核页面中止它。", "deepReviewLabel": "深审", - "deepReviewAll": "深度审查", + "deepReviewAll": "深度审核", "loadFailed": "加载失败", "clickToViewDiff": "点击查看 Diff", "review": { - "displayMessage": "审查修改的文件:\n{{files}}", - "displayMessageFiltered": "审查过滤后的文件:\n{{files}}\n\n已跳过 {{skipped}} 个命中排除规则的文件。", + "displayMessage": "审核修改的文件:\n{{files}}", + "displayMessageFiltered": "审核过滤后的文件:\n{{files}}\n\n已跳过 {{skipped}} 个命中排除规则的文件。", "filteredNotice": "本次将分析 {{included}} 个文件,并跳过 {{skipped}} 个命中排除规则的文件,例如锁文件、生成物或二进制资源。", - "noEligibleFiles": "排除命中规则的文件后,本次会话里没有可审查文件。", - "prompt": "请审查本次会话中修改的以下文件:\n\n{{files}}", - "promptFiltered": "请审查本次会话中修改的以下文件:\n\n{{files}}\n\n请不要审查另外 {{skipped}} 个已跳过的文件,因为它们命中了锁文件、生成物或二进制文件的排除规则。", - "threadTitle": "代码审查" + "noEligibleFiles": "排除命中规则的文件后,本次会话里没有可审核文件。", + "prompt": "请审核本次会话中修改的以下文件:\n\n{{files}}", + "promptFiltered": "请审核本次会话中修改的以下文件:\n\n{{files}}\n\n请不要审核另外 {{skipped}} 个已跳过的文件,因为它们命中了锁文件、生成物或二进制文件的排除规则。", + "threadTitle": "代码审核" }, "deepReview": { - "info": "深度审查会启动并行代码审核团队,可能更耗时并消耗更多 Token。", - "displayMessage": "深度审查修改的文件:\n{{files}}", - "displayMessageFiltered": "深度审查过滤后的文件:\n{{files}}\n\n已跳过 {{skipped}} 个命中排除规则的文件。", - "threadTitle": "深度审查" + "info": "深度审核会启动并行代码审核团队,可能更耗时并消耗更多 Token。", + "displayMessage": "深度审核修改的文件:\n{{files}}", + "displayMessageFiltered": "深度审核过滤后的文件:\n{{files}}\n\n已跳过 {{skipped}} 个命中排除规则的文件。", + "threadTitle": "深度审核" } }, "sessionFileModificationsBar": { "filesCount": "{{count}} 个文件", "expandList": "展开列表", "collapseList": "收起列表", - "reviewSource": "审查", - "deepReviewSource": "深度审查" + "reviewSource": "审核", + "deepReviewSource": "深度审核" }, "snapshotSystem": { "errors": { @@ -1043,31 +1043,31 @@ "rollbackTooltip": "回滚到此轮" }, "reviewSessionSummary": { - "standardTitle": "审查", - "deepTitle": "深度审查", + "standardTitle": "审核", + "deepTitle": "深度审核", "running": "进行中", "failed": "失败", "issueCount": "{{count}} 个问题", "noIssues": "无阻塞问题", "filesChanged": "{{count}} 个文件", - "waitingSummary": "审查仍在运行,代码审核团队完成后会在这里显示结果。", - "emptySummary": "暂时没有结构化审查总结。", - "changedFilesTitle": "本次审查涉及的文件", - "openReview": "打开审查页面" + "waitingSummary": "审核仍在运行,代码审核团队完成后会在这里显示结果。", + "emptySummary": "暂时没有结构化审核总结。", + "changedFilesTitle": "本次审核涉及的文件", + "openReview": "打开审核页面" }, "codeReview": { - "reviewResult": "审查结果", - "reviewingCode": "正在审查代码...", - "preparingReview": "准备代码审查", - "reviewFailed": "代码审查失败:{{error}}", + "reviewResult": "审核结果", + "reviewingCode": "正在审核代码...", + "preparingReview": "准备代码审核", + "reviewFailed": "代码审核失败:{{error}}", "unknownError": "未知错误", "collapseDetails": "收起详情", "expandDetails": "展开详情", "overallAssessment": "总体评估", "riskLevel": "风险等级", "recommendedAction": "建议操作", - "deepReviewResult": "深度审查结果", - "reviewMode": "审查模式", + "deepReviewResult": "深度审核结果", + "reviewMode": "审核模式", "reviewScope": "范围", "contextLimitations": "上下文限制", "issuesCount": "问题 ({{count}})", @@ -1101,9 +1101,9 @@ "other": "其他" }, "report": { - "titleStandard": "代码审查报告", - "titleDeep": "深度审查报告", - "reviewDecision": "审查结论", + "titleStandard": "代码审核报告", + "titleDeep": "深度审核报告", + "reviewDecision": "审核结论", "noIssues": "没有已验证问题。", "status": "状态", "findings": "问题数", @@ -1115,14 +1115,14 @@ "copyMarkdown": "复制 Markdown", "openMarkdown": "以 Markdown 打开", "saveMarkdown": "保存 Markdown", - "copySuccess": "已复制 Markdown 审查报告。", - "copyFailed": "复制审查报告失败。", - "openFailed": "在编辑器中打开审查报告失败。", - "saveSuccess": "审查报告已导出。", - "saveFailed": "导出审查报告失败。", - "saveDialogTitle": "导出审查报告", - "fileNamePrefix": "BitFun-代码审查", - "editorTitle": "审查报告" + "copySuccess": "已复制 Markdown 审核报告。", + "copyFailed": "复制审核报告失败。", + "openFailed": "在编辑器中打开审核报告失败。", + "saveSuccess": "审核报告已导出。", + "saveFailed": "导出审核报告失败。", + "saveDialogTitle": "导出审核报告", + "fileNamePrefix": "BitFun-代码审核", + "editorTitle": "审核报告" }, "reviewModes": { "standard": "普通", @@ -1130,7 +1130,7 @@ }, "remediationActions": { "title": "下一步", - "hint": "深度审查默认只读。请选择要修复的计划项。", + "hint": "深度审核默认只读。请选择要修复的计划项。", "selectAll": "全选", "selectionCount": "已选择 {{selected}}/{{total}} 项", "expandPlan": "详情", @@ -1142,11 +1142,11 @@ "location": "位置", "noSelectionHint": "至少选择一项修复计划后才能开始修复。", "startFix": "开始修复", - "fixAndReview": "修复后重新审查", + "fixAndReview": "修复后重新审核", "cancel": "取消", - "fixUnavailable": "无法开始修复:审查会话不可用。", - "fixRequestDisplay": "开始修复深度审查问题", - "fixAndReviewRequestDisplay": "修复深度审查问题并重新审查" + "fixUnavailable": "无法开始修复:审核会话不可用。", + "fixRequestDisplay": "开始修复深度审核问题", + "fixAndReviewRequestDisplay": "修复深度审核问题并重新审核" }, "riskLevels": { "low": "低风险", @@ -1207,8 +1207,8 @@ "timeSinceLastCommit": "上次提交:", "timeIcon": "时间", "youMightWant": "您可能想要:", - "defaultAction1": "审查最近的更改", - "defaultAction1Command": "审查最近的更改", + "defaultAction1": "审核最近的更改", + "defaultAction1Command": "审核最近的更改", "defaultAction2": "继续之前的工作", "defaultAction2Command": "继续之前的工作", "defaultAction3": "开始新功能", diff --git a/src/web-ui/src/locales/zh-CN/panels/git.json b/src/web-ui/src/locales/zh-CN/panels/git.json index 03d79c531..7d37a0a8b 100644 --- a/src/web-ui/src/locales/zh-CN/panels/git.json +++ b/src/web-ui/src/locales/zh-CN/panels/git.json @@ -33,7 +33,7 @@ "commitWithCount": "提交 ({{count}})", "aiGenerateCommit": "AI 生成提交信息", "cancelGenerate": "取消生成", - "aiCodeReview": "AI 代码审查", + "aiCodeReview": "AI 代码审核", "viewAllDiff": "查看所有差异", "discardFile": "恢复此文件", "deleteFile": "删除此文件", @@ -139,10 +139,10 @@ } }, "review": { - "noFilesToReview": "没有文件变更,无法进行代码审查", - "displayMessage": "审查 Git 变更文件:\n{{files}}", - "requestMessage": "请审查以下 Git 变更文件:\n\n{{files}}", - "sendFailed": "发送代码审查请求失败" + "noFilesToReview": "没有文件变更,无法进行代码审核", + "displayMessage": "审核 Git 变更文件:\n{{files}}", + "requestMessage": "请审核以下 Git 变更文件:\n\n{{files}}", + "sendFailed": "发送代码审核请求失败" }, "relativeTime": { "justNow": "刚刚", diff --git a/src/web-ui/src/locales/zh-CN/scenes/agents.json b/src/web-ui/src/locales/zh-CN/scenes/agents.json index cd0558ada..806bc68ef 100644 --- a/src/web-ui/src/locales/zh-CN/scenes/agents.json +++ b/src/web-ui/src/locales/zh-CN/scenes/agents.json @@ -158,7 +158,7 @@ "back": "返回专业智能体", "openSettings": "审核设置", "title": "代码审核团队", - "subtitle": "配置深度审查及 /DeepReview 使用的代码审核团队。每个审核员默认使用快速模型,你也可以单独修改。", + "subtitle": "配置深度审核及 /DeepReview 使用的代码审核团队。每个审核员默认使用快速模型,你也可以单独修改。", "summaryTitle": "团队概览", "summaryDescription": "多个审核员会并行覆盖目标范围,最后由质检员汇总为最终结论。", "membersTitle": "团队成员", @@ -200,7 +200,7 @@ "memberDetailDescription": "团队运行时,每个审核员都会保留自己的独立上下文。", "responsibilities": "职责范围", "model": "使用模型", - "modelDescription": "只修改当前审核员使用的模型。深度审查默认使用快速模型,保存后会立即生效。", + "modelDescription": "只修改当前审核员使用的模型。深度审核默认使用快速模型,保存后会立即生效。", "remove": "移除成员", "removeDescription": "将这个额外 Sub-Agent 从代码审核团队中移除。固定角色不可删除。", "addTitle": "添加额外 Sub-Agent", @@ -227,9 +227,9 @@ }, "strategy": { "teamTitle": "审核策略", - "teamDescription": "选择整个代码审核团队的默认审查深度。单个审核员可以在成员详情中单独覆盖。", + "teamDescription": "选择整个代码审核团队的默认审核深度。单个审核员可以在成员详情中单独覆盖。", "memberTitle": "审核员策略", - "memberDescription": "只有当某个角色需要不同于团队默认值的审查深度时,才建议单独覆盖。", + "memberDescription": "只有当某个角色需要不同于团队默认值的审核深度时,才建议单独覆盖。", "impact": "大约 {{token}} token 消耗,{{runtime}} 耗时。", "inheritLabel": "继承团队({{level}})", "inheritSummary": "该审核员使用团队级审核策略。", @@ -241,16 +241,16 @@ }, "normal": { "label": "正常", - "summary": "适合日常代码审查的均衡深度,兼顾覆盖面和证据质量。" + "summary": "适合日常代码审核的均衡深度,兼顾覆盖面和证据质量。" }, "deep": { "label": "深度", - "summary": "适合高风险、大范围或发布前变更的多轮深度审查。" + "summary": "适合高风险、大范围或发布前变更的多轮深度审核。" } }, "members": { "businessLogic": { - "funName": "逻辑审查员", + "funName": "逻辑审核员", "role": "业务逻辑审核员", "description": "专门追踪业务规则、状态流转、回滚路径和真实用户正确性的流程侦探。", "responsibilities": [ @@ -260,7 +260,7 @@ ] }, "performance": { - "funName": "性能审查员", + "funName": "性能审核员", "role": "性能审核员", "description": "专盯热点路径、无效工作、阻塞调用和规模化回退问题的速度分析员。", "responsibilities": [ @@ -270,11 +270,11 @@ ] }, "security": { - "funName": "安全审查员", + "funName": "安全审核员", "role": "安全审核员", "description": "专门盯住注入风险、信任边界、权限错误以及危险文件或命令处理的边界守卫。", "responsibilities": [ - "审查信任边界、鉴权假设和敏感数据处理方式。", + "审核信任边界、鉴权假设和敏感数据处理方式。", "寻找注入、危险命令执行与数据暴露风险。", "优先给出不依赖大改造的具体修复方案。" ] @@ -282,10 +282,10 @@ "judge": { "funName": "审核仲裁员", "role": "审核质检员", - "description": "独立的第三方仲裁者,从逻辑一致性和证据质量角度校验审核员报告。仅在特定声明需要验证时才抽查代码位置,而非从头重新审查代码库。", + "description": "独立的第三方仲裁者,从逻辑一致性和证据质量角度校验审核员报告。仅在特定声明需要验证时才抽查代码位置,而非从头重新审核代码库。", "responsibilities": [ "基于逻辑一致性和证据质量,对各审核员的发现进行验证、合并、降级或驳回。", - "通过审查审核员的推理过程,过滤误报以及方向错误的优化建议。", + "通过审核审核员的推理过程,过滤误报以及方向错误的优化建议。", "仅在审核员的声明需要验证时,对特定代码位置进行抽查。", "确保每条保留的问题都带有明确修复方案或后续计划。" ] @@ -293,7 +293,7 @@ }, "extraReviewer": { "role": "额外专项审核员", - "description": "由用户手动加入的额外 Sub-Agent,会带着自己的系统提示、工具和视角参与深度审查。", + "description": "由用户手动加入的额外 Sub-Agent,会带着自己的系统提示、工具和视角参与深度审核。", "responsibilities": [ "从额外的独立视角补充本次审核目标中的问题发现。", "保持 scope 严格聚焦在本次 diff、commit 或工作区改动上。", diff --git a/src/web-ui/src/locales/zh-CN/settings.json b/src/web-ui/src/locales/zh-CN/settings.json index ddb0418ad..4d77a413c 100644 --- a/src/web-ui/src/locales/zh-CN/settings.json +++ b/src/web-ui/src/locales/zh-CN/settings.json @@ -11,7 +11,7 @@ "basics": "语言、主题、日志、终端 Shell 与开机启动。", "models": "AI 模型、API 密钥、供应商与代理。", "sessionConfig": "会话行为、工具与超时。", - "review": "深度审查策略、审核员模型与额外审核 Sub-Agent。", + "review": "深度审核策略、审核员模型与额外审核 Sub-Agent。", "aiContext": "规则与记忆等 AI 上下文。", "mcpTools": "MCP 服务器与工具集成。", "editor": "编辑器字体、显示与格式化。", @@ -375,8 +375,8 @@ "textChatDesc": "处理日常对话和问答", "codeGeneration": "代码生成", "codeGenerationDesc": "生成和补全代码", - "codeReview": "代码审查", - "codeReviewDesc": "审查和优化代码", + "codeReview": "代码审核", + "codeReviewDesc": "审核和优化代码", "planning": "任务规划", "planningDesc": "分解和规划复杂任务", "selectModel": "选择模型", diff --git a/src/web-ui/src/locales/zh-CN/settings/ai-features.json b/src/web-ui/src/locales/zh-CN/settings/ai-features.json index 935980020..65f9b7697 100644 --- a/src/web-ui/src/locales/zh-CN/settings/ai-features.json +++ b/src/web-ui/src/locales/zh-CN/settings/ai-features.json @@ -41,7 +41,7 @@ "builtinAgents": { "git-func-agent": { "displayName": "Git 功能", - "description": "提交信息、代码审查" + "description": "提交信息、代码审核" }, "startchat-func-agent": { "displayName": "会话启动", diff --git a/src/web-ui/src/locales/zh-CN/settings/default-model.json b/src/web-ui/src/locales/zh-CN/settings/default-model.json index df87c59f8..508ab8de7 100644 --- a/src/web-ui/src/locales/zh-CN/settings/default-model.json +++ b/src/web-ui/src/locales/zh-CN/settings/default-model.json @@ -58,7 +58,7 @@ "subAgents": { "git-func-agent": { "displayName": "Git 功能", - "description": "提交信息、代码审查" + "description": "提交信息、代码审核" }, "startchat-func-agent": { "displayName": "会话启动", diff --git a/src/web-ui/src/locales/zh-CN/settings/review.json b/src/web-ui/src/locales/zh-CN/settings/review.json index 283ffd598..dcb4b7cac 100644 --- a/src/web-ui/src/locales/zh-CN/settings/review.json +++ b/src/web-ui/src/locales/zh-CN/settings/review.json @@ -1,11 +1,11 @@ { "title": "审核", - "subtitle": "配置深度审查及 /DeepReview 命令如何组织审核员、应用审核深度,并通过质量检查汇总结果。", + "subtitle": "配置深度审核及 /DeepReview 命令如何组织审核员、应用审核深度,并通过质量检查汇总结果。", "loading": "正在加载审核设置...", "overview": { "title": "审核工作流", - "description": "深度审查会并行启动只读审核团队,再由质量检查员合并并校验最终报告。", - "badge": "深度审查", + "description": "深度审核会并行启动只读审核团队,再由质量检查员合并并校验最终报告。", + "badge": "深度审核", "command": { "title": "/DeepReview 入口", "description": "聊天会话启动完整代码审核团队时,会使用这里的策略、模型与执行设置。" diff --git a/src/web-ui/src/locales/zh-TW/common.json b/src/web-ui/src/locales/zh-TW/common.json index 536ea4546..d7d014051 100644 --- a/src/web-ui/src/locales/zh-TW/common.json +++ b/src/web-ui/src/locales/zh-TW/common.json @@ -179,8 +179,8 @@ "parentSession": "父會話", "childSourceWithoutTurn": "來自 {{parentTitle}}", "childSourceWithTurn": "來自 {{parentTitle}} · 第 {{turnIndex}} 輪", - "reviewRunning": "審查中", - "deepReviewRunning": "審查中", + "reviewRunning": "審核中", + "deepReviewRunning": "審核中", "unreadCompleted": "已完成 — 未讀", "unreadError": "執行失敗 — 未讀", "needProjectWorkspaceForSession": "請先打開或添加項目工作區。編碼與工作(Cowork)會話不能在助理區域創建。" diff --git a/src/web-ui/src/locales/zh-TW/errors.json b/src/web-ui/src/locales/zh-TW/errors.json index dd8e457a4..bf63bf43f 100644 --- a/src/web-ui/src/locales/zh-TW/errors.json +++ b/src/web-ui/src/locales/zh-TW/errors.json @@ -92,7 +92,7 @@ }, "contentPolicy": { "title": "模型安全策略已攔截", - "message": "模型服務商因策略原因攔截了輸入或輸出。請調整提示詞或審查範圍後再試。" + "message": "模型服務商因策略原因攔截了輸入或輸出。請調整提示詞或審核範圍後再試。" }, "actions": { "retry": "重試", diff --git a/src/web-ui/src/locales/zh-TW/flow-chat.json b/src/web-ui/src/locales/zh-TW/flow-chat.json index bfe71017e..23f007fa1 100644 --- a/src/web-ui/src/locales/zh-TW/flow-chat.json +++ b/src/web-ui/src/locales/zh-TW/flow-chat.json @@ -150,7 +150,7 @@ "select": "選擇智能體", "default": "默認助手", "coding": "編程助手", - "review": "代碼審查", + "review": "代碼審核", "architect": "架構師", "custom": "自定義智能體" }, @@ -226,14 +226,14 @@ "initUsage": "請直接使用 /init,不要附加額外參數。", "initFailed": "初始化 AGENTS.md 失敗", "initPrompt": "請根據當前項目內容生成或更新 AGENTS.md", - "deepreviewAction": "深度審查", - "deepreviewNoSession": "當前沒有可用於深度審查的會話", - "deepreviewUsage": "請使用 /DeepReview,可附加聚焦說明,例如 /DeepReview 審查提交 abc123 的安全風險。", - "deepreviewFailed": "深度審查失敗", - "deepreviewInfo": "深度審查會拉起並行程式碼審核團隊,可能會比普通審查耗時更長且 Token 消耗更高。", - "deepreviewBusy": "目前會話已有審查正在進行,請停止或等待完成後再開始新的深度審查。", - "deepreviewNestedDisabled": "深度審查只能從主會話啟動。", - "deepreviewThreadTitle": "深度審查", + "deepreviewAction": "深度審核", + "deepreviewNoSession": "當前沒有可用於深度審核的會話", + "deepreviewUsage": "請使用 /DeepReview,可附加聚焦說明,例如 /DeepReview 審核提交 abc123 的安全風險。", + "deepreviewFailed": "深度審核失敗", + "deepreviewInfo": "深度審核會拉起並行程式碼審核團隊,可能會比普通審核耗時更長且 Token 消耗更高。", + "deepreviewBusy": "目前會話已有審核正在進行,請停止或等待完成後再開始新的深度審核。", + "deepreviewNestedDisabled": "深度審核只能從主會話啟動。", + "deepreviewThreadTitle": "深度審核", "currentMode": "當前模式: {{mode}}", "noMatchingMode": "沒有匹配的模式", "noMatchingCommand": "沒有匹配的命令", @@ -339,9 +339,9 @@ "noSession": "當前沒有可用於 /btw 的會話" }, "childSession": { - "stopReview": "中止審查", - "stoppingReview": "正在中止審查...", - "stopReviewFailed": "中止審查會話失敗。", + "stopReview": "中止審核", + "stoppingReview": "正在中止審核...", + "stopReviewFailed": "中止審核會話失敗。", "kinds": { "btw": { "short": "側問", @@ -349,24 +349,24 @@ "origin": "來自" }, "review": { - "short": "審查", - "title": "代碼審查", - "origin": "審查來源" + "short": "審核", + "title": "代碼審核", + "origin": "審核來源" }, "deep_review": { "short": "深審", - "title": "深度審查", - "origin": "審查來源" + "title": "深度審核", + "origin": "審核來源" } } }, "deepReviewActionBar": { - "reviewCompleted": "深度審查已完成", + "reviewCompleted": "深度審核已完成", "fixRunning": "正在修復...", "fixCompleted": "修復完成", "fixFailed": "修復失敗", "fixTimeout": "修復超時", - "reviewError": "審查出錯", + "reviewError": "審核出錯", "dismiss": "關閉", "close": "關閉", "showCustomInput": "補充說明", @@ -374,49 +374,49 @@ "customInstructionsPlaceholder": "描述修復的額外要求或上下文信息...", "fillBackInput": "填入輸入框", "fillBackInputHint": "將選中的修復計劃填入輸入框,供手動編輯", - "reviewInterrupted": "深度審查已中斷", + "reviewInterrupted": "深度審核已中斷", "resumeBlocked": "繼續前需要處理問題", - "resumeRunning": "正在繼續審查...", + "resumeRunning": "正在繼續審核...", "resumeFailed": "繼續失敗", - "resumeBlockedHint": "請先處理模型配置或額度問題,再繼續審查。", - "resumeBlockedConfirmTitle": "繼續審查?", - "resumeBlockedConfirmMessage": "導致審查中斷的錯誤尚未解決,繼續審查可能會再次失敗。是否仍要繼續?", + "resumeBlockedHint": "請先處理模型配置或額度問題,再繼續審核。", + "resumeBlockedConfirmTitle": "繼續審核?", + "resumeBlockedConfirmMessage": "導致審核中斷的錯誤尚未解決,繼續審核可能會再次失敗。是否仍要繼續?", "resumeBlockedConfirmAction": "仍然繼續", - "resumeReview": "繼續審查", - "resumeRequestDisplay": "繼續中斷的深度審查", - "resumeFailedMessage": "無法繼續深度審查。請檢查模型設置,或稍後重試。", + "resumeReview": "繼續審核", + "resumeRequestDisplay": "繼續中斷的深度審核", + "resumeFailedMessage": "無法繼續深度審核。請檢查模型設置,或稍後重試。", "openModelSettings": "打開模型設置", "copyDiagnostics": "複製診斷信息", "diagnosticsCopied": "診斷信息已複製", - "diagnosticsTitle": "=== 深度審查中斷診斷信息 ===", + "diagnosticsTitle": "=== 深度審核中斷診斷信息 ===", "diagnosticsErrorType": "錯誤類型", "diagnosticsDescription": "錯誤描述", "diagnosticsSuggestedActions": "建議操作", "diagnosticsTechnicalDetails": "技術詳情" }, "reviewActionBar": { - "reviewCompletedStandard": "審查已完成", - "reviewCompletedDeep": "深度審查已完成", - "fixRequestDisplayStandard": "開始修復程式碼審查問題", - "fixAndReviewRequestDisplayStandard": "修復程式碼審查問題並重新審查", - "fixRequestDisplayDeep": "開始修復深度審查問題", - "fixAndReviewRequestDisplayDeep": "修復深度審查問題並重新審查", + "reviewCompletedStandard": "審核已完成", + "reviewCompletedDeep": "深度審核已完成", + "fixRequestDisplayStandard": "開始修復程式碼審核問題", + "fixAndReviewRequestDisplayStandard": "修復程式碼審核問題並重新審核", + "fixRequestDisplayDeep": "開始修復深度審核問題", + "fixAndReviewRequestDisplayDeep": "修復深度審核問題並重新審核", "needsDecisionTag": "需決策" }, "deepReviewConsent": { - "windowTitle": "深度審查", + "windowTitle": "深度審核", "eyebrow": "程式碼審核團隊", - "title": "開始深度審查?", - "body": "深度審查會啟動並行程式碼審核團隊,通常比普通審查耗時更久,也會消耗更多 Token。", + "title": "開始深度審核?", + "body": "深度審核會啟動並行程式碼審核團隊,通常比普通審核耗時更久,也會消耗更多 Token。", "readonlyLabel": "首次只讀", - "readonly": "首次審查默認只讀:它會先輸出問題和修復計劃,不會直接修改代碼。", + "readonly": "首次審核默認只讀:它會先輸出問題和修復計劃,不會直接修改代碼。", "costLabel": "Token 消耗更高", - "cost": "預期消耗:多個審查角色加質量門禁,運行時間和 Token 可能達到普通審查的數倍。", + "cost": "預期消耗:多個審核角色加質量門禁,運行時間和 Token 可能達到普通審核的數倍。", "timeLabel": "耗時更久", - "time": "運行期間可以繼續工作;只有整個深度審查在後台完成時才會通知你。", + "time": "運行期間可以繼續工作;只有整個深度審核在後台完成時才會通知你。", "dontShowAgain": "下次不再提示", "cancel": "取消", - "confirm": "開始深度審查" + "confirm": "開始深度審核" }, "flowChatHeader": { "turnList": "輪次列表", @@ -441,41 +441,41 @@ }, "sessionFilesBadge": { "files": "文件", - "reviewLabel": "審查", - "reviewAll": "全部審查", - "reviewMenuLabel": "審查", - "reviewMenuHint": "選擇審查深度", - "reviewModeStandard": "普通審查", - "reviewModeDeep": "深度審查", - "reviewRunningStandard": "審查中", - "reviewRunningDeep": "審查中", - "reviewRunningHint": "請等待當前審查完成,或在審查頁面中止它。", + "reviewLabel": "審核", + "reviewAll": "全部審核", + "reviewMenuLabel": "審核", + "reviewMenuHint": "選擇審核深度", + "reviewModeStandard": "普通審核", + "reviewModeDeep": "深度審核", + "reviewRunningStandard": "審核中", + "reviewRunningDeep": "審核中", + "reviewRunningHint": "請等待當前審核完成,或在審核頁面中止它。", "deepReviewLabel": "深審", - "deepReviewAll": "深度審查", + "deepReviewAll": "深度審核", "loadFailed": "加載失敗", "clickToViewDiff": "點擊查看 Diff", "review": { - "displayMessage": "審查修改的文件:\n{{files}}", - "displayMessageFiltered": "審查過濾後的文件:\n{{files}}\n\n已跳過 {{skipped}} 個命中排除規則的文件。", + "displayMessage": "審核修改的文件:\n{{files}}", + "displayMessageFiltered": "審核過濾後的文件:\n{{files}}\n\n已跳過 {{skipped}} 個命中排除規則的文件。", "filteredNotice": "本次將分析 {{included}} 個文件,並跳過 {{skipped}} 個命中排除規則的文件,例如鎖文件、生成物或二進制資源。", - "noEligibleFiles": "排除命中規則的文件後,本次會話裡沒有可審查文件。", - "prompt": "請審查本次會話中修改的以下文件:\n\n{{files}}", - "promptFiltered": "請審查本次會話中修改的以下文件:\n\n{{files}}\n\n請不要審查另外 {{skipped}} 個已跳過的文件,因為它們命中了鎖文件、生成物或二進制文件的排除規則。", - "threadTitle": "代碼審查" + "noEligibleFiles": "排除命中規則的文件後,本次會話裡沒有可審核文件。", + "prompt": "請審核本次會話中修改的以下文件:\n\n{{files}}", + "promptFiltered": "請審核本次會話中修改的以下文件:\n\n{{files}}\n\n請不要審核另外 {{skipped}} 個已跳過的文件,因為它們命中了鎖文件、生成物或二進制文件的排除規則。", + "threadTitle": "代碼審核" }, "deepReview": { - "info": "深度審查會啟動並行程式碼審核團隊,可能更耗時並消耗更多 Token。", - "displayMessage": "深度審查修改的文件:\n{{files}}", - "displayMessageFiltered": "深度審查過濾後的文件:\n{{files}}\n\n已跳過 {{skipped}} 個命中排除規則的文件。", - "threadTitle": "深度審查" + "info": "深度審核會啟動並行程式碼審核團隊,可能更耗時並消耗更多 Token。", + "displayMessage": "深度審核修改的文件:\n{{files}}", + "displayMessageFiltered": "深度審核過濾後的文件:\n{{files}}\n\n已跳過 {{skipped}} 個命中排除規則的文件。", + "threadTitle": "深度審核" } }, "sessionFileModificationsBar": { "filesCount": "{{count}} 個文件", "expandList": "展開列表", "collapseList": "收起列表", - "reviewSource": "審查", - "deepReviewSource": "深度審查" + "reviewSource": "審核", + "deepReviewSource": "深度審核" }, "snapshotSystem": { "errors": { @@ -996,31 +996,31 @@ "rollbackTooltip": "回滾到此輪" }, "reviewSessionSummary": { - "standardTitle": "審查", - "deepTitle": "深度審查", + "standardTitle": "審核", + "deepTitle": "深度審核", "running": "進行中", "failed": "失敗", "issueCount": "{{count}} 個問題", "noIssues": "無阻塞問題", "filesChanged": "{{count}} 個文件", - "waitingSummary": "審查仍在運行,程式碼審核團隊完成後會在這裡顯示結果。", - "emptySummary": "暫時沒有結構化審查總結。", - "changedFilesTitle": "本次審查涉及的文件", - "openReview": "打開審查頁面" + "waitingSummary": "審核仍在運行,程式碼審核團隊完成後會在這裡顯示結果。", + "emptySummary": "暫時沒有結構化審核總結。", + "changedFilesTitle": "本次審核涉及的文件", + "openReview": "打開審核頁面" }, "codeReview": { - "reviewResult": "審查結果", - "reviewingCode": "正在審查代碼...", - "preparingReview": "準備代碼審查", - "reviewFailed": "代碼審查失敗:{{error}}", + "reviewResult": "審核結果", + "reviewingCode": "正在審核代碼...", + "preparingReview": "準備代碼審核", + "reviewFailed": "代碼審核失敗:{{error}}", "unknownError": "未知錯誤", "collapseDetails": "收起詳情", "expandDetails": "展開詳情", "overallAssessment": "總體評估", "riskLevel": "風險等級", "recommendedAction": "建議操作", - "deepReviewResult": "深度審查結果", - "reviewMode": "審查模式", + "deepReviewResult": "深度審核結果", + "reviewMode": "審核模式", "reviewScope": "範圍", "contextLimitations": "上下文限制", "issuesCount": "問題 ({{count}})", @@ -1054,9 +1054,9 @@ "other": "其他" }, "report": { - "titleStandard": "程式碼審查報告", - "titleDeep": "深度審查報告", - "reviewDecision": "審查結論", + "titleStandard": "程式碼審核報告", + "titleDeep": "深度審核報告", + "reviewDecision": "審核結論", "noIssues": "沒有已驗證問題。", "status": "狀態", "findings": "問題數", @@ -1068,14 +1068,14 @@ "copyMarkdown": "複製 Markdown", "openMarkdown": "以 Markdown 開啟", "saveMarkdown": "儲存 Markdown", - "copySuccess": "已複製 Markdown 審查報告。", - "copyFailed": "複製審查報告失敗。", - "openFailed": "在編輯器中開啟審查報告失敗。", - "saveSuccess": "審查報告已匯出。", - "saveFailed": "匯出審查報告失敗。", - "saveDialogTitle": "匯出審查報告", - "fileNamePrefix": "BitFun-程式碼審查", - "editorTitle": "審查報告" + "copySuccess": "已複製 Markdown 審核報告。", + "copyFailed": "複製審核報告失敗。", + "openFailed": "在編輯器中開啟審核報告失敗。", + "saveSuccess": "審核報告已匯出。", + "saveFailed": "匯出審核報告失敗。", + "saveDialogTitle": "匯出審核報告", + "fileNamePrefix": "BitFun-程式碼審核", + "editorTitle": "審核報告" }, "reviewModes": { "standard": "普通", @@ -1083,7 +1083,7 @@ }, "remediationActions": { "title": "下一步", - "hint": "深度審查默認只讀。請選擇要修復的計劃項。", + "hint": "深度審核默認只讀。請選擇要修復的計劃項。", "selectAll": "全選", "selectionCount": "已選擇 {{selected}}/{{total}} 項", "expandPlan": "詳情", @@ -1095,11 +1095,11 @@ "location": "位置", "noSelectionHint": "至少選擇一項修復計劃後才能開始修復。", "startFix": "開始修復", - "fixAndReview": "修復後重新審查", + "fixAndReview": "修復後重新審核", "cancel": "取消", - "fixUnavailable": "無法開始修復:審查會話不可用。", - "fixRequestDisplay": "開始修復深度審查問題", - "fixAndReviewRequestDisplay": "修復深度審查問題並重新審查" + "fixUnavailable": "無法開始修復:審核會話不可用。", + "fixRequestDisplay": "開始修復深度審核問題", + "fixAndReviewRequestDisplay": "修復深度審核問題並重新審核" }, "riskLevels": { "low": "低風險", @@ -1160,8 +1160,8 @@ "timeSinceLastCommit": "上次提交:", "timeIcon": "時間", "youMightWant": "您可能想要:", - "defaultAction1": "審查最近的更改", - "defaultAction1Command": "審查最近的更改", + "defaultAction1": "審核最近的更改", + "defaultAction1Command": "審核最近的更改", "defaultAction2": "繼續之前的工作", "defaultAction2Command": "繼續之前的工作", "defaultAction3": "開始新功能", diff --git a/src/web-ui/src/locales/zh-TW/panels/git.json b/src/web-ui/src/locales/zh-TW/panels/git.json index 2ea1ad5f4..3dacccb49 100644 --- a/src/web-ui/src/locales/zh-TW/panels/git.json +++ b/src/web-ui/src/locales/zh-TW/panels/git.json @@ -33,7 +33,7 @@ "commitWithCount": "提交 ({{count}})", "aiGenerateCommit": "AI 生成提交信息", "cancelGenerate": "取消生成", - "aiCodeReview": "AI 代碼審查", + "aiCodeReview": "AI 代碼審核", "viewAllDiff": "查看所有差異", "discardFile": "恢復此文件", "deleteFile": "刪除此文件", @@ -139,10 +139,10 @@ } }, "review": { - "noFilesToReview": "沒有文件變更,無法進行代碼審查", - "displayMessage": "審查 Git 變更文件:\n{{files}}", - "requestMessage": "請審查以下 Git 變更文件:\n\n{{files}}", - "sendFailed": "發送代碼審查請求失敗" + "noFilesToReview": "沒有文件變更,無法進行代碼審核", + "displayMessage": "審核 Git 變更文件:\n{{files}}", + "requestMessage": "請審核以下 Git 變更文件:\n\n{{files}}", + "sendFailed": "發送代碼審核請求失敗" }, "relativeTime": { "justNow": "剛剛", diff --git a/src/web-ui/src/locales/zh-TW/scenes/agents.json b/src/web-ui/src/locales/zh-TW/scenes/agents.json index 05c4fb00a..52875610d 100644 --- a/src/web-ui/src/locales/zh-TW/scenes/agents.json +++ b/src/web-ui/src/locales/zh-TW/scenes/agents.json @@ -158,7 +158,7 @@ "back": "返回專業智能體", "openSettings": "審核設定", "title": "程式碼審核團隊", - "subtitle": "配置深度審查及 /DeepReview 使用的程式碼審核團隊。每個審核員預設使用快速模型,也可以單獨修改。", + "subtitle": "配置深度審核及 /DeepReview 使用的程式碼審核團隊。每個審核員預設使用快速模型,也可以單獨修改。", "summaryTitle": "團隊概覽", "summaryDescription": "多個審核員會並行覆蓋目標範圍,最後由質檢員彙總為最終結論。", "membersTitle": "團隊成員", @@ -227,9 +227,9 @@ }, "strategy": { "teamTitle": "審核策略", - "teamDescription": "選擇整個程式碼審核團隊的預設審查深度。單個審核員可以在成員詳情中單獨覆蓋。", + "teamDescription": "選擇整個程式碼審核團隊的預設審核深度。單個審核員可以在成員詳情中單獨覆蓋。", "memberTitle": "審核員策略", - "memberDescription": "只有當某個角色需要不同於團隊預設值的審查深度時,才建議單獨覆蓋。", + "memberDescription": "只有當某個角色需要不同於團隊預設值的審核深度時,才建議單獨覆蓋。", "impact": "大約 {{token}} token 消耗,{{runtime}} 耗時。", "inheritLabel": "繼承團隊({{level}})", "inheritSummary": "該審核員使用團隊級審核策略。", @@ -241,16 +241,16 @@ }, "normal": { "label": "正常", - "summary": "適合日常代碼審查的均衡深度,兼顧覆蓋面和證據質量。" + "summary": "適合日常代碼審核的均衡深度,兼顧覆蓋面和證據質量。" }, "deep": { "label": "深度", - "summary": "適合高風險、大範圍或發布前變更的多輪深度審查。" + "summary": "適合高風險、大範圍或發布前變更的多輪深度審核。" } }, "members": { "businessLogic": { - "funName": "邏輯審查員", + "funName": "邏輯審核員", "role": "業務邏輯審核員", "description": "專門追蹤業務規則、狀態流轉、回滾路徑和真實用戶正確性的流程偵探。", "responsibilities": [ @@ -260,7 +260,7 @@ ] }, "performance": { - "funName": "效能審查員", + "funName": "效能審核員", "role": "性能審核員", "description": "專盯熱點路徑、無效工作、阻塞調用和規模化回退問題的速度分析員。", "responsibilities": [ @@ -270,11 +270,11 @@ ] }, "security": { - "funName": "安全審查員", + "funName": "安全審核員", "role": "安全審核員", "description": "專門盯住注入風險、信任邊界、權限錯誤以及危險文件或命令處理的邊界守衛。", "responsibilities": [ - "審查信任邊界、鑑權假設和敏感數據處理方式。", + "審核信任邊界、鑑權假設和敏感數據處理方式。", "尋找注入、危險命令執行與數據暴露風險。", "優先給出不依賴大改造的具體修復方案。" ] @@ -282,10 +282,10 @@ "judge": { "funName": "審核仲裁員", "role": "審核質檢員", - "description": "獨立的第三方仲裁者,從邏輯一致性和證據品質角度校驗審核員報告。僅在特定聲明需要驗證時才抽查程式碼位置,而非從頭重新審查程式碼庫。", + "description": "獨立的第三方仲裁者,從邏輯一致性和證據品質角度校驗審核員報告。僅在特定聲明需要驗證時才抽查程式碼位置,而非從頭重新審核程式碼庫。", "responsibilities": [ "基於邏輯一致性和證據品質,對各審核員的發現進行驗證、合併、降級或駁回。", - "透過審查審核員的推理過程,過濾誤報以及方向錯誤的優化建議。", + "透過審核審核員的推理過程,過濾誤報以及方向錯誤的優化建議。", "僅在審核員的聲明需要驗證時,對特定程式碼位置進行抽查。", "確保每條保留的問題都帶有明確修復方案或後續計劃。" ] @@ -293,7 +293,7 @@ }, "extraReviewer": { "role": "額外專項審核員", - "description": "由用戶手動加入的額外 Sub-Agent,會帶著自己的系統提示、工具和視角參與深度審查。", + "description": "由用戶手動加入的額外 Sub-Agent,會帶著自己的系統提示、工具和視角參與深度審核。", "responsibilities": [ "從額外的獨立視角補充本次審核目標中的問題發現。", "保持 scope 嚴格聚焦在本次 diff、commit 或工作區改動中。", diff --git a/src/web-ui/src/locales/zh-TW/settings.json b/src/web-ui/src/locales/zh-TW/settings.json index 05104c79f..7c04b212d 100644 --- a/src/web-ui/src/locales/zh-TW/settings.json +++ b/src/web-ui/src/locales/zh-TW/settings.json @@ -11,7 +11,7 @@ "basics": "語言、主題、日誌、終端 Shell 與開機啟動。", "models": "AI 模型、API 密鑰、供應商與代理。", "sessionConfig": "會話行為、工具與超時。", - "review": "深度審查策略、審核員模型與額外審核 Sub-Agent。", + "review": "深度審核策略、審核員模型與額外審核 Sub-Agent。", "aiContext": "規則與記憶等 AI 上下文。", "mcpTools": "MCP 服務器與工具集成。", "editor": "編輯器字體、顯示與格式化。", @@ -375,8 +375,8 @@ "textChatDesc": "處理日常對話和問答", "codeGeneration": "代碼生成", "codeGenerationDesc": "生成和補全代碼", - "codeReview": "代碼審查", - "codeReviewDesc": "審查和優化代碼", + "codeReview": "代碼審核", + "codeReviewDesc": "審核和優化代碼", "planning": "任務規劃", "planningDesc": "分解和規劃複雜任務", "selectModel": "選擇模型", diff --git a/src/web-ui/src/locales/zh-TW/settings/ai-features.json b/src/web-ui/src/locales/zh-TW/settings/ai-features.json index f189f3ae1..bfe893ba7 100644 --- a/src/web-ui/src/locales/zh-TW/settings/ai-features.json +++ b/src/web-ui/src/locales/zh-TW/settings/ai-features.json @@ -41,7 +41,7 @@ "builtinAgents": { "git-func-agent": { "displayName": "Git 功能", - "description": "提交信息、代碼審查" + "description": "提交信息、代碼審核" }, "startchat-func-agent": { "displayName": "會話啟動", diff --git a/src/web-ui/src/locales/zh-TW/settings/default-model.json b/src/web-ui/src/locales/zh-TW/settings/default-model.json index 8cdaea709..4bd4c055e 100644 --- a/src/web-ui/src/locales/zh-TW/settings/default-model.json +++ b/src/web-ui/src/locales/zh-TW/settings/default-model.json @@ -58,7 +58,7 @@ "subAgents": { "git-func-agent": { "displayName": "Git 功能", - "description": "提交信息、代碼審查" + "description": "提交信息、代碼審核" }, "startchat-func-agent": { "displayName": "會話啟動", diff --git a/src/web-ui/src/locales/zh-TW/settings/review.json b/src/web-ui/src/locales/zh-TW/settings/review.json index 55d29a74d..35a6b1195 100644 --- a/src/web-ui/src/locales/zh-TW/settings/review.json +++ b/src/web-ui/src/locales/zh-TW/settings/review.json @@ -1,11 +1,11 @@ { "title": "審核", - "subtitle": "配置深度審查及 /DeepReview 命令如何組織審核員、套用審核深度,並透過品質檢查彙總結果。", + "subtitle": "配置深度審核及 /DeepReview 命令如何組織審核員、套用審核深度,並透過品質檢查彙總結果。", "loading": "正在載入審核設定...", "overview": { "title": "審核工作流", - "description": "深度審查會並行啟動唯讀審核團隊,再由品質檢查員合併並校驗最終報告。", - "badge": "深度審查", + "description": "深度審核會並行啟動唯讀審核團隊,再由品質檢查員合併並校驗最終報告。", + "badge": "深度審核", "command": { "title": "/DeepReview 入口", "description": "聊天工作階段啟動完整程式碼審核團隊時,會使用這裡的策略、模型與執行設定。" From 25304799c1dcc2be7d3f6fa12b5b0b64aefc5d35 Mon Sep 17 00:00:00 2001 From: limityan Date: Sun, 26 Apr 2026 10:23:59 +0800 Subject: [PATCH 3/4] fix(flow-chat): use scrollToIndex for unread session scroll-to-bottom --- .../components/modern/VirtualMessageList.tsx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx b/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx index 0c7482c17..97e56ab8f 100644 --- a/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx +++ b/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx @@ -1678,9 +1678,20 @@ export const VirtualMessageList = forwardRef((_, ref) => const hasUnread = activeSession?.hasUnreadCompletion; const isFinished = !isStreamingOutput; - if (hasUnread && isFinished && virtuosoRef.current) { + if (hasUnread && isFinished && virtuosoRef.current && virtualItems.length > 0) { + // Use scrollToIndex instead of scrollTo({ top: largeNumber }) because + // Virtuoso's scrollHeight may not be stable immediately after a session + // switch; scrolling by index lets Virtuoso resolve the correct position. + const scrollToBottom = () => { + virtuosoRef.current?.scrollToIndex({ + index: virtualItems.length - 1, + align: 'end', + behavior: 'auto', + }); + }; + // Allow two frames for virtual items to settle before scrolling. requestAnimationFrame(() => { - virtuosoRef.current?.scrollTo({ top: 999999999, behavior: 'auto' }); + requestAnimationFrame(scrollToBottom); }); } From 91734ce155be4de42eb2c0e00328584c7bb8af37 Mon Sep 17 00:00:00 2001 From: limityan Date: Sun, 26 Apr 2026 10:29:05 +0800 Subject: [PATCH 4/4] feat(session): add needsUserAttention for pending tool confirmations and ask-user questions Add high-priority attention indicator for sessions waiting for user input while not active. This complements the existing hasUnreadCompletion for finished sessions. Changes: * Add needsUserAttention field ('ask_user' | 'tool_confirm') to Session * Show pulsing dot + attention badge in sidebar for non-active sessions * Persist needsUserAttention in backend SessionMetadata * Clear attention state when user switches to session or tool resolves * Add i18n labels for all three locales The three session notification states are now treated as peers: * hasUnreadCompletion (completed/error): weak hint, small dot * needsUserAttention (ask_user/tool_confirm): strong hint, pulsing dot + badge * none: no indicator --- .../src/agentic/coordination/coordinator.rs | 1 + .../core/src/agentic/persistence/manager.rs | 1 + src/crates/core/src/service/session/types.rs | 14 +++++ .../sections/sessions/SessionsSection.scss | 56 +++++++++++++++++++ .../sections/sessions/SessionsSection.tsx | 30 ++++++++-- .../components/modern/VirtualMessageList.tsx | 1 + .../flow-chat-manager/ToolEventModule.ts | 21 +++++-- .../src/flow_chat/store/FlowChatStore.ts | 40 +++++++++++++ src/web-ui/src/flow_chat/types/flow-chat.ts | 9 +++ .../src/flow_chat/utils/sessionMetadata.ts | 6 ++ src/web-ui/src/locales/en-US/common.json | 4 ++ src/web-ui/src/locales/zh-CN/common.json | 4 ++ src/web-ui/src/locales/zh-TW/common.json | 4 ++ .../src/shared/types/session-history.ts | 7 +++ 14 files changed, 189 insertions(+), 9 deletions(-) diff --git a/src/crates/core/src/agentic/coordination/coordinator.rs b/src/crates/core/src/agentic/coordination/coordinator.rs index 3c462bd5e..a0e094134 100644 --- a/src/crates/core/src/agentic/coordination/coordinator.rs +++ b/src/crates/core/src/agentic/coordination/coordinator.rs @@ -813,6 +813,7 @@ Update the persona files and delete BOOTSTRAP.md as soon as bootstrap is complet workspace_path: Some(workspace_path.to_string()), workspace_hostname: None, unread_completion: None, + needs_user_attention: None, }; if let Err(e) = persistence_manager .save_session_metadata(&workspace_path_buf, &metadata) diff --git a/src/crates/core/src/agentic/persistence/manager.rs b/src/crates/core/src/agentic/persistence/manager.rs index eb7ec8246..b9f9d1836 100644 --- a/src/crates/core/src/agentic/persistence/manager.rs +++ b/src/crates/core/src/agentic/persistence/manager.rs @@ -662,6 +662,7 @@ impl PersistenceManager { workspace_path: Some(workspace_root), workspace_hostname, unread_completion: existing.and_then(|value| value.unread_completion.clone()), + needs_user_attention: existing.and_then(|value| value.needs_user_attention.clone()), } } diff --git a/src/crates/core/src/service/session/types.rs b/src/crates/core/src/service/session/types.rs index 78b47ac91..0387f4ced 100644 --- a/src/crates/core/src/service/session/types.rs +++ b/src/crates/core/src/service/session/types.rs @@ -101,6 +101,19 @@ pub struct SessionMetadata { alias = "unreadCompletion" )] pub unread_completion: Option, + + /// High-priority attention status for the session. + /// Set when the session requires user action while not the active session. + /// 'ask_user' → pending AskUserQuestion waiting for answer. + /// 'tool_confirm' → pending tool confirmations. + /// Takes precedence over unread_completion in the UI. + #[serde( + default, + skip_serializing_if = "Option::is_none", + alias = "needs_user_attention", + alias = "needsUserAttention" + )] + pub needs_user_attention: Option, } /// Session status @@ -516,6 +529,7 @@ impl SessionMetadata { workspace_path: None, workspace_hostname: None, unread_completion: None, + needs_user_attention: None, } } diff --git a/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.scss b/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.scss index 29ba8c145..ab767375a 100644 --- a/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.scss +++ b/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.scss @@ -230,6 +230,23 @@ } } + // Attention badge for high-priority states (ask_user / tool_confirm) + &__inline-item-attention-badge { + flex: 0 0 auto; + display: inline-flex; + align-items: center; + gap: 3px; + height: 14px; + padding: 0 5px; + border: 1px solid color-mix(in srgb, var(--color-warning, #f59e0b) 35%, var(--border-subtle)); + border-radius: 999px; + background: color-mix(in srgb, var(--color-warning, #f59e0b) 14%, transparent); + color: color-mix(in srgb, var(--color-warning, #f59e0b) 80%, var(--color-text-primary)); + font-size: var(--font-size-xxs); + font-weight: 600; + letter-spacing: 0.02em; + } + &__inline-item-tooltip { display: flex; flex-direction: column; @@ -271,6 +288,45 @@ 0 0 0 1px color-mix(in srgb, var(--color-error, #ef4444) 35%, transparent), 0 0 6px color-mix(in srgb, var(--color-error, #ef4444) 42%, transparent); } + + // High-priority attention states: larger, brighter, with subtle pulse + &.is-ask-user, + &.is-tool-confirm { + width: 8px; + height: 8px; + right: -2px; + bottom: -2px; + } + + &.is-ask-user { + background: var(--color-warning, #f59e0b); + box-shadow: + 0 0 0 1.5px color-mix(in srgb, var(--color-warning, #f59e0b) 45%, transparent), + 0 0 8px color-mix(in srgb, var(--color-warning, #f59e0b) 55%, transparent); + } + + &.is-tool-confirm { + background: var(--color-accent-500, #8b5cf6); + box-shadow: + 0 0 0 1.5px color-mix(in srgb, var(--color-accent-500, #8b5cf6) 45%, transparent), + 0 0 8px color-mix(in srgb, var(--color-accent-500, #8b5cf6) 55%, transparent); + } + + // Pulse animation for high-priority states + &.is-high-priority { + animation: bitfun-unread-dot-pulse 2s ease-in-out infinite; + } + } + + @keyframes bitfun-unread-dot-pulse { + 0%, 100% { + transform: scale(1); + opacity: 1; + } + 50% { + transform: scale(1.35); + opacity: 0.85; + } } &__inline-item-actions { diff --git a/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx b/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx index 089d74f71..f181726c3 100644 --- a/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx +++ b/src/web-ui/src/app/components/NavPanel/sections/sessions/SessionsSection.tsx @@ -183,6 +183,7 @@ const SessionsSection: React.FC = ({ requestAnimationFrame(() => { requestAnimationFrame(() => { flowChatStore.clearSessionUnreadCompletion(sessionId); + flowChatStore.clearSessionNeedsAttention(sessionId); }); }); }; @@ -477,7 +478,6 @@ const SessionsSection: React.FC = ({ : Bot : Code2; const isRunning = runningSessionIds.has(session.sessionId); - const isUnreadCompleted = !isRunning && session.hasUnreadCompletion; const isRowActive = isSessionNavRowActive({ rowSessionId: session.sessionId, activeTabId, @@ -485,6 +485,12 @@ const SessionsSection: React.FC = ({ activeChildSessionId: activeBtwSessionData?.childSessionId, activeChildParentSessionId: activeBtwSessionData?.parentSessionId, }); + // Determine the notification state for this session row. + // Priority: needsUserAttention > hasUnreadCompletion. + const attentionKind = !isRunning && !isRowActive + ? (session.needsUserAttention || session.hasUnreadCompletion || undefined) + : undefined; + const isHighPriority = !!session.needsUserAttention; const row = (
= ({ ].join(' ')} /> )} - {isUnreadCompleted ? ( + {attentionKind ? ( ) : null} @@ -577,6 +590,13 @@ const SessionsSection: React.FC = ({ {isChildSession ? ( {childSessionBadge} ) : null} + {attentionKind === 'ask_user' || attentionKind === 'tool_confirm' ? ( + + {attentionKind === 'ask_user' + ? t('nav.sessions.badgeNeedsInput') + : t('nav.sessions.badgeNeedsConfirm')} + + ) : null} {reviewActivityKind ? ( diff --git a/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx b/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx index 97e56ab8f..2da070935 100644 --- a/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx +++ b/src/web-ui/src/flow_chat/components/modern/VirtualMessageList.tsx @@ -1722,6 +1722,7 @@ export const VirtualMessageList = forwardRef((_, ref) => cancelPendingAutoFollowArm, isStreamingOutput, latestTurnId, + virtualItems.length, ]); useEffect(() => { diff --git a/src/web-ui/src/flow_chat/services/flow-chat-manager/ToolEventModule.ts b/src/web-ui/src/flow_chat/services/flow-chat-manager/ToolEventModule.ts index aa954f593..f0bb50a72 100644 --- a/src/web-ui/src/flow_chat/services/flow-chat-manager/ToolEventModule.ts +++ b/src/web-ui/src/flow_chat/services/flow-chat-manager/ToolEventModule.ts @@ -405,7 +405,9 @@ function handleCompleted( }; store.updateModelRoundItem(sessionId, turnId, toolEvent.tool_id, updates as any); - + + store.clearSessionNeedsAttention(sessionId); + immediateSaveDialogTurn(context, sessionId, turnId); } @@ -428,7 +430,9 @@ function handleFailed( status: 'error', endTime: Date.now() } as any); - + + store.clearSessionNeedsAttention(sessionId); + immediateSaveDialogTurn(context, sessionId, turnId); } @@ -445,7 +449,7 @@ function handleCancelled( const existingToolItem = store.findToolItem(sessionId, turnId, toolEvent.tool_id); const currentStatus = existingToolItem?.status; const finalStatus = currentStatus === 'confirmed' ? 'confirmed' : 'cancelled'; - + store.updateModelRoundItem(sessionId, turnId, toolEvent.tool_id, { toolResult: { result: null, @@ -455,7 +459,9 @@ function handleCancelled( status: finalStatus, endTime: Date.now() } as any); - + + store.clearSessionNeedsAttention(sessionId); + immediateSaveDialogTurn(context, sessionId, turnId); } @@ -472,6 +478,13 @@ function handleConfirmationNeeded( requiresConfirmation: true, status: 'pending_confirmation' } as any); + + const state = store.getState(); + const activeSessionId = state.activeSessionId; + if (sessionId !== activeSessionId) { + const attentionKind = toolEvent.tool_name === 'AskUserQuestion' ? 'ask_user' : 'tool_confirm'; + store.setSessionNeedsAttention(sessionId, attentionKind); + } } /** diff --git a/src/web-ui/src/flow_chat/store/FlowChatStore.ts b/src/web-ui/src/flow_chat/store/FlowChatStore.ts index 74b504334..be9e9931f 100644 --- a/src/web-ui/src/flow_chat/store/FlowChatStore.ts +++ b/src/web-ui/src/flow_chat/store/FlowChatStore.ts @@ -1322,6 +1322,45 @@ export class FlowChatStore { this.onPersistUnreadCompletion?.(sessionId, undefined); } + public setSessionNeedsAttention( + sessionId: string, + attentionKind: 'ask_user' | 'tool_confirm' + ): void { + this.setState(prev => { + const session = prev.sessions.get(sessionId); + if (!session) return prev; + + const updatedSession: Session = { + ...session, + needsUserAttention: attentionKind, + }; + + const newSessions = new Map(prev.sessions); + newSessions.set(sessionId, updatedSession); + + return { ...prev, sessions: newSessions }; + }); + this.onPersistUnreadCompletion?.(sessionId, undefined); + } + + public clearSessionNeedsAttention(sessionId: string): void { + this.setState(prev => { + const session = prev.sessions.get(sessionId); + if (!session || !session.needsUserAttention) return prev; + + const updatedSession: Session = { + ...session, + needsUserAttention: undefined, + }; + + const newSessions = new Map(prev.sessions); + newSessions.set(sessionId, updatedSession); + + return { ...prev, sessions: newSessions }; + }); + this.onPersistUnreadCompletion?.(sessionId, undefined); + } + public async updateSessionTitle( sessionId: string, title: string, @@ -1615,6 +1654,7 @@ export class FlowChatStore { btwThreads: [], btwOrigin: relationship.btwOrigin, hasUnreadCompletion: metadata.unreadCompletion, + needsUserAttention: metadata.needsUserAttention, }; const newSessions = new Map(prev.sessions); diff --git a/src/web-ui/src/flow_chat/types/flow-chat.ts b/src/web-ui/src/flow_chat/types/flow-chat.ts index fa93d3637..b1374dd53 100644 --- a/src/web-ui/src/flow_chat/types/flow-chat.ts +++ b/src/web-ui/src/flow_chat/types/flow-chat.ts @@ -253,6 +253,15 @@ export interface Session { * 'completed' → green dot, 'error' → red dot. */ hasUnreadCompletion?: 'completed' | 'error'; + + /** + * Set when a session requires user attention while not the active session. + * This is a high-priority alert that takes precedence over hasUnreadCompletion. + * 'ask_user' → session has pending AskUserQuestion waiting for answer + * 'tool_confirm' → session has pending tool confirmations + * Cleared when the user switches to the session or the pending action is resolved. + */ + needsUserAttention?: 'ask_user' | 'tool_confirm'; } export interface SessionConfig { diff --git a/src/web-ui/src/flow_chat/utils/sessionMetadata.ts b/src/web-ui/src/flow_chat/utils/sessionMetadata.ts index f7d4868b9..5ec0a580e 100644 --- a/src/web-ui/src/flow_chat/utils/sessionMetadata.ts +++ b/src/web-ui/src/flow_chat/utils/sessionMetadata.ts @@ -246,6 +246,8 @@ export function buildSessionMetadata( | 'titleSource' | 'titleI18nKey' | 'titleI18nParams' + | 'hasUnreadCompletion' + | 'needsUserAttention' >, existingMetadata?: SessionMetadata | null ): SessionMetadata { @@ -296,5 +298,9 @@ export function buildSessionMetadata( remoteConnectionId: session.remoteConnectionId ?? existingMetadata?.remoteConnectionId, remoteSshHost: session.remoteSshHost ?? existingMetadata?.remoteSshHost, + unreadCompletion: + session.hasUnreadCompletion ?? existingMetadata?.unreadCompletion, + needsUserAttention: + session.needsUserAttention ?? existingMetadata?.needsUserAttention, }; } diff --git a/src/web-ui/src/locales/en-US/common.json b/src/web-ui/src/locales/en-US/common.json index 00e3d3677..a8df072bd 100644 --- a/src/web-ui/src/locales/en-US/common.json +++ b/src/web-ui/src/locales/en-US/common.json @@ -183,6 +183,10 @@ "deepReviewRunning": "Reviewing", "unreadCompleted": "Completed — unread", "unreadError": "Failed — unread", + "needsUserInput": "Needs your input", + "needsToolConfirm": "Needs confirmation", + "badgeNeedsInput": "Waiting", + "badgeNeedsConfirm": "Confirm", "needProjectWorkspaceForSession": "Open or add a project workspace first. Code and Cowork sessions cannot be created in the assistant area." }, "scheduledJobs": { diff --git a/src/web-ui/src/locales/zh-CN/common.json b/src/web-ui/src/locales/zh-CN/common.json index 9afe8ce41..96ffb2341 100644 --- a/src/web-ui/src/locales/zh-CN/common.json +++ b/src/web-ui/src/locales/zh-CN/common.json @@ -183,6 +183,10 @@ "deepReviewRunning": "审核中", "unreadCompleted": "已完成 — 未读", "unreadError": "执行失败 — 未读", + "needsUserInput": "等待你的输入", + "needsToolConfirm": "等待确认", + "badgeNeedsInput": "等待中", + "badgeNeedsConfirm": "待确认", "needProjectWorkspaceForSession": "请先打开或添加项目工作区。编码与工作(Cowork)会话不能在助理区域创建。" }, "scheduledJobs": { diff --git a/src/web-ui/src/locales/zh-TW/common.json b/src/web-ui/src/locales/zh-TW/common.json index d7d014051..aeba88c3c 100644 --- a/src/web-ui/src/locales/zh-TW/common.json +++ b/src/web-ui/src/locales/zh-TW/common.json @@ -183,6 +183,10 @@ "deepReviewRunning": "審核中", "unreadCompleted": "已完成 — 未讀", "unreadError": "執行失敗 — 未讀", + "needsUserInput": "等待你的輸入", + "needsToolConfirm": "等待確認", + "badgeNeedsInput": "等待中", + "badgeNeedsConfirm": "待確認", "needProjectWorkspaceForSession": "請先打開或添加項目工作區。編碼與工作(Cowork)會話不能在助理區域創建。" }, "scheduledJobs": { diff --git a/src/web-ui/src/shared/types/session-history.ts b/src/web-ui/src/shared/types/session-history.ts index a73de5ec5..d97c9d4ad 100644 --- a/src/web-ui/src/shared/types/session-history.ts +++ b/src/web-ui/src/shared/types/session-history.ts @@ -43,6 +43,13 @@ export interface SessionMetadata { workspaceHostname?: string; /** Unread completion status for the session. 'completed' → green dot, 'error' → red dot. */ unreadCompletion?: 'completed' | 'error'; + /** + * High-priority attention status for the session. + * 'ask_user' → pending AskUserQuestion waiting for answer. + * 'tool_confirm' → pending tool confirmations. + * Takes precedence over unreadCompletion in the UI. + */ + needsUserAttention?: 'ask_user' | 'tool_confirm'; } export type SessionStatus = 'active' | 'archived' | 'completed';