fix(coding): reconcile snapshot after abort

This commit is contained in:
2026-09-01 15:17:45 +08:00
parent 143aaec3d6
commit d2ef37bc4d
6 changed files with 165 additions and 13 deletions

View File

@@ -170,6 +170,10 @@ export function CodingChatPanel({
? `new:${activeProject.id}:${selectedAgent.id}`
: null;
const draftKey = targetConversationId ?? provisionalDraftKey;
const abortConversation = useCallback(async (conversationId: string) => {
await abortCodingConversation(conversationId);
await loadConversationSnapshot(conversationId, 'silent');
}, [loadConversationSnapshot]);
const promptMode = draftKey ? modesByDraftKey[draftKey] ?? 'prompt' : 'prompt';
const provisionalDraft = provisionalDraftKey ? provisionalDrafts[provisionalDraftKey] ?? '' : '';
const submissionError = draftKey ? submissionErrors[draftKey] ?? null : null;
@@ -598,6 +602,9 @@ export function CodingChatPanel({
onRename={async (title) => {
if (targetConversationId) await patchConversation(targetConversationId, { title });
}}
onAbort={async () => {
if (targetConversationId) await abortConversation(targetConversationId);
}}
onRecover={async () => {
if (targetConversationId) await recoverConversation(targetConversationId);
}}
@@ -698,13 +705,15 @@ export function CodingChatPanel({
onSubmit={handleSubmit}
onAbort={() => {
if (!targetConversationId) return;
void abortCodingConversation(targetConversationId).catch((error) => {
if (!draftKey) return;
setSubmissionErrors((current) => ({
...current,
[draftKey]: localSubmissionError(error),
}));
});
const conversationId = targetConversationId;
void abortConversation(conversationId)
.catch((error) => {
if (!draftKey) return;
setSubmissionErrors((current) => ({
...current,
[draftKey]: localSubmissionError(error),
}));
});
}}
onRecover={() => {
if (targetConversationId) {

View File

@@ -16,7 +16,6 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import { Input } from '@/components/ui/input';
import { abortCodingConversation } from '@/lib/coding-conversations';
import { cn } from '@/lib/utils';
import { useSettingsStore } from '@/stores/settings';
import type { ConversationSnapshot } from '@/types/coding-conversation';
@@ -54,11 +53,13 @@ export function CodingConversationHeader({
conversation,
snapshot,
onRename,
onAbort,
onRecover,
}: {
conversation: CodingConversationMetadata | null;
snapshot: ConversationSnapshot | null;
onRename(title: string): Promise<void>;
onAbort(): Promise<void>;
onRecover(): Promise<void>;
}) {
const sidebarCollapsed = useSettingsStore((state) => state.sidebarCollapsed);
@@ -128,7 +129,7 @@ export function CodingConversationHeader({
disabled={Boolean(busyAction) || !conversation}
aria-label="中止"
title="中止"
onClick={() => perform('abort', async () => abortCodingConversation(conversation!.id))}
onClick={() => perform('abort', onAbort)}
>
{busyAction === 'abort'
? <LoaderCircle className="h-4 w-4 animate-spin" aria-hidden="true" />