fix(vscode): multi-select question jumps to next after only one answe… (#3079)

* fix(vscode): multi-select question jumps to next after only one answer selected

* chore: add changeset

---------

Co-authored-by: gaoyuan <gaoyuan@moonshot.ai>
This commit is contained in:
Rick 2026-08-19 16:27:07 +08:00 committed by GitHub
parent be8e017597
commit 35befdcef2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 14 deletions

View file

@ -0,0 +1,5 @@
---
"kimi-code": patch
---
multi-select question jumps to next after only one answer selected

View file

@ -9,9 +9,12 @@ export function QuestionDialog() {
const [selectedIndex, setSelectedIndex] = useState(1);
const [questionIndex, setQuestionIndex] = useState(0);
const [answers, setAnswers] = useState<Record<string, string>>({});
const [multiSelected, setMultiSelected] = useState<string[]>([]);
const questions = pendingQuestion?.questions ?? [];
const question = questions[questionIndex];
const isMultiSelect = question?.multi_select === true;
const isLastQuestion = questionIndex + 1 >= questions.length;
useEffect(() => {
if (pendingQuestion) {
@ -20,6 +23,7 @@ export function QuestionDialog() {
setSelectedIndex(1);
setQuestionIndex(0);
setAnswers({});
setMultiSelected([]);
}
}, [pendingQuestion?.id]);
@ -28,28 +32,43 @@ export function QuestionDialog() {
// Step through the questions one by one; submit all answers after the last.
const handleAnswer = async (answer: string) => {
const nextAnswers = { ...answers, [question.question]: answer };
if (questionIndex + 1 < questions.length) {
if (!isLastQuestion) {
setAnswers(nextAnswers);
setQuestionIndex(questionIndex + 1);
setShowCustom(false);
setCustomInput("");
setSelectedIndex(1);
setMultiSelected([]);
} else {
await respondQuestion(nextAnswers);
}
};
const handleSelect = async (optionLabel: string) => {
if (isMultiSelect) {
setMultiSelected((prev) =>
prev.includes(optionLabel) ? prev.filter((value) => value !== optionLabel) : [...prev, optionLabel],
);
return;
}
await handleAnswer(optionLabel);
};
const handleCustomSubmit = async () => {
if (!customInput.trim()) return;
await handleAnswer(customInput.trim());
const value = customInput.trim();
if (!value) return;
if (isMultiSelect) {
setMultiSelected((prev) => (prev.includes(value) ? prev : [...prev, value]));
setCustomInput("");
setShowCustom(false);
return;
}
await handleAnswer(value);
};
const options = question.options || [];
const customIndex = options.length + 1;
const customValues = multiSelected.filter((value) => !options.some((option) => option.label === value));
return (
<div className={cn("mb-0.5 border border-blue-200 dark:border-blue-800 rounded-lg overflow-hidden bg-background flex flex-col shrink")}>
@ -61,25 +80,51 @@ export function QuestionDialog() {
)}
{question.header && <div className="text-[10px] text-muted-foreground uppercase tracking-wide">{question.header}</div>}
<div className="text-xs font-semibold text-foreground">{question.question}</div>
{isMultiSelect && <div className="text-[10px] text-muted-foreground">Select all that apply</div>}
<div className="space-y-1.5">
{options.map((option, idx) => (
{options.map((option, idx) => {
const isChecked = isMultiSelect && multiSelected.includes(option.label);
const isHighlighted = selectedIndex === idx + 1;
return (
<button
key={idx}
onClick={() => {
void handleSelect(option.label);
}}
onMouseEnter={() => setSelectedIndex(idx + 1)}
className={cn(
"w-full text-left px-2 py-1 rounded-md text-xs transition-colors",
"border cursor-pointer",
isChecked
? "bg-blue-500/15 border-blue-500"
: isHighlighted
? "bg-blue-500 text-white border-blue-500"
: "bg-background border-border hover:bg-muted/50",
)}
>
<span className={cn("mr-2", isHighlighted && !isChecked ? "text-blue-200" : "text-muted-foreground")}>
{isChecked ? "✓" : idx + 1}
</span>
<span className="font-medium">{option.label}</span>
{option.description && (
<span className={cn("ml-2", isHighlighted && !isChecked ? "text-blue-200" : "text-muted-foreground")}>- {option.description}</span>
)}
</button>
);
})}
{customValues.map((value) => (
<button
key={idx}
key={value}
onClick={() => {
void handleSelect(option.label);
void handleSelect(value);
}}
onMouseEnter={() => setSelectedIndex(idx + 1)}
className={cn(
"w-full text-left px-2 py-1 rounded-md text-xs transition-colors",
"border border-border cursor-pointer",
selectedIndex === idx + 1 ? "bg-blue-500 text-white border-blue-500" : "bg-background hover:bg-muted/50",
"border cursor-pointer bg-blue-500/15 border-blue-500",
)}
>
<span className={cn("mr-2", selectedIndex === idx + 1 ? "text-blue-200" : "text-muted-foreground")}>{idx + 1}</span>
<span className="font-medium">{option.label}</span>
{option.description && (
<span className={cn("ml-2", selectedIndex === idx + 1 ? "text-blue-200" : "text-muted-foreground")}>- {option.description}</span>
)}
<span className="mr-2 text-muted-foreground"></span>
<span className="font-medium">{value}</span>
</button>
))}
{showCustom ? (
@ -119,6 +164,17 @@ export function QuestionDialog() {
<span className="font-medium">Custom response...</span>
</button>
)}
{isMultiSelect && (
<button
onClick={() => {
void handleAnswer(multiSelected.join(", "));
}}
disabled={multiSelected.length === 0}
className="w-full px-2 py-1 rounded-md text-xs bg-blue-500 text-white disabled:opacity-50 cursor-pointer"
>
{isLastQuestion ? "Submit" : "Next"}
</button>
)}
</div>
</div>
</div>