diff --git a/ui/src/components/CommentThread.tsx b/ui/src/components/CommentThread.tsx index 84041401..2501d95a 100644 --- a/ui/src/components/CommentThread.tsx +++ b/ui/src/components/CommentThread.tsx @@ -10,6 +10,7 @@ import { MarkdownEditor, type MarkdownEditorRef, type MentionOption } from "./Ma import { StatusBadge } from "./StatusBadge"; import { AgentIcon } from "./AgentIconPicker"; import { formatDateTime } from "../lib/utils"; +import { restoreSubmittedCommentDraft } from "../lib/comment-submit-draft"; import { PluginSlotOutlet } from "@/plugins/slots"; interface CommentWithRunMeta extends IssueComment { @@ -420,17 +421,24 @@ export function CommentThread({ if (!trimmed) return; const hasReassignment = enableReassign && reassignTarget !== currentAssigneeValue; const reassignment = hasReassignment ? parseReassignment(reassignTarget) : null; + const submittedBody = trimmed; setSubmitting(true); + setBody(""); try { // TODO: wire an explicit "send + interrupt" action through the composer if we expose it in the UI. - await onAdd(trimmed, reopen ? true : undefined, reassignment ?? undefined); - setBody(""); + await onAdd(submittedBody, reopen ? true : undefined, reassignment ?? undefined); if (draftKey) clearDraft(draftKey); setReopen(true); setReassignTarget(effectiveSuggestedAssigneeValue); } catch { - // Parent mutation handlers surface the failure and keep the draft intact. + setBody((current) => + restoreSubmittedCommentDraft({ + currentBody: current, + submittedBody, + }), + ); + // Parent mutation handlers surface the failure and the draft is restored for retry. } finally { setSubmitting(false); } diff --git a/ui/src/lib/comment-submit-draft.test.ts b/ui/src/lib/comment-submit-draft.test.ts new file mode 100644 index 00000000..395885f6 --- /dev/null +++ b/ui/src/lib/comment-submit-draft.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from "vitest"; +import { restoreSubmittedCommentDraft } from "./comment-submit-draft"; + +describe("restoreSubmittedCommentDraft", () => { + it("restores the submitted body when the editor is still empty after a failed request", () => { + expect( + restoreSubmittedCommentDraft({ + currentBody: "", + submittedBody: "Retry me", + }), + ).toBe("Retry me"); + }); + + it("treats whitespace-only input as empty when restoring a failed draft", () => { + expect( + restoreSubmittedCommentDraft({ + currentBody: " ", + submittedBody: "Retry me", + }), + ).toBe("Retry me"); + }); + + it("preserves newer input when the user has already typed again", () => { + expect( + restoreSubmittedCommentDraft({ + currentBody: "new draft", + submittedBody: "Retry me", + }), + ).toBe("new draft"); + }); +}); diff --git a/ui/src/lib/comment-submit-draft.ts b/ui/src/lib/comment-submit-draft.ts new file mode 100644 index 00000000..5d9b56cc --- /dev/null +++ b/ui/src/lib/comment-submit-draft.ts @@ -0,0 +1,6 @@ +export function restoreSubmittedCommentDraft(params: { + currentBody: string; + submittedBody: string; +}) { + return params.currentBody.trim() ? params.currentBody : params.submittedBody; +}