refactor(rectification): split the chat component into hooks, parameter functions and blocks
Move-only (TASK-rectification-code-split-20260926 T1/T4). No behavior, API,
copy, DB, Skill or scoring change.
- rectification-agentic-chat.tsx: 2043 -> 737 lines; component body
1625 -> 630; useState 35 -> 12, useRef 16 -> 9, effects 9 -> 5,
useCallback 12 -> 6.
- Snapshot sync -> hooks/use-rectification-case-snapshot.ts; board layout
and live label clock -> two small hooks.
- send / submitStructuredChoice / acceptCandidate bodies ->
lib/rectification-chat-{turn,choice,accept}-run.ts parameter functions
(bodies verbatim, deps destructured to the same names); copy/regenerate,
question repair, pure transcript and snapshot helpers and the per-render
view derivation -> lib/rectification-chat-*.ts (no React hooks).
- Question-gap blocks and the read-only range line ->
components/rectification-question-gap-notices.tsx.
- Dependency arrays kept exactly as before (lint warnings +3, listed in
PROGRESS); no dep added to appease the linter.
- Tests: whole-source contracts read tests/rectification-chat-surface.ts
(container + split files, like home-surface.ts); slices of moved code now
call the extracted functions or render the extracted block, each with a
原值/新值/原因 comment. New tests/rectification-growth-contract.test.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
12cbe6f868
commit
3a66c39f48
@@ -14,13 +14,13 @@ import {
|
||||
} from "../src/lib/queued-draft.ts";
|
||||
import { chatReplyAnnouncer } from "../src/lib/chat-reply-announcement.ts";
|
||||
import { RECTIFICATION_STOPPED_NOTICE } from "../src/lib/rectification-surface-state.ts";
|
||||
import { rectificationChatSurface } from "./rectification-chat-surface.ts";
|
||||
import { abortWiringOf } from "./rectification-chat-run-fixtures.ts";
|
||||
|
||||
const page = readFileSync(new URL("../src/app/(app)/page.tsx", import.meta.url), "utf8");
|
||||
const composer = readFileSync(new URL("../src/components/chat-composer.tsx", import.meta.url), "utf8");
|
||||
const rectification = readFileSync(
|
||||
new URL("../src/components/rectification-agentic-chat.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
// 原值: 读 rectification-agentic-chat.tsx 单文件。新值: rectificationChatSurface(容器 + 拆出的 hook / 参数函数 / 块,见 rectification-chat-surface.ts)。原因: TASK-rectification-code-split-20260926 只搬不改,整文件断言跟着代码走;切片断言另行改为调用函数。
|
||||
const rectification = rectificationChatSurface;
|
||||
const consultationRun = readFileSync(
|
||||
new URL("../src/hooks/use-consultation-run.ts", import.meta.url),
|
||||
"utf8",
|
||||
@@ -136,7 +136,7 @@ test("a queued card renders above the live textarea", () => {
|
||||
assert.match(html, /还想再问一句/);
|
||||
});
|
||||
|
||||
test("rectification abort settles as stopped, not a failed alert", () => {
|
||||
test("rectification abort settles as stopped, not a failed alert", async () => {
|
||||
assert.match(rectification, /stopped: true/);
|
||||
assert.match(rectification, /failed: false,\s*stopped: true/);
|
||||
assert.doesNotMatch(rectification, /if \(raw\.trim\(\)\) setError\(RECTIFICATION_STOPPED_NOTICE\)/);
|
||||
@@ -149,18 +149,18 @@ test("rectification abort settles as stopped, not a failed alert", () => {
|
||||
assert.match(messageRow, /className="message-stopped-notice"/);
|
||||
assert.equal(RECTIFICATION_STOPPED_NOTICE, "已停止,已生成的内容保留;本次不会扣点。");
|
||||
assert.match(rectification, /signal: abortController\.signal/);
|
||||
const choiceFetch = rectification.slice(
|
||||
rectification.indexOf("const submitStructuredChoice"),
|
||||
rectification.indexOf("const acceptCandidate"),
|
||||
);
|
||||
assert.match(choiceFetch, /runAbort\.current = abortController/);
|
||||
assert.match(choiceFetch, /signal: abortController\.signal/);
|
||||
const adoptFetch = rectification.slice(
|
||||
rectification.indexOf("const acceptCandidate"),
|
||||
rectification.indexOf("async function copyMessage"),
|
||||
);
|
||||
assert.match(adoptFetch, /runAbort\.current = abortController/);
|
||||
assert.match(adoptFetch, /signal: abortController\.signal/);
|
||||
// 原值: 从 chat 源码切 submitStructuredChoice / acceptCandidate 两段,各匹配 `runAbort.current = abortController` 与 `signal: abortController.signal`。
|
||||
// 新值: 调用 runRectificationStructuredChoice / runRectificationCandidateAccept(假 fetch):请求带的 signal
|
||||
// 就是 runAbort 里那个控制器的(停止按钮能掐断它),结束后 runAbort 清空。
|
||||
// 原因: TASK-rectification-code-split-20260926 两段函数体原样搬进参数函数;行为断言覆盖接线本身。
|
||||
const choiceWiring = await abortWiringOf("choice");
|
||||
assert.equal(choiceWiring.url, "/api/rectification/agent");
|
||||
assert.equal(choiceWiring.signalMatchesRunAbort, true);
|
||||
assert.equal(choiceWiring.runAbortCleared, true);
|
||||
const adoptWiring = await abortWiringOf("accept");
|
||||
assert.match(adoptWiring.url, /\/api\/rectification\/cases\/[^/]+\/candidates\/accept$/);
|
||||
assert.equal(adoptWiring.signalMatchesRunAbort, true);
|
||||
assert.equal(adoptWiring.runAbortCleared, true);
|
||||
assert.equal(chatReplyAnnouncer("stopped"), "chat_notice_toast");
|
||||
assert.doesNotMatch(
|
||||
rectification.slice(rectification.indexOf("{error &&"), rectification.indexOf("{readonly &&")),
|
||||
|
||||
Reference in New Issue
Block a user