From 92b76c96051228de93c8e7830db3dcba3b17e9c0 Mon Sep 17 00:00:00 2001 From: Nathan Shan Date: Fri, 23 Oct 2026 15:46:56 +0800 Subject: [PATCH 1/6] =?UTF-8?q?fix(web):=20=E4=BF=9D=E7=95=99=E6=9C=AA?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E7=9A=84=E6=8E=92=E9=98=9F=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将编辑原文和附件移除记录随草稿持久化,返回线程时恢复已有编辑,避免原文覆盖草稿。 保存、取消及队列退出时清理编辑状态,保留线程独立草稿;通过 76 个相关测试、范围检查及隔离客户端前后验证。 --- .../src/components/chat/queuedMessageEdit.ts | 1 + .../chat/useQueuedRunEdit.browser.test.tsx | 124 ++++++++++++++++++ .../src/components/chat/useQueuedRunEdit.ts | 27 +++- apps/web/src/composerDraftStore.ts | 49 +++++++ 4 files changed, 196 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/components/chat/useQueuedRunEdit.browser.test.tsx diff --git a/apps/web/src/components/chat/queuedMessageEdit.ts b/apps/web/src/components/chat/queuedMessageEdit.ts index 238534d91de47..193643cb1a58b 100644 --- a/apps/web/src/components/chat/queuedMessageEdit.ts +++ b/apps/web/src/components/chat/queuedMessageEdit.ts @@ -19,6 +19,7 @@ export function recoverQueuedMessageEdit(input: { (draft.prompt !== input.originalText || draft.images.length > 0 || draft.files.length > 0); if (dirty && !composerDraftHasUserContent(store.getComposerDraft(input.threadTarget))) { store.moveComposerPromptAndImages(input.editTarget, input.threadTarget); + store.setQueuedMessageEdit(input.editTarget, undefined); return "kept"; } store.clearComposerContent(input.editTarget); diff --git a/apps/web/src/components/chat/useQueuedRunEdit.browser.test.tsx b/apps/web/src/components/chat/useQueuedRunEdit.browser.test.tsx new file mode 100644 index 0000000000000..1adfddd23d348 --- /dev/null +++ b/apps/web/src/components/chat/useQueuedRunEdit.browser.test.tsx @@ -0,0 +1,124 @@ +import { EnvironmentId, MessageId, RunId, ThreadId } from "@t3tools/contracts"; +import { scopeThreadRef } from "@t3tools/client-runtime/environment"; +import { useRef } from "react"; +import { expect, it, vi } from "vite-plus/test"; +import { render } from "../../../test/render"; +import { + partializeComposerDraftStoreState, + useComposerDraftStore, + useComposerThreadDraft, +} from "../../composerDraftStore"; +import { useQueuedRunEdit } from "./useQueuedRunEdit"; + +const commands = vi.hoisted(() => ({ + save: vi.fn(async (_input: unknown) => ({ _tag: "Success" })), +})); +vi.mock(import("../../state/use-atom-command"), () => ({ + useAtomCommand: (() => commands.save) as never, +})); +vi.mock(import("../../assets/assetUrls"), () => ({ useAssetUrls: () => [] })); + +function Editor() { + const environmentId = EnvironmentId.make("environment:queue-edit"); + const threadId = ThreadId.make("thread:queue-edit"); + const threadRef = scopeThreadRef(environmentId, threadId); + const promptRef = useRef(""); + const edit = useQueuedRunEdit({ + environmentId, + routeThreadRef: threadRef, + threadDraftTarget: threadRef, + activeThreadId: threadId, + serverProjection: null, + promptRef, + composerRef: { current: null }, + }); + const draft = useComposerThreadDraft(edit.draftTarget); + return ( + <> + +