圆桌 Step1:支持多澄清实时展示与合并提交
流式解析 ask_clarification 逐题渲染澄清卡;草稿恢复 selectedAgents 修复参与角色;Step3 隐藏左侧栏让报告区全宽。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
838cae0d2d
commit
23d6f9efc2
9 files changed
+376
-136
No files matched your search
@@ -12,6 +12,20 @@ export type IntentClarificationSelection = {
|
||||
custom: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Composite selection-map key for one clarification question. When the intent
|
||||
* agent asks several questions in one turn, each needs its own selection state,
|
||||
* so we key by `${msgId}::${clarificationId}` (falling back to the arrival
|
||||
* index when the payload has no id). The map itself lives in `useStep1Intent`.
|
||||
*/
|
||||
export function clarificationKey(
|
||||
msgId: number,
|
||||
clarId: string | undefined,
|
||||
index: number,
|
||||
): string {
|
||||
return `${msgId}::${clarId ?? `idx-${index}`}`;
|
||||
}
|
||||
|
||||
type ClarificationOption = {
|
||||
id: string;
|
||||
label: string;
|
||||
@@ -72,6 +86,13 @@ interface IntentClarificationCardProps {
|
||||
submitting?: boolean;
|
||||
/** Multi-select: submit current selection. Single-select: also used for custom text. */
|
||||
onSubmit: (overrideSelection?: IntentClarificationSelection) => void;
|
||||
/**
|
||||
* Defer submission: even single-select option clicks only *record* the
|
||||
* selection (via onSelectionChange) instead of sending immediately, and the
|
||||
* card hides its own submit button. Used when several questions are answered
|
||||
* together and the parent owns one combined submit button.
|
||||
*/
|
||||
deferSubmit?: boolean;
|
||||
}
|
||||
|
||||
export function IntentClarificationCard({
|
||||
@@ -82,6 +103,7 @@ export function IntentClarificationCard({
|
||||
disabled = false,
|
||||
submitting = false,
|
||||
onSubmit,
|
||||
deferSubmit = false,
|
||||
}: IntentClarificationCardProps) {
|
||||
const options = useMemo(
|
||||
() => normalizeOptions(Array.isArray(payload.options) ? payload.options : []),
|
||||
@@ -97,11 +119,18 @@ export function IntentClarificationCard({
|
||||
return selection.selectedIds.length > 0 || selection.custom.trim().length > 0;
|
||||
}, [selection.custom, selection.selectedIds]);
|
||||
|
||||
const showSubmitButton = allowMultiple || payload.allowCustom !== false;
|
||||
// In defer mode the parent owns the (combined) submit button.
|
||||
const showSubmitButton = !deferSubmit && (allowMultiple || payload.allowCustom !== false);
|
||||
|
||||
function handleOptionClick(optionId: string) {
|
||||
if (disabled || submitting) return;
|
||||
if (!allowMultiple) {
|
||||
if (deferSubmit) {
|
||||
// Single-select but combined submit: just record the choice (replace),
|
||||
// keep any custom text the user already typed.
|
||||
onSelectionChange({ ...selection, selectedIds: [optionId] });
|
||||
return;
|
||||
}
|
||||
onSubmit({ selectedIds: [optionId], custom: "" });
|
||||
return;
|
||||
}
|
||||
@@ -144,6 +173,10 @@ export function IntentClarificationCard({
|
||||
</div>
|
||||
{options.map((option) => {
|
||||
const isSelected = selection.selectedIds.includes(option.id);
|
||||
// Highlight selected options whenever the choice is "sticky" — i.e.
|
||||
// multi-select, or single-select in defer mode (where a click records
|
||||
// rather than immediately sends).
|
||||
const showSelected = isSelected && (allowMultiple || deferSubmit);
|
||||
return (
|
||||
<button
|
||||
key={option.id}
|
||||
@@ -152,10 +185,10 @@ export function IntentClarificationCard({
|
||||
onClick={() => handleOptionClick(option.id)}
|
||||
className={cn(
|
||||
"flex w-full items-start justify-between gap-2 rounded-xl border px-3 py-2 text-left transition",
|
||||
isSelected && allowMultiple
|
||||
showSelected
|
||||
? "border-primary bg-primary text-white shadow-sm"
|
||||
: "border-slate-200 bg-slate-50/80 text-slate-800 hover:border-primary/40 hover:bg-white",
|
||||
!allowMultiple && !disabled && !submitting && "active:scale-[0.99]",
|
||||
!allowMultiple && !deferSubmit && !disabled && !submitting && "active:scale-[0.99]",
|
||||
(disabled || submitting) && "cursor-not-allowed opacity-60",
|
||||
)}
|
||||
>
|
||||
@@ -165,14 +198,14 @@ export function IntentClarificationCard({
|
||||
<span
|
||||
className={cn(
|
||||
"text-[10px] leading-snug",
|
||||
isSelected && allowMultiple ? "text-white/80" : "text-slate-500",
|
||||
showSelected ? "text-white/80" : "text-slate-500",
|
||||
)}
|
||||
>
|
||||
{option.description}
|
||||
</span>
|
||||
) : null}
|
||||
</span>
|
||||
{isSelected && allowMultiple ? (
|
||||
{showSelected ? (
|
||||
<Check className="mt-0.5 h-3.5 w-3.5 shrink-0" />
|
||||
) : null}
|
||||
</button>
|
||||
|
||||
Reference in new issue
Block a user