Files
Jyotisha/frontend/src/components/rectification-choice-card.tsx
T
Jesse_ChenandCursor 1ded6bb0d4 fix(rectification): hide option hover impact and composer step strip (BUG-575)
Hovering A/B/C/D rewrote a leading/lagging time line and made the card flicker; the step-state sentence above the composer was instructional filler, not something the user needed to answer.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-07 14:07:49 +08:00

96 lines
3.3 KiB
TypeScript

"use client";
import { useState } from "react";
import { InlineSpinner } from "@/components/inline-spinner";
import {
CHOICE_STOP_LABEL,
type ChoiceKey,
type RectificationChoiceCard as ChoiceCard,
} from "@/lib/rectification-agentic/v9/choice-card";
type RectificationChoiceCardProps = Readonly<{
card: ChoiceCard;
pending: boolean;
disabled: boolean;
selectedKey?: ChoiceKey | "stop" | "skip_probe" | "";
onSelect: (key: ChoiceKey) => void;
onStop: () => void;
variant?: "card" | "embedded";
hidePrompt?: boolean;
}>;
export function RectificationChoiceCard(props: RectificationChoiceCardProps) {
const [localSelected, setLocalSelected] = useState<ChoiceKey | "stop" | "skip_probe" | "">("");
const selectedKey = props.selectedKey || localSelected;
const answered = Boolean(selectedKey);
function select(key: ChoiceKey) {
if (props.pending || props.disabled || selectedKey) return;
setLocalSelected(key);
props.onSelect(key);
}
function stop() {
if (props.pending || props.disabled || selectedKey) return;
setLocalSelected("stop");
props.onStop();
}
const embedded = props.variant === "embedded";
const hidePrompt = embedded || props.hidePrompt === true;
return (
<section
className={`rectification-choice-card${embedded ? " is-embedded" : ""}${props.pending ? " is-pending" : ""}${answered ? " is-answered" : ""}`}
aria-label={props.card.scoring ? "校正判断" : "盘外核对"}
>
{props.pending && answered ? (
// The tap is being recorded: the same live-row shape as a timeline step,
// so the card itself says something is happening.
<p className="rectification-choice-card__pending" role="status">
<InlineSpinner size={12} />
<span className="agent-activity-status__text">正在记录…</span>
</p>
) : null}
<fieldset
className="birth-time-choice-question"
disabled={props.pending || props.disabled || answered}
data-answered={answered ? "true" : "false"}
>
{hidePrompt
? <legend className="sr-only">{props.card.prompt}</legend>
: <legend>{props.card.prompt}</legend>}
{!hidePrompt && props.card.why_user ? (
<details className="rectification-choice-why-user">
<summary>为什么问这题</summary>
<p>{props.card.why_user}</p>
</details>
) : null}
<div className="birth-time-primary-choices">
{props.card.options.map((option) => (
<button
key={option.key}
type="button"
className="birth-time-choice-option is-primary"
data-selected={selectedKey === option.key ? "true" : "false"}
onClick={() => select(option.key)}
>
<strong>{option.key}.</strong> {option.label}
</button>
))}
{props.card.stop_label !== CHOICE_STOP_LABEL ? (
<button
type="button"
className="birth-time-choice-option is-primary"
data-selected={selectedKey === "stop" || selectedKey === "skip_probe" ? "true" : "false"}
onClick={stop}
>
{props.card.stop_label}
</button>
) : null}
</div>
</fieldset>
</section>
);
}