mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-24 22:55:13 +00:00
Fix truncation for AskQuestion (#18001)
This commit is contained in:
@@ -10,6 +10,7 @@ import { renderWithProviders } from '../../test-utils/render.js';
|
|||||||
import { waitFor } from '../../test-utils/async.js';
|
import { waitFor } from '../../test-utils/async.js';
|
||||||
import { AskUserDialog } from './AskUserDialog.js';
|
import { AskUserDialog } from './AskUserDialog.js';
|
||||||
import { QuestionType, type Question } from '@google/gemini-cli-core';
|
import { QuestionType, type Question } from '@google/gemini-cli-core';
|
||||||
|
import { UIStateContext, type UIState } from '../contexts/UIStateContext.js';
|
||||||
|
|
||||||
// Helper to write to stdin with proper act() wrapping
|
// Helper to write to stdin with proper act() wrapping
|
||||||
const writeKey = (stdin: { write: (data: string) => void }, key: string) => {
|
const writeKey = (stdin: { write: (data: string) => void }, key: string) => {
|
||||||
@@ -42,7 +43,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -108,7 +108,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -129,7 +128,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -159,33 +157,49 @@ describe('AskUserDialog', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows scroll arrows when options exceed available height', async () => {
|
describe.each([
|
||||||
const questions: Question[] = [
|
{ useAlternateBuffer: true, expectedArrows: false },
|
||||||
{
|
{ useAlternateBuffer: false, expectedArrows: true },
|
||||||
question: 'Choose an option',
|
])(
|
||||||
header: 'Scroll Test',
|
'Scroll Arrows (useAlternateBuffer: $useAlternateBuffer)',
|
||||||
options: Array.from({ length: 15 }, (_, i) => ({
|
({ useAlternateBuffer, expectedArrows }) => {
|
||||||
label: `Option ${i + 1}`,
|
it(`shows scroll arrows correctly when useAlternateBuffer is ${useAlternateBuffer}`, async () => {
|
||||||
description: `Description ${i + 1}`,
|
const questions: Question[] = [
|
||||||
})),
|
{
|
||||||
multiSelect: false,
|
question: 'Choose an option',
|
||||||
},
|
header: 'Scroll Test',
|
||||||
];
|
options: Array.from({ length: 15 }, (_, i) => ({
|
||||||
|
label: `Option ${i + 1}`,
|
||||||
|
description: `Description ${i + 1}`,
|
||||||
|
})),
|
||||||
|
multiSelect: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const { lastFrame } = renderWithProviders(
|
const { lastFrame } = renderWithProviders(
|
||||||
<AskUserDialog
|
<AskUserDialog
|
||||||
questions={questions}
|
questions={questions}
|
||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={80}
|
width={80}
|
||||||
availableHeight={10} // Small height to force scrolling
|
availableHeight={10} // Small height to force scrolling
|
||||||
/>,
|
/>,
|
||||||
);
|
{ useAlternateBuffer },
|
||||||
|
);
|
||||||
|
|
||||||
await waitFor(() => {
|
await waitFor(() => {
|
||||||
expect(lastFrame()).toMatchSnapshot();
|
if (expectedArrows) {
|
||||||
});
|
expect(lastFrame()).toContain('▲');
|
||||||
});
|
expect(lastFrame()).toContain('▼');
|
||||||
|
} else {
|
||||||
|
expect(lastFrame()).not.toContain('▲');
|
||||||
|
expect(lastFrame()).not.toContain('▼');
|
||||||
|
}
|
||||||
|
expect(lastFrame()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it('navigates to custom option when typing unbound characters (Type-to-Jump)', async () => {
|
it('navigates to custom option when typing unbound characters (Type-to-Jump)', async () => {
|
||||||
const { stdin, lastFrame } = renderWithProviders(
|
const { stdin, lastFrame } = renderWithProviders(
|
||||||
@@ -194,7 +208,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -246,7 +259,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -261,7 +273,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -276,7 +287,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -308,7 +318,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -351,7 +360,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -420,7 +428,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -450,7 +457,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -496,7 +502,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -533,7 +538,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -567,7 +571,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -590,7 +593,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -613,7 +615,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -649,7 +650,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -681,7 +681,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -729,7 +728,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -780,7 +778,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -807,7 +804,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={onCancel}
|
onCancel={onCancel}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -854,7 +850,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={vi.fn()}
|
onSubmit={vi.fn()}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -914,7 +909,6 @@ describe('AskUserDialog', () => {
|
|||||||
onSubmit={onSubmit}
|
onSubmit={onSubmit}
|
||||||
onCancel={vi.fn()}
|
onCancel={vi.fn()}
|
||||||
width={120}
|
width={120}
|
||||||
availableHeight={20}
|
|
||||||
/>,
|
/>,
|
||||||
{ width: 120 },
|
{ width: 120 },
|
||||||
);
|
);
|
||||||
@@ -946,4 +940,72 @@ describe('AskUserDialog', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('uses availableTerminalHeight from UIStateContext if availableHeight prop is missing', () => {
|
||||||
|
const questions: Question[] = [
|
||||||
|
{
|
||||||
|
question: 'Choose an option',
|
||||||
|
header: 'Context Test',
|
||||||
|
options: Array.from({ length: 10 }, (_, i) => ({
|
||||||
|
label: `Option ${i + 1}`,
|
||||||
|
description: `Description ${i + 1}`,
|
||||||
|
})),
|
||||||
|
multiSelect: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockUIState = {
|
||||||
|
availableTerminalHeight: 5, // Small height to force scroll arrows
|
||||||
|
} as UIState;
|
||||||
|
|
||||||
|
const { lastFrame } = renderWithProviders(
|
||||||
|
<UIStateContext.Provider value={mockUIState}>
|
||||||
|
<AskUserDialog
|
||||||
|
questions={questions}
|
||||||
|
onSubmit={vi.fn()}
|
||||||
|
onCancel={vi.fn()}
|
||||||
|
width={80}
|
||||||
|
/>
|
||||||
|
</UIStateContext.Provider>,
|
||||||
|
{ useAlternateBuffer: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
// With height 5 and alternate buffer disabled, it should show scroll arrows (▲)
|
||||||
|
expect(lastFrame()).toContain('▲');
|
||||||
|
expect(lastFrame()).toContain('▼');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does NOT truncate the question when in alternate buffer mode even with small height', () => {
|
||||||
|
const longQuestion =
|
||||||
|
'This is a very long question ' + 'with many words '.repeat(10);
|
||||||
|
const questions: Question[] = [
|
||||||
|
{
|
||||||
|
question: longQuestion,
|
||||||
|
header: 'Alternate Buffer Test',
|
||||||
|
options: [{ label: 'Option 1', description: 'Desc 1' }],
|
||||||
|
multiSelect: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const mockUIState = {
|
||||||
|
availableTerminalHeight: 5,
|
||||||
|
} as UIState;
|
||||||
|
|
||||||
|
const { lastFrame } = renderWithProviders(
|
||||||
|
<UIStateContext.Provider value={mockUIState}>
|
||||||
|
<AskUserDialog
|
||||||
|
questions={questions}
|
||||||
|
onSubmit={vi.fn()}
|
||||||
|
onCancel={vi.fn()}
|
||||||
|
width={40} // Small width to force wrapping
|
||||||
|
/>
|
||||||
|
</UIStateContext.Provider>,
|
||||||
|
{ useAlternateBuffer: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should NOT contain the truncation message
|
||||||
|
expect(lastFrame()).not.toContain('hidden ...');
|
||||||
|
// Should contain the full long question (or at least its parts)
|
||||||
|
expect(lastFrame()).toContain('This is a very long question');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,7 +5,14 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { useCallback, useMemo, useRef, useEffect, useReducer } from 'react';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useEffect,
|
||||||
|
useReducer,
|
||||||
|
useContext,
|
||||||
|
} from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { theme } from '../semantic-colors.js';
|
||||||
import type { Question } from '@google/gemini-cli-core';
|
import type { Question } from '@google/gemini-cli-core';
|
||||||
@@ -21,6 +28,8 @@ import { getCachedStringWidth } from '../utils/textUtils.js';
|
|||||||
import { useTabbedNavigation } from '../hooks/useTabbedNavigation.js';
|
import { useTabbedNavigation } from '../hooks/useTabbedNavigation.js';
|
||||||
import { DialogFooter } from './shared/DialogFooter.js';
|
import { DialogFooter } from './shared/DialogFooter.js';
|
||||||
import { MaxSizedBox } from './shared/MaxSizedBox.js';
|
import { MaxSizedBox } from './shared/MaxSizedBox.js';
|
||||||
|
import { UIStateContext } from '../contexts/UIStateContext.js';
|
||||||
|
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
|
||||||
|
|
||||||
interface AskUserDialogState {
|
interface AskUserDialogState {
|
||||||
answers: { [key: string]: string };
|
answers: { [key: string]: string };
|
||||||
@@ -121,7 +130,7 @@ interface AskUserDialogProps {
|
|||||||
/**
|
/**
|
||||||
* Height constraint for scrollable content.
|
* Height constraint for scrollable content.
|
||||||
*/
|
*/
|
||||||
availableHeight: number;
|
availableHeight?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReviewViewProps {
|
interface ReviewViewProps {
|
||||||
@@ -199,7 +208,7 @@ interface TextQuestionViewProps {
|
|||||||
onSelectionChange?: (answer: string) => void;
|
onSelectionChange?: (answer: string) => void;
|
||||||
onEditingCustomOption?: (editing: boolean) => void;
|
onEditingCustomOption?: (editing: boolean) => void;
|
||||||
availableWidth: number;
|
availableWidth: number;
|
||||||
availableHeight: number;
|
availableHeight?: number;
|
||||||
initialAnswer?: string;
|
initialAnswer?: string;
|
||||||
progressHeader?: React.ReactNode;
|
progressHeader?: React.ReactNode;
|
||||||
keyboardHints?: React.ReactNode;
|
keyboardHints?: React.ReactNode;
|
||||||
@@ -216,6 +225,7 @@ const TextQuestionView: React.FC<TextQuestionViewProps> = ({
|
|||||||
progressHeader,
|
progressHeader,
|
||||||
keyboardHints,
|
keyboardHints,
|
||||||
}) => {
|
}) => {
|
||||||
|
const isAlternateBuffer = useAlternateBuffer();
|
||||||
const prefix = '> ';
|
const prefix = '> ';
|
||||||
const horizontalPadding = 1; // 1 for cursor
|
const horizontalPadding = 1; // 1 for cursor
|
||||||
const bufferWidth =
|
const bufferWidth =
|
||||||
@@ -279,13 +289,20 @@ const TextQuestionView: React.FC<TextQuestionViewProps> = ({
|
|||||||
const INPUT_HEIGHT = 2; // TextInput + margin
|
const INPUT_HEIGHT = 2; // TextInput + margin
|
||||||
const FOOTER_HEIGHT = 2; // DialogFooter + margin
|
const FOOTER_HEIGHT = 2; // DialogFooter + margin
|
||||||
const overhead = HEADER_HEIGHT + INPUT_HEIGHT + FOOTER_HEIGHT;
|
const overhead = HEADER_HEIGHT + INPUT_HEIGHT + FOOTER_HEIGHT;
|
||||||
const questionHeight = Math.max(1, availableHeight - overhead);
|
const questionHeight =
|
||||||
|
availableHeight && !isAlternateBuffer
|
||||||
|
? Math.max(1, availableHeight - overhead)
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
{progressHeader}
|
{progressHeader}
|
||||||
<Box marginBottom={1}>
|
<Box marginBottom={1}>
|
||||||
<MaxSizedBox maxHeight={questionHeight} maxWidth={availableWidth}>
|
<MaxSizedBox
|
||||||
|
maxHeight={questionHeight}
|
||||||
|
maxWidth={availableWidth}
|
||||||
|
overflowDirection="bottom"
|
||||||
|
>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
{question.question}
|
{question.question}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -389,7 +406,7 @@ interface ChoiceQuestionViewProps {
|
|||||||
onSelectionChange?: (answer: string) => void;
|
onSelectionChange?: (answer: string) => void;
|
||||||
onEditingCustomOption?: (editing: boolean) => void;
|
onEditingCustomOption?: (editing: boolean) => void;
|
||||||
availableWidth: number;
|
availableWidth: number;
|
||||||
availableHeight: number;
|
availableHeight?: number;
|
||||||
initialAnswer?: string;
|
initialAnswer?: string;
|
||||||
progressHeader?: React.ReactNode;
|
progressHeader?: React.ReactNode;
|
||||||
keyboardHints?: React.ReactNode;
|
keyboardHints?: React.ReactNode;
|
||||||
@@ -406,6 +423,7 @@ const ChoiceQuestionView: React.FC<ChoiceQuestionViewProps> = ({
|
|||||||
progressHeader,
|
progressHeader,
|
||||||
keyboardHints,
|
keyboardHints,
|
||||||
}) => {
|
}) => {
|
||||||
|
const isAlternateBuffer = useAlternateBuffer();
|
||||||
const numOptions =
|
const numOptions =
|
||||||
(question.options?.length ?? 0) + (question.type !== 'yesno' ? 1 : 0);
|
(question.options?.length ?? 0) + (question.type !== 'yesno' ? 1 : 0);
|
||||||
const numLen = String(numOptions).length;
|
const numLen = String(numOptions).length;
|
||||||
@@ -711,18 +729,27 @@ const ChoiceQuestionView: React.FC<ChoiceQuestionViewProps> = ({
|
|||||||
const TITLE_MARGIN = 1;
|
const TITLE_MARGIN = 1;
|
||||||
const FOOTER_HEIGHT = 2; // DialogFooter + margin
|
const FOOTER_HEIGHT = 2; // DialogFooter + margin
|
||||||
const overhead = HEADER_HEIGHT + TITLE_MARGIN + FOOTER_HEIGHT;
|
const overhead = HEADER_HEIGHT + TITLE_MARGIN + FOOTER_HEIGHT;
|
||||||
const listHeight = Math.max(1, availableHeight - overhead);
|
const listHeight = availableHeight
|
||||||
const questionHeight = Math.min(3, Math.max(1, listHeight - 4));
|
? Math.max(1, availableHeight - overhead)
|
||||||
const maxItemsToShow = Math.max(
|
: undefined;
|
||||||
1,
|
const questionHeight =
|
||||||
Math.floor((listHeight - questionHeight) / 2),
|
listHeight && !isAlternateBuffer
|
||||||
);
|
? Math.min(15, Math.max(1, listHeight - 4))
|
||||||
|
: undefined;
|
||||||
|
const maxItemsToShow =
|
||||||
|
listHeight && questionHeight
|
||||||
|
? Math.max(1, Math.floor((listHeight - questionHeight) / 2))
|
||||||
|
: selectionItems.length;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column">
|
<Box flexDirection="column">
|
||||||
{progressHeader}
|
{progressHeader}
|
||||||
<Box marginBottom={TITLE_MARGIN}>
|
<Box marginBottom={TITLE_MARGIN}>
|
||||||
<MaxSizedBox maxHeight={questionHeight} maxWidth={availableWidth}>
|
<MaxSizedBox
|
||||||
|
maxHeight={questionHeight}
|
||||||
|
maxWidth={availableWidth}
|
||||||
|
overflowDirection="bottom"
|
||||||
|
>
|
||||||
<Text bold color={theme.text.primary}>
|
<Text bold color={theme.text.primary}>
|
||||||
{question.question}
|
{question.question}
|
||||||
{question.multiSelect && (
|
{question.multiSelect && (
|
||||||
@@ -824,8 +851,15 @@ export const AskUserDialog: React.FC<AskUserDialogProps> = ({
|
|||||||
onCancel,
|
onCancel,
|
||||||
onActiveTextInputChange,
|
onActiveTextInputChange,
|
||||||
width,
|
width,
|
||||||
availableHeight,
|
availableHeight: availableHeightProp,
|
||||||
}) => {
|
}) => {
|
||||||
|
const uiState = useContext(UIStateContext);
|
||||||
|
const availableHeight =
|
||||||
|
availableHeightProp ??
|
||||||
|
(uiState?.constrainHeight !== false
|
||||||
|
? uiState?.availableTerminalHeight
|
||||||
|
: undefined);
|
||||||
|
|
||||||
const [state, dispatch] = useReducer(askUserDialogReducerLogic, initialState);
|
const [state, dispatch] = useReducer(askUserDialogReducerLogic, initialState);
|
||||||
const { answers, isEditingCustomOption, submitted } = state;
|
const { answers, isEditingCustomOption, submitted } = state;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,56 @@
|
|||||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||||
|
|
||||||
|
exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: false) > shows scroll arrows correctly when useAlternateBuffer is false 1`] = `
|
||||||
|
"Choose an option
|
||||||
|
|
||||||
|
▲
|
||||||
|
● 1. Option 1
|
||||||
|
Description 1
|
||||||
|
2. Option 2
|
||||||
|
Description 2
|
||||||
|
▼
|
||||||
|
|
||||||
|
Enter to select · ↑/↓ to navigate · Esc to cancel"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`AskUserDialog > Scroll Arrows (useAlternateBuffer: true) > shows scroll arrows correctly when useAlternateBuffer is true 1`] = `
|
||||||
|
"Choose an option
|
||||||
|
|
||||||
|
● 1. Option 1
|
||||||
|
Description 1
|
||||||
|
2. Option 2
|
||||||
|
Description 2
|
||||||
|
3. Option 3
|
||||||
|
Description 3
|
||||||
|
4. Option 4
|
||||||
|
Description 4
|
||||||
|
5. Option 5
|
||||||
|
Description 5
|
||||||
|
6. Option 6
|
||||||
|
Description 6
|
||||||
|
7. Option 7
|
||||||
|
Description 7
|
||||||
|
8. Option 8
|
||||||
|
Description 8
|
||||||
|
9. Option 9
|
||||||
|
Description 9
|
||||||
|
10. Option 10
|
||||||
|
Description 10
|
||||||
|
11. Option 11
|
||||||
|
Description 11
|
||||||
|
12. Option 12
|
||||||
|
Description 12
|
||||||
|
13. Option 13
|
||||||
|
Description 13
|
||||||
|
14. Option 14
|
||||||
|
Description 14
|
||||||
|
15. Option 15
|
||||||
|
Description 15
|
||||||
|
16. Enter a custom value
|
||||||
|
|
||||||
|
Enter to select · ↑/↓ to navigate · Esc to cancel"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`AskUserDialog > Text type questions > renders text input for type: "text" 1`] = `
|
exports[`AskUserDialog > Text type questions > renders text input for type: "text" 1`] = `
|
||||||
"What should we name this component?
|
"What should we name this component?
|
||||||
|
|
||||||
@@ -104,19 +155,6 @@ Which database should we use?
|
|||||||
Enter to select · ←/→ to switch questions · Esc to cancel"
|
Enter to select · ←/→ to switch questions · Esc to cancel"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
exports[`AskUserDialog > shows scroll arrows when options exceed available height 1`] = `
|
|
||||||
"Choose an option
|
|
||||||
|
|
||||||
▲
|
|
||||||
● 1. Option 1
|
|
||||||
Description 1
|
|
||||||
2. Option 2
|
|
||||||
Description 2
|
|
||||||
▼
|
|
||||||
|
|
||||||
Enter to select · ↑/↓ to navigate · Esc to cancel"
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`AskUserDialog > shows warning for unanswered questions on Review tab 1`] = `
|
exports[`AskUserDialog > shows warning for unanswered questions on Review tab 1`] = `
|
||||||
"← □ License │ □ README │ ≡ Review →
|
"← □ License │ □ README │ ≡ Review →
|
||||||
|
|
||||||
|
|||||||
@@ -271,7 +271,7 @@ export const ToolConfirmationMessage: React.FC<
|
|||||||
handleConfirm(ToolConfirmationOutcome.Cancel);
|
handleConfirm(ToolConfirmationOutcome.Cancel);
|
||||||
}}
|
}}
|
||||||
width={terminalWidth}
|
width={terminalWidth}
|
||||||
availableHeight={availableBodyContentHeight() ?? 10}
|
availableHeight={availableBodyContentHeight()}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
return { question: '', bodyContent, options: [] };
|
return { question: '', bodyContent, options: [] };
|
||||||
|
|||||||
Reference in New Issue
Block a user