mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-03 09:50:40 -07:00
Enable 'Other' option for yesno question type (#24545)
This commit is contained in:
@@ -1409,6 +1409,53 @@ describe('AskUserDialog', () => {
|
||||
expect(lastFrame()).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
it('supports "Other" option for yesno questions', async () => {
|
||||
const questions: Question[] = [
|
||||
{
|
||||
question: 'Is this correct?',
|
||||
header: 'Confirm',
|
||||
type: QuestionType.YESNO,
|
||||
},
|
||||
];
|
||||
|
||||
const onSubmit = vi.fn();
|
||||
const { stdin, lastFrame, waitUntilReady } = await renderWithProviders(
|
||||
<AskUserDialog
|
||||
questions={questions}
|
||||
onSubmit={onSubmit}
|
||||
onCancel={vi.fn()}
|
||||
width={80}
|
||||
/>,
|
||||
{ width: 80 },
|
||||
);
|
||||
|
||||
// Navigate to "Other" (3rd option: 1. Yes, 2. No, 3. Other)
|
||||
writeKey(stdin, '\x1b[B'); // Down to No
|
||||
writeKey(stdin, '\x1b[B'); // Down to Other
|
||||
|
||||
await waitFor(async () => {
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toContain('Enter a custom value');
|
||||
});
|
||||
|
||||
// Type feedback
|
||||
for (const char of 'Yes, but with caveats') {
|
||||
writeKey(stdin, char);
|
||||
}
|
||||
|
||||
await waitFor(async () => {
|
||||
await waitUntilReady();
|
||||
expect(lastFrame()).toContain('Yes, but with caveats');
|
||||
});
|
||||
|
||||
// Submit
|
||||
writeKey(stdin, '\r');
|
||||
|
||||
await waitFor(async () => {
|
||||
expect(onSubmit).toHaveBeenCalledWith({ '0': 'Yes, but with caveats' });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('expands paste placeholders in multi-select custom option via Done', async () => {
|
||||
|
||||
@@ -511,8 +511,9 @@ const ChoiceQuestionView: React.FC<ChoiceQuestionViewProps> = ({
|
||||
}) => {
|
||||
const keyMatchers = useKeyMatchers();
|
||||
const isAlternateBuffer = useAlternateBuffer();
|
||||
const numOptions =
|
||||
(question.options?.length ?? 0) + (question.type !== 'yesno' ? 1 : 0);
|
||||
const hasAll = question.multiSelect && (question.options?.length ?? 0) > 1;
|
||||
// Calculate total options including 'All' and 'Other' to ensure consistent numbering column width
|
||||
const numOptions = (question.options?.length ?? 0) + (hasAll ? 1 : 0) + 1;
|
||||
const numLen = String(numOptions).length;
|
||||
const radioWidth = 2; // "● "
|
||||
const numberWidth = numLen + 2; // e.g., "1. "
|
||||
@@ -735,17 +736,15 @@ const ChoiceQuestionView: React.FC<ChoiceQuestionViewProps> = ({
|
||||
list.push({ key: 'all', value: allItem });
|
||||
}
|
||||
|
||||
// Only add custom option for choice type, not yesno
|
||||
if (question.type !== 'yesno') {
|
||||
const otherItem: OptionItem = {
|
||||
key: 'other',
|
||||
label: customOptionText || '',
|
||||
description: '',
|
||||
type: 'other',
|
||||
index: list.length,
|
||||
};
|
||||
list.push({ key: 'other', value: otherItem });
|
||||
}
|
||||
// Add custom option for choice and yesno types
|
||||
const otherItem: OptionItem = {
|
||||
key: 'other',
|
||||
label: customOptionText || '',
|
||||
description: '',
|
||||
type: 'other',
|
||||
index: list.length,
|
||||
};
|
||||
list.push({ key: 'other', value: otherItem });
|
||||
|
||||
if (question.multiSelect) {
|
||||
const doneItem: OptionItem = {
|
||||
@@ -759,7 +758,7 @@ const ChoiceQuestionView: React.FC<ChoiceQuestionViewProps> = ({
|
||||
}
|
||||
|
||||
return list;
|
||||
}, [questionOptions, question.multiSelect, question.type, customOptionText]);
|
||||
}, [questionOptions, question.multiSelect, customOptionText]);
|
||||
|
||||
const handleHighlight = useCallback(
|
||||
(itemValue: OptionItem) => {
|
||||
|
||||
@@ -183,13 +183,13 @@ export enum QuestionType {
|
||||
export interface Question {
|
||||
question: string;
|
||||
header: string;
|
||||
/** Question type: 'choice' renders selectable options, 'text' renders free-form input, 'yesno' renders a binary Yes/No choice. */
|
||||
/** Question type: 'choice' renders selectable options, 'text' renders free-form input, 'yesno' renders a Yes/No choice with an optional 'Other' feedback field. */
|
||||
type: QuestionType;
|
||||
/** Selectable choices. REQUIRED when type='choice'. IGNORED for 'text' and 'yesno'. */
|
||||
options?: QuestionOption[];
|
||||
/** Allow multiple selections. Only applies when type='choice'. */
|
||||
multiSelect?: boolean;
|
||||
/** Placeholder hint text. For type='text', shown in the input field. For type='choice', shown in the "Other" custom input. */
|
||||
/** Placeholder hint text. For type='text', shown in the input field. For type='choice' and 'yesno', shown in the 'Other' custom input. */
|
||||
placeholder?: string;
|
||||
/** Allow the question to consume more vertical space instead of being strictly capped. */
|
||||
unconstrainedHeight?: boolean;
|
||||
|
||||
@@ -88,7 +88,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snaps
|
||||
"type": "boolean",
|
||||
},
|
||||
"options": {
|
||||
"description": "The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.",
|
||||
"description": "The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added for 'choice' and 'yesno' types. Not needed for 'text' or 'yesno'.",
|
||||
"items": {
|
||||
"properties": {
|
||||
"description": {
|
||||
@@ -109,7 +109,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snaps
|
||||
"type": "array",
|
||||
},
|
||||
"placeholder": {
|
||||
"description": "Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.",
|
||||
"description": "Hint text shown in the input field. For type='text', shown in the main input. For type='choice' and 'yesno', shown in the 'Other' custom input.",
|
||||
"type": "string",
|
||||
},
|
||||
"question": {
|
||||
@@ -118,7 +118,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-2.5-pro > snaps
|
||||
},
|
||||
"type": {
|
||||
"default": "choice",
|
||||
"description": "Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.",
|
||||
"description": "Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation with optional 'Other' feedback.",
|
||||
"enum": [
|
||||
"choice",
|
||||
"text",
|
||||
@@ -918,7 +918,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
|
||||
"type": "boolean",
|
||||
},
|
||||
"options": {
|
||||
"description": "The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.",
|
||||
"description": "The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added for 'choice' and 'yesno' types. Not needed for 'text' or 'yesno'.",
|
||||
"items": {
|
||||
"properties": {
|
||||
"description": {
|
||||
@@ -939,7 +939,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
|
||||
"type": "array",
|
||||
},
|
||||
"placeholder": {
|
||||
"description": "Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.",
|
||||
"description": "Hint text shown in the input field. For type='text', shown in the main input. For type='choice' and 'yesno', shown in the 'Other' custom input.",
|
||||
"type": "string",
|
||||
},
|
||||
"question": {
|
||||
@@ -948,7 +948,7 @@ exports[`coreTools snapshots for specific models > Model: gemini-3-pro-preview >
|
||||
},
|
||||
"type": {
|
||||
"default": "choice",
|
||||
"description": "Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.",
|
||||
"description": "Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation with optional 'Other' feedback.",
|
||||
"enum": [
|
||||
"choice",
|
||||
"text",
|
||||
|
||||
@@ -695,12 +695,12 @@ The agent did not use the todo list because this task could be completed by a ti
|
||||
enum: ['choice', 'text', 'yesno'],
|
||||
default: 'choice',
|
||||
description:
|
||||
"Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.",
|
||||
"Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation with optional 'Other' feedback.",
|
||||
},
|
||||
[ASK_USER_QUESTION_PARAM_OPTIONS]: {
|
||||
type: 'array',
|
||||
description:
|
||||
"The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.",
|
||||
"The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added for 'choice' and 'yesno' types. Not needed for 'text' or 'yesno'.",
|
||||
items: {
|
||||
type: 'object',
|
||||
required: [
|
||||
@@ -729,7 +729,7 @@ The agent did not use the todo list because this task could be completed by a ti
|
||||
[ASK_USER_QUESTION_PARAM_PLACEHOLDER]: {
|
||||
type: 'string',
|
||||
description:
|
||||
"Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.",
|
||||
"Hint text shown in the input field. For type='text', shown in the main input. For type='choice' and 'yesno', shown in the 'Other' custom input.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -671,12 +671,12 @@ The agent did not use the todo list because this task could be completed by a ti
|
||||
enum: ['choice', 'text', 'yesno'],
|
||||
default: 'choice',
|
||||
description:
|
||||
"Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.",
|
||||
"Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation with optional 'Other' feedback.",
|
||||
},
|
||||
[ASK_USER_QUESTION_PARAM_OPTIONS]: {
|
||||
type: 'array',
|
||||
description:
|
||||
"The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.",
|
||||
"The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added for 'choice' and 'yesno' types. Not needed for 'text' or 'yesno'.",
|
||||
items: {
|
||||
type: 'object',
|
||||
required: [
|
||||
@@ -705,7 +705,7 @@ The agent did not use the todo list because this task could be completed by a ti
|
||||
[ASK_USER_QUESTION_PARAM_PLACEHOLDER]: {
|
||||
type: 'string',
|
||||
description:
|
||||
"Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.",
|
||||
"Hint text shown in the input field. For type='text', shown in the main input. For type='choice' and 'yesno', shown in the 'Other' custom input.",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user