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
67 lines
2.3 KiB
TypeScript
67 lines
2.3 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useEffect, useRef, type Dispatch, type SetStateAction } from "react";
|
|
import { rectificationLiveProgressLabel } from "@/lib/rectification-activity-labels";
|
|
import type { RenderMessage } from "@/components/rectification-message-entry";
|
|
|
|
/**
|
|
* The live working row's label clock: when the run and the current step
|
|
* started, which tool is running, and the 4-second tick that refreshes the
|
|
* elapsed-time label while a turn is busy. Moved out of
|
|
* RectificationAgenticChat unchanged (TASK-rectification-code-split-20260926).
|
|
*/
|
|
export function useRectificationLiveActivity({
|
|
busy,
|
|
setMessages,
|
|
}: Readonly<{
|
|
busy: boolean;
|
|
setMessages: Dispatch<SetStateAction<RenderMessage[]>>;
|
|
}>) {
|
|
const runStartedAtRef = useRef(0);
|
|
const stepStartedAtRef = useRef(0);
|
|
const liveToolRef = useRef<string | null>(null);
|
|
const liveBaseLabelRef = useRef("正在处理…");
|
|
|
|
const beginLiveRun = useCallback((baseLabel: string) => {
|
|
const now = Date.now();
|
|
runStartedAtRef.current = now;
|
|
stepStartedAtRef.current = now;
|
|
liveToolRef.current = null;
|
|
liveBaseLabelRef.current = baseLabel;
|
|
}, []);
|
|
|
|
const rememberLiveActivity = useCallback((baseLabel: string, tool: string | null = liveToolRef.current) => {
|
|
if (liveBaseLabelRef.current !== baseLabel) {
|
|
liveBaseLabelRef.current = baseLabel;
|
|
stepStartedAtRef.current = Date.now();
|
|
}
|
|
liveToolRef.current = tool;
|
|
return rectificationLiveProgressLabel({
|
|
tool,
|
|
baseLabel,
|
|
stepStartedAt: stepStartedAtRef.current,
|
|
runStartedAt: runStartedAtRef.current,
|
|
});
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (!busy) return;
|
|
const id = window.setInterval(() => {
|
|
const label = rectificationLiveProgressLabel({
|
|
tool: liveToolRef.current,
|
|
baseLabel: liveBaseLabelRef.current,
|
|
stepStartedAt: stepStartedAtRef.current,
|
|
runStartedAt: runStartedAtRef.current,
|
|
});
|
|
setMessages((current) => current.map((message) => {
|
|
if (message.state !== "thinking" && message.state !== "streaming") return message;
|
|
if (!message.activity || message.activity.label === label) return message;
|
|
return { ...message, activity: { ...message.activity, label } };
|
|
}));
|
|
}, 4000);
|
|
return () => window.clearInterval(id);
|
|
}, [busy]);
|
|
|
|
return { beginLiveRun, rememberLiveActivity };
|
|
}
|