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
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,151 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
RECTIFICATION_DELIVERED_COPY,
|
||||
RECTIFICATION_QUESTION_PREPARING_LABEL,
|
||||
RECTIFICATION_QUESTION_RELOAD_LABEL,
|
||||
RECTIFICATION_QUESTION_REPAIR_FAILED_COPY,
|
||||
RECTIFICATION_QUESTION_REPAIR_LIMIT,
|
||||
RECTIFICATION_QUESTION_UNAVAILABLE_COPY,
|
||||
RECTIFICATION_SNAPSHOT_RETRY_LABEL,
|
||||
RECTIFICATION_SNAPSHOT_UNAVAILABLE_COPY,
|
||||
persistedQuestionSurface,
|
||||
rectificationReadonlyRangeCopy,
|
||||
type RectificationSnapshotFailure,
|
||||
} from "@/lib/rectification-surface-state";
|
||||
import type { ChoiceKey, RectificationChoiceCard as ChoiceCardModel } from "@/lib/rectification-agentic/v9/choice-card";
|
||||
import type { CurrentQuestionModel } from "@/lib/rectification-chat-snapshot";
|
||||
import type { RectificationChatView } from "@/lib/rectification-chat-view";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ConsultationTimelineLiveRow } from "./consultation-run-timeline";
|
||||
import { RectificationChoiceCard } from "./rectification-choice-card";
|
||||
|
||||
export function RectificationReadonlyRange({
|
||||
range,
|
||||
sessionOutcome,
|
||||
discriminatingEventProbes,
|
||||
contrastProbes,
|
||||
}: Readonly<{
|
||||
range: readonly [string, string];
|
||||
sessionOutcome: string | null;
|
||||
discriminatingEventProbes: unknown;
|
||||
contrastProbes: unknown;
|
||||
}>) {
|
||||
return (
|
||||
<p className="rectification-readonly-range" role="status">
|
||||
{rectificationReadonlyRangeCopy({
|
||||
range,
|
||||
sessionOutcome,
|
||||
discriminatingEventProbes,
|
||||
contrastProbes,
|
||||
})}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The blocks under the transcript that stand in for a missing next question:
|
||||
* the standalone persisted question (only when no assistant message can carry
|
||||
* it), the preparing row, the delivered note, the snapshot-failure retry, the
|
||||
* repair button, and the verified-idle note. Moved out of
|
||||
* RectificationAgenticChat unchanged (TASK-rectification-code-split-20260926).
|
||||
*/
|
||||
export function RectificationQuestionGapNotices({
|
||||
view,
|
||||
currentQuestion,
|
||||
choiceCard,
|
||||
choiceNonce,
|
||||
busy,
|
||||
readonly,
|
||||
snapshotFailure,
|
||||
questionRepairAttempts,
|
||||
questionRepairing,
|
||||
submitChoice,
|
||||
submitStop,
|
||||
refetchQuestion,
|
||||
repairQuestion,
|
||||
}: Readonly<{
|
||||
view: Pick<RectificationChatView, "showStandaloneQuestion" | "questionGap" | "showSelectionCards" | "verifiedIdleCopy">;
|
||||
currentQuestion: CurrentQuestionModel | null;
|
||||
choiceCard: ChoiceCardModel | null;
|
||||
choiceNonce: number;
|
||||
busy: boolean;
|
||||
readonly: boolean;
|
||||
snapshotFailure: RectificationSnapshotFailure | null;
|
||||
questionRepairAttempts: number;
|
||||
questionRepairing: boolean;
|
||||
submitChoice: (key: ChoiceKey) => void;
|
||||
submitStop: () => void;
|
||||
refetchQuestion: () => Promise<void>;
|
||||
repairQuestion: () => Promise<void>;
|
||||
}>) {
|
||||
const { showStandaloneQuestion, questionGap, showSelectionCards, verifiedIdleCopy } = view;
|
||||
return (
|
||||
<>
|
||||
{showStandaloneQuestion && currentQuestion?.prompt && (
|
||||
<div className="rectification-message-wrap rectification-message-entry" data-testid="persisted-question">
|
||||
<div className="rectification-message-question is-standalone">
|
||||
{persistedQuestionSurface({
|
||||
questionKind: currentQuestion.kind,
|
||||
hasChoiceCard: Boolean(choiceCard),
|
||||
questionPersisted: true,
|
||||
}) === "choice_card" && choiceCard ? (
|
||||
<>
|
||||
<p className="rectification-message-question__prompt">{currentQuestion.prompt}</p>
|
||||
<RectificationChoiceCard
|
||||
key={`${choiceCard.question_id}:${choiceNonce}:persisted`}
|
||||
variant="embedded"
|
||||
card={choiceCard}
|
||||
pending={busy}
|
||||
disabled={busy || readonly}
|
||||
selectedKey=""
|
||||
onSelect={submitChoice}
|
||||
onStop={submitStop}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<p className="rectification-message-question__prompt">{currentQuestion.prompt}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{questionGap === "preparing" && (
|
||||
<div className="rectification-message-wrap rectification-message-entry rectification-question-gap">
|
||||
<ConsultationTimelineLiveRow id="question-preparing" label={RECTIFICATION_QUESTION_PREPARING_LABEL} />
|
||||
</div>
|
||||
)}
|
||||
{questionGap === "delivered" && !showSelectionCards && (
|
||||
<p className="rectification-pending-note" role="status">
|
||||
{RECTIFICATION_DELIVERED_COPY}
|
||||
</p>
|
||||
)}
|
||||
{snapshotFailure && (
|
||||
<div className="rectification-message-wrap rectification-message-entry rectification-question-gap" role="status">
|
||||
<p className="rectification-question-gap__copy">{RECTIFICATION_SNAPSHOT_UNAVAILABLE_COPY}</p>
|
||||
<Button type="button" variant="outline" onClick={() => void refetchQuestion()}>
|
||||
{RECTIFICATION_SNAPSHOT_RETRY_LABEL}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{questionGap === "unavailable" && !snapshotFailure && (
|
||||
<div className="rectification-message-wrap rectification-message-entry rectification-question-gap" role="status">
|
||||
{questionRepairAttempts >= RECTIFICATION_QUESTION_REPAIR_LIMIT ? (
|
||||
<p className="rectification-question-gap__copy">{RECTIFICATION_QUESTION_REPAIR_FAILED_COPY}</p>
|
||||
) : (
|
||||
<>
|
||||
<p className="rectification-question-gap__copy">{RECTIFICATION_QUESTION_UNAVAILABLE_COPY}</p>
|
||||
<Button type="button" variant="outline" onClick={() => void repairQuestion()} disabled={questionRepairing}>
|
||||
{RECTIFICATION_QUESTION_RELOAD_LABEL}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
{questionGap === "verified_idle" && verifiedIdleCopy && !showSelectionCards && (
|
||||
<p className="rectification-pending-note" role="status">
|
||||
{verifiedIdleCopy}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user