From 717854d06bbe2009e9d13def8eee9c7cafdb1ac2 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 26 Aug 2026 19:50:07 +0300 Subject: [PATCH] refactor: remove aborted status banner from chat UI Removes the transient aborted banner from the composer status area Simplifies status row rendering to focus on working state and pending changes Cleans up unused abort-status localization strings --- packages/ui/src/components/chat/ChatInput.tsx | 49 +++---------------- .../src/components/chat/ComposerStatusBar.tsx | 15 +----- packages/ui/src/components/chat/StatusRow.tsx | 22 ++------- .../components/chat/StatusRowContainer.tsx | 15 +----- packages/ui/src/lib/i18n/messages/de.ts | 1 - packages/ui/src/lib/i18n/messages/en.ts | 1 - packages/ui/src/lib/i18n/messages/es.ts | 1 - packages/ui/src/lib/i18n/messages/fr.ts | 1 - packages/ui/src/lib/i18n/messages/ja.ts | 1 - packages/ui/src/lib/i18n/messages/ko.ts | 1 - packages/ui/src/lib/i18n/messages/pl.ts | 1 - packages/ui/src/lib/i18n/messages/pt-BR.ts | 1 - packages/ui/src/lib/i18n/messages/uk.ts | 1 - packages/ui/src/lib/i18n/messages/zh-CN.ts | 1 - packages/ui/src/lib/i18n/messages/zh-TW.ts | 1 - 15 files changed, 15 insertions(+), 97 deletions(-) diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index 8d375160..1c5a40f1 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -419,7 +419,6 @@ const ChatInputComponent: React.FC = ({ const ensureGitStatus = useGitStore((state) => state.ensureStatus); const fetchGitStatus = useGitStore((state) => state.fetchStatus); const clearGitDiffCache = useGitStore((state) => state.clearDiffCache); - const [showAbortStatus, setShowAbortStatus] = React.useState(false); const setSessionAutoAccept = usePermissionStore((state) => state.setSessionAutoAccept); const [isNarrowComposer, setIsNarrowComposer] = React.useState(false); const [attachmentPreview, setAttachmentPreview] = React.useState({ @@ -697,7 +696,6 @@ const ChatInputComponent: React.FC = ({ attachments, }; }, [resolveInlineFileMention]); - const abortTimeoutRef = React.useRef | null>(null); const prevWasAbortedRef = React.useRef(false); // Issue linking state @@ -1721,29 +1719,15 @@ const ChatInputComponent: React.FC = ({ containerRef: dropZoneRef, }); - const startAbortIndicator = React.useCallback(() => { - if (abortTimeoutRef.current) { - clearTimeout(abortTimeoutRef.current); - abortTimeoutRef.current = null; - } - - setShowAbortStatus(true); - - abortTimeoutRef.current = setTimeout(() => { - setShowAbortStatus(false); - abortTimeoutRef.current = null; - }, 1800); - }, []); const handleAbort = React.useCallback(() => { clearAbortPrompt(); - startAbortIndicator(); // btw mode: the stop button stops the fork's turn, not the main // session's. const abortTarget = isBtwActive && btwSessionId ? btwSessionId : currentSessionId; void abortCurrentOperation(abortTarget || undefined); - }, [abortCurrentOperation, btwSessionId, clearAbortPrompt, currentSessionId, isBtwActive, startAbortIndicator]); + }, [abortCurrentOperation, btwSessionId, clearAbortPrompt, currentSessionId, isBtwActive]); const handleCycleAgent = React.useCallback((direction: 1 | -1 = 1) => { const nextAgentName = getCycledPrimaryAgentName(agents, currentAgentName, direction); @@ -2592,31 +2576,15 @@ const ChatInputComponent: React.FC = ({ handlePermissionAutoAcceptToggle(); }); + // Acknowledging the abort record is what lets the working chip resume for + // the next run; the old "Aborted" banner that used to accompany it is gone. React.useEffect(() => { - const pendingAbortBanner = Boolean(abortPromptSessionId) && abortPromptSessionId === currentSessionId; - if (!prevWasAbortedRef.current && pendingAbortBanner && !showAbortStatus) { - startAbortIndicator(); - if (currentSessionId) { - acknowledgeSessionAbort(currentSessionId); - } + const pendingAbort = Boolean(abortPromptSessionId) && abortPromptSessionId === currentSessionId; + if (!prevWasAbortedRef.current && pendingAbort && currentSessionId) { + acknowledgeSessionAbort(currentSessionId); } - prevWasAbortedRef.current = pendingAbortBanner; - }, [ - abortPromptSessionId, - acknowledgeSessionAbort, - currentSessionId, - showAbortStatus, - startAbortIndicator, - ]); - - React.useEffect(() => { - return () => { - if (abortTimeoutRef.current) { - clearTimeout(abortTimeoutRef.current); - abortTimeoutRef.current = null; - } - }; - }, []); + prevWasAbortedRef.current = pendingAbort; + }, [abortPromptSessionId, acknowledgeSessionAbort, currentSessionId]); return ( <> @@ -2687,7 +2655,6 @@ const ChatInputComponent: React.FC = ({ directory={currentSessionDirectoryForSync ?? currentDirectory} /> = ({ todo }) => { const EMPTY_TODOS: TodoItem[] = []; interface ComposerStatusBarProps { - showAbortStatus?: boolean; showTodos?: boolean; leftAccessory?: React.ReactNode; } export const ComposerStatusBar: React.FC = ({ - showAbortStatus, showTodos = true, leftAccessory, }) => { @@ -186,7 +184,7 @@ export const ComposerStatusBar: React.FC = ({ const hasTodoContent = showTodos && statusSummary.left > 0; const hasLeftAccessory = Boolean(leftAccessory); - const hasContent = Boolean(showAbortStatus) || hasTodoContent || hasLeftAccessory; + const hasContent = hasTodoContent || hasLeftAccessory; const popoverRef = React.useRef(null); React.useEffect(() => { @@ -252,16 +250,7 @@ export const ComposerStatusBar: React.FC = ({
{/* Left: abort status | pending-changes accessory */}
- {showAbortStatus ? ( -
- - -
- ) : leftAccessory ? ( - leftAccessory - ) : null} + {leftAccessory ?? null}
{/* Right: todos dropdown */} diff --git a/packages/ui/src/components/chat/StatusRow.tsx b/packages/ui/src/components/chat/StatusRow.tsx index a577a3d2..0b1efbb9 100644 --- a/packages/ui/src/components/chat/StatusRow.tsx +++ b/packages/ui/src/components/chat/StatusRow.tsx @@ -1,11 +1,9 @@ import React from "react"; import { useSessionUIStore } from '@/sync/session-ui-store'; import { WorkingPlaceholder } from "./message/parts/WorkingPlaceholder"; -import { Icon } from "@/components/icon/Icon"; -import { useI18n } from "@/lib/i18n"; // The floating assistant-status chip that hovers above the composer while the -// agent works ("Claude is working…", abort notice). ONLY that. The composer's +// agent works ("Claude is working…"). ONLY that. The composer's // own bar — pending changes, todos dropdown — is ComposerStatusBar: they used // to share this component, and every restyle of this chip (glass, placement) // silently dragged the composer bar and its dropdown along with it. @@ -17,10 +15,8 @@ interface StatusRowProps { statusText?: string | null; isGenericStatus?: boolean; isWaitingForPermission?: boolean; - wasAborted?: boolean; abortActive?: boolean; retryInfo?: { attempt?: number; next?: number } | null; - showAbortStatus?: boolean; agentName?: string; modelName?: string | null; providerId?: string | null; @@ -31,19 +27,16 @@ export const StatusRow: React.FC = ({ statusText = null, isGenericStatus, isWaitingForPermission, - wasAborted, abortActive, retryInfo, - showAbortStatus, agentName, modelName, providerId, }) => { - const { t } = useI18n(); const currentSessionId = useSessionUIStore((state) => state.currentSessionId); - const shouldRenderPlaceholder = !showAbortStatus && (wasAborted || !abortActive); - const hasContent = isWorking || Boolean(wasAborted) || Boolean(showAbortStatus); + const shouldRenderPlaceholder = !abortActive; + const hasContent = isWorking; if (!hasContent) { return null; @@ -63,14 +56,7 @@ export const StatusRow: React.FC = ({ a shrink-to-fit wrapper around it always collapsed to zero. */}
- {showAbortStatus ? ( -
- - -
- ) : shouldRenderPlaceholder ? ( + {shouldRenderPlaceholder ? ( { - const currentSessionId = useSessionUIStore((state) => state.currentSessionId); - const abortRecord = useSessionUIStore( - React.useCallback((state) => { - if (!currentSessionId) { - return null; - } - return state.sessionAbortFlags?.get(currentSessionId) ?? null; - }, [currentSessionId]), - ); const { activeModel, working } = useAssistantStatus(); const currentAgentName = useConfigStore((state) => state.currentAgentName); const providers = useConfigStore((state) => state.providers); @@ -35,16 +25,13 @@ export const StatusRowContainer: React.FC = React.memo(() => { return getProviderModelDisplayName(provider, activeModel.modelId) || null; }, [activeModel, providers]); - const wasAborted = Boolean(abortRecord && !abortRecord.acknowledged); - return ( = { "chat.statusRow.tasksTitle": "Tareas", "chat.statusRow.modelStatus": "{model} · {status}", "chat.statusRow.summary.activeLeft": "{active} activas · {left} restantes", - "chat.statusRow.aborted": "Interrumpido", "chat.revertIndicator.redo": "Rehacer", "chat.revertIndicator.redoAria": "Rehacer — restaurar mensajes revertidos", "chat.revertPopover.title": "Revertidos", diff --git a/packages/ui/src/lib/i18n/messages/fr.ts b/packages/ui/src/lib/i18n/messages/fr.ts index 573aed79..147b644f 100644 --- a/packages/ui/src/lib/i18n/messages/fr.ts +++ b/packages/ui/src/lib/i18n/messages/fr.ts @@ -1826,7 +1826,6 @@ export const dict = { 'chat.statusRow.tasksTitle': 'Tâches', 'chat.statusRow.modelStatus': '{model} · {status}', 'chat.statusRow.summary.activeLeft': '{active} actif · {left} gauche', - 'chat.statusRow.aborted': 'Avorté', 'chat.revertIndicator.redo': 'Refaire', 'chat.revertIndicator.redoAria': 'Rétablir : restaurer les messages annulés', 'chat.revertPopover.title': 'Rétabli', diff --git a/packages/ui/src/lib/i18n/messages/ja.ts b/packages/ui/src/lib/i18n/messages/ja.ts index 2b474a39..9bc2aaee 100644 --- a/packages/ui/src/lib/i18n/messages/ja.ts +++ b/packages/ui/src/lib/i18n/messages/ja.ts @@ -2080,7 +2080,6 @@ export const dict: Record = { 'chat.statusRow.tasksTitle': 'タスク', 'chat.statusRow.modelStatus': '{model} · {status}', 'chat.statusRow.summary.activeLeft': '{active}アクティブ · {left}残り', - 'chat.statusRow.aborted': '中止されました', 'chat.revertIndicator.redo': 'やり直し', 'chat.revertIndicator.redoAria': 'やり直し — 元に戻したメッセージを復元', 'chat.revertPopover.title': '元に戻しました', diff --git a/packages/ui/src/lib/i18n/messages/ko.ts b/packages/ui/src/lib/i18n/messages/ko.ts index 439f8ba6..f89cd65f 100644 --- a/packages/ui/src/lib/i18n/messages/ko.ts +++ b/packages/ui/src/lib/i18n/messages/ko.ts @@ -2086,7 +2086,6 @@ export const dict: Record = { 'chat.statusRow.tasksTitle': '작업', 'chat.statusRow.modelStatus': '{model} · {status}', 'chat.statusRow.summary.activeLeft': '{active}개 활성 · {left}개 남음', - 'chat.statusRow.aborted': '중단됨', 'chat.revertIndicator.redo': '다시 실행', 'chat.revertIndicator.redoAria': '다시 실행 — 되돌린 메시지 복원', 'chat.revertPopover.title': '되돌림', diff --git a/packages/ui/src/lib/i18n/messages/pl.ts b/packages/ui/src/lib/i18n/messages/pl.ts index 76610ba7..a729a278 100644 --- a/packages/ui/src/lib/i18n/messages/pl.ts +++ b/packages/ui/src/lib/i18n/messages/pl.ts @@ -776,7 +776,6 @@ export const dict: Record = { 'chat.statusRow.tasksTitle': 'Zadania', 'chat.statusRow.modelStatus': '{model} · {status}', 'chat.statusRow.summary.activeLeft': '{active} aktywne · {left} pozostało', - 'chat.statusRow.aborted': 'Przerwane', 'chat.revertIndicator.redo': 'Ponów', 'chat.revertIndicator.redoAria': 'Ponów — przywróć cofnięte wiadomości', 'chat.revertPopover.title': 'Cofnięte', diff --git a/packages/ui/src/lib/i18n/messages/pt-BR.ts b/packages/ui/src/lib/i18n/messages/pt-BR.ts index 0f63edb1..4a8a9f32 100644 --- a/packages/ui/src/lib/i18n/messages/pt-BR.ts +++ b/packages/ui/src/lib/i18n/messages/pt-BR.ts @@ -2062,7 +2062,6 @@ export const dict: Record = { "chat.statusRow.tasksTitle": "Tarefas", "chat.statusRow.modelStatus": "{model} · {status}", "chat.statusRow.summary.activeLeft": "{active} ativas · {left} restantes", - "chat.statusRow.aborted": "Interrompido", "chat.revertIndicator.redo": "Refazer", "chat.revertIndicator.redoAria": "Refazer — restaurar mensagens revertidas", "chat.revertPopover.title": "Revertidas", diff --git a/packages/ui/src/lib/i18n/messages/uk.ts b/packages/ui/src/lib/i18n/messages/uk.ts index 35f91a31..b94dfb15 100644 --- a/packages/ui/src/lib/i18n/messages/uk.ts +++ b/packages/ui/src/lib/i18n/messages/uk.ts @@ -2062,7 +2062,6 @@ export const dict: Record = { "chat.statusRow.tasksTitle": "завдання", "chat.statusRow.modelStatus": "{model} · {status}", "chat.statusRow.summary.activeLeft": "Активних: {active} · залишилось: {left}", - "chat.statusRow.aborted": "Перервано", "chat.revertIndicator.redo": "Повторити", "chat.revertIndicator.redoAria": "Повторити — відновити відкочені повідомлення", "chat.revertPopover.title": "Відкочено", diff --git a/packages/ui/src/lib/i18n/messages/zh-CN.ts b/packages/ui/src/lib/i18n/messages/zh-CN.ts index 7072ef26..6af651a2 100644 --- a/packages/ui/src/lib/i18n/messages/zh-CN.ts +++ b/packages/ui/src/lib/i18n/messages/zh-CN.ts @@ -2050,7 +2050,6 @@ export const dict: Record = { 'chat.statusRow.tasksTitle': '任务', 'chat.statusRow.modelStatus': '{model} · {status}', 'chat.statusRow.summary.activeLeft': '{active} 个活跃 · 剩余 {left} 个', - 'chat.statusRow.aborted': '已中止', 'chat.revertIndicator.redo': '重做', 'chat.revertIndicator.redoAria': '重做 — 恢复已撤回的消息', 'chat.revertPopover.title': '已撤回', diff --git a/packages/ui/src/lib/i18n/messages/zh-TW.ts b/packages/ui/src/lib/i18n/messages/zh-TW.ts index 17861ebd..89b9b996 100644 --- a/packages/ui/src/lib/i18n/messages/zh-TW.ts +++ b/packages/ui/src/lib/i18n/messages/zh-TW.ts @@ -2054,7 +2054,6 @@ export const dict: Record = { 'chat.statusRow.tasksTitle': '任務', 'chat.statusRow.modelStatus': '{model} · {status}', 'chat.statusRow.summary.activeLeft': '{active} 個活躍 · 剩餘 {left} 個', - 'chat.statusRow.aborted': '已中止', 'chat.revertIndicator.redo': '重做', 'chat.revertIndicator.redoAria': '重做 — 恢復已收回的訊息', 'chat.revertPopover.title': '已收回',