mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-02 21:21:09 -07:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5a49437cea |
@@ -515,6 +515,8 @@ const baseMockUiState = {
|
|||||||
activePtyId: undefined,
|
activePtyId: undefined,
|
||||||
backgroundTasks: new Map(),
|
backgroundTasks: new Map(),
|
||||||
backgroundTaskHeight: 0,
|
backgroundTaskHeight: 0,
|
||||||
|
copyModeEnabled: false,
|
||||||
|
mouseMode: true,
|
||||||
quota: {
|
quota: {
|
||||||
userTier: undefined,
|
userTier: undefined,
|
||||||
stats: undefined,
|
stats: undefined,
|
||||||
|
|||||||
@@ -2014,6 +2014,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
|||||||
|
|
||||||
useKeypress(handleGlobalKeypress, { isActive: true, priority: true });
|
useKeypress(handleGlobalKeypress, { isActive: true, priority: true });
|
||||||
|
|
||||||
|
const isSelectionMode = isAlternateBuffer && !mouseMode;
|
||||||
|
|
||||||
useKeypress(
|
useKeypress(
|
||||||
(key: Key) => {
|
(key: Key) => {
|
||||||
if (
|
if (
|
||||||
@@ -2028,13 +2030,16 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
|||||||
}
|
}
|
||||||
|
|
||||||
setCopyModeEnabled(false);
|
setCopyModeEnabled(false);
|
||||||
if (mouseMode) {
|
|
||||||
|
if (isSelectionMode) {
|
||||||
|
setMouseMode(true);
|
||||||
|
} else if (mouseMode) {
|
||||||
enableMouseEvents();
|
enableMouseEvents();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
isActive: copyModeEnabled,
|
isActive: copyModeEnabled || isSelectionMode,
|
||||||
// We need to receive keypresses first so they do not bubble to other
|
// We need to receive keypresses first so they do not bubble to other
|
||||||
// handlers.
|
// handlers.
|
||||||
priority: KeypressPriority.Critical,
|
priority: KeypressPriority.Critical,
|
||||||
|
|||||||
@@ -8,18 +8,56 @@ import { CopyModeWarning } from './CopyModeWarning.js';
|
|||||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||||
import { renderWithProviders } from '../../test-utils/render.js';
|
import { renderWithProviders } from '../../test-utils/render.js';
|
||||||
import { useInputState } from '../contexts/InputContext.js';
|
import { useInputState } from '../contexts/InputContext.js';
|
||||||
|
import { useUIState, type UIState } from '../contexts/UIStateContext.js';
|
||||||
|
import { useConfig } from '../contexts/ConfigContext.js';
|
||||||
|
import type { Config } from '@google/gemini-cli-core';
|
||||||
|
|
||||||
vi.mock('../contexts/InputContext.js');
|
vi.mock('../contexts/InputContext.js');
|
||||||
|
vi.mock('../contexts/UIStateContext.js');
|
||||||
|
vi.mock('../contexts/ConfigContext.js');
|
||||||
|
|
||||||
describe('CopyModeWarning', () => {
|
describe('CopyModeWarning', () => {
|
||||||
|
const mockUseUIState = vi.mocked(useUIState);
|
||||||
|
const mockUseConfig = vi.mocked(useConfig);
|
||||||
|
const mockUseInputState = vi.mocked(useInputState);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
mockUseConfig.mockReturnValue({
|
||||||
|
getUseAlternateBuffer: () => false,
|
||||||
it('renders nothing when copy mode is disabled', async () => {
|
} as unknown as Config);
|
||||||
vi.mocked(useInputState).mockReturnValue({
|
mockUseInputState.mockReturnValue({
|
||||||
copyModeEnabled: false,
|
copyModeEnabled: false,
|
||||||
} as unknown as ReturnType<typeof useInputState>);
|
} as unknown as ReturnType<typeof useInputState>);
|
||||||
|
mockUseUIState.mockReturnValue({
|
||||||
|
mouseMode: true,
|
||||||
|
} as unknown as UIState);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders nothing when copy mode is disabled and not in alternate buffer', async () => {
|
||||||
|
mockUseInputState.mockReturnValue({
|
||||||
|
copyModeEnabled: false,
|
||||||
|
} as unknown as ReturnType<typeof useInputState>);
|
||||||
|
mockUseUIState.mockReturnValue({
|
||||||
|
mouseMode: true,
|
||||||
|
} as unknown as UIState);
|
||||||
|
const { lastFrame, unmount } = await renderWithProviders(
|
||||||
|
<CopyModeWarning />,
|
||||||
|
);
|
||||||
|
expect(lastFrame({ allowEmpty: true })).toBe('');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders nothing when copy mode is disabled and mouse mode is disabled but not in alternate buffer', async () => {
|
||||||
|
mockUseInputState.mockReturnValue({
|
||||||
|
copyModeEnabled: false,
|
||||||
|
} as unknown as ReturnType<typeof useInputState>);
|
||||||
|
mockUseUIState.mockReturnValue({
|
||||||
|
mouseMode: false,
|
||||||
|
} as unknown as UIState);
|
||||||
|
mockUseConfig.mockReturnValue({
|
||||||
|
getUseAlternateBuffer: () => false,
|
||||||
|
} as unknown as Config);
|
||||||
const { lastFrame, unmount } = await renderWithProviders(
|
const { lastFrame, unmount } = await renderWithProviders(
|
||||||
<CopyModeWarning />,
|
<CopyModeWarning />,
|
||||||
);
|
);
|
||||||
@@ -28,9 +66,31 @@ describe('CopyModeWarning', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('renders warning when copy mode is enabled', async () => {
|
it('renders warning when copy mode is enabled', async () => {
|
||||||
vi.mocked(useInputState).mockReturnValue({
|
mockUseInputState.mockReturnValue({
|
||||||
copyModeEnabled: true,
|
copyModeEnabled: true,
|
||||||
} as unknown as ReturnType<typeof useInputState>);
|
} as unknown as ReturnType<typeof useInputState>);
|
||||||
|
mockUseUIState.mockReturnValue({
|
||||||
|
mouseMode: true,
|
||||||
|
} as unknown as UIState);
|
||||||
|
const { lastFrame, unmount } = await renderWithProviders(
|
||||||
|
<CopyModeWarning />,
|
||||||
|
);
|
||||||
|
expect(lastFrame()).toContain('In Copy Mode');
|
||||||
|
expect(lastFrame()).toContain('Use Page Up/Down to scroll');
|
||||||
|
expect(lastFrame()).toContain('Press Ctrl+S or any other key to exit');
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders warning when in alternate buffer and mouse mode is disabled', async () => {
|
||||||
|
mockUseInputState.mockReturnValue({
|
||||||
|
copyModeEnabled: false,
|
||||||
|
} as unknown as ReturnType<typeof useInputState>);
|
||||||
|
mockUseUIState.mockReturnValue({
|
||||||
|
mouseMode: false,
|
||||||
|
} as unknown as UIState);
|
||||||
|
mockUseConfig.mockReturnValue({
|
||||||
|
getUseAlternateBuffer: () => true,
|
||||||
|
} as unknown as Config);
|
||||||
const { lastFrame, unmount } = await renderWithProviders(
|
const { lastFrame, unmount } = await renderWithProviders(
|
||||||
<CopyModeWarning />,
|
<CopyModeWarning />,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -7,14 +7,22 @@
|
|||||||
import type React from 'react';
|
import type React from 'react';
|
||||||
import { Box, Text } from 'ink';
|
import { Box, Text } from 'ink';
|
||||||
import { useInputState } from '../contexts/InputContext.js';
|
import { useInputState } from '../contexts/InputContext.js';
|
||||||
|
import { useUIState } from '../contexts/UIStateContext.js';
|
||||||
|
import { useConfig } from '../contexts/ConfigContext.js';
|
||||||
import { theme } from '../semantic-colors.js';
|
import { theme } from '../semantic-colors.js';
|
||||||
|
|
||||||
export const CopyModeWarning: React.FC = () => {
|
export const CopyModeWarning: React.FC = () => {
|
||||||
const { copyModeEnabled } = useInputState();
|
const { copyModeEnabled } = useInputState();
|
||||||
|
const { mouseMode } = useUIState();
|
||||||
|
const config = useConfig();
|
||||||
|
const isTrueAlternateBuffer = config.getUseAlternateBuffer();
|
||||||
|
|
||||||
|
const isSelectionMode = isTrueAlternateBuffer && !mouseMode;
|
||||||
|
const showWarning = copyModeEnabled || isSelectionMode;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box height={1}>
|
<Box height={1}>
|
||||||
{copyModeEnabled && (
|
{showWarning && (
|
||||||
<Text color={theme.status.warning}>
|
<Text color={theme.status.warning}>
|
||||||
In Copy Mode. Use Page Up/Down to scroll. Press Ctrl+S or any other
|
In Copy Mode. Use Page Up/Down to scroll. Press Ctrl+S or any other
|
||||||
key to exit.
|
key to exit.
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ interface ScrollableListProps<T> extends VirtualizedListProps<T> {
|
|||||||
width?: string | number;
|
width?: string | number;
|
||||||
scrollbar?: boolean;
|
scrollbar?: boolean;
|
||||||
stableScrollback?: boolean;
|
stableScrollback?: boolean;
|
||||||
copyModeEnabled?: boolean;
|
|
||||||
isStatic?: boolean;
|
isStatic?: boolean;
|
||||||
fixedItemHeight?: boolean;
|
fixedItemHeight?: boolean;
|
||||||
targetScrollIndex?: number;
|
targetScrollIndex?: number;
|
||||||
|
|||||||
@@ -316,9 +316,8 @@ describe('<VirtualizedList />', () => {
|
|||||||
unmount();
|
unmount();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders correctly in copyModeEnabled when scrolled', async () => {
|
it('renders correctly with scrollbar={false} when scrolled', async () => {
|
||||||
const longData = Array.from({ length: 100 }, (_, i) => `Item ${i}`);
|
const longData = Array.from({ length: 100 }, (_, i) => `Item ${i}`);
|
||||||
// Use copy mode
|
|
||||||
const { lastFrame, unmount } = await render(
|
const { lastFrame, unmount } = await render(
|
||||||
<Box height={10} width={100}>
|
<Box height={10} width={100}>
|
||||||
<VirtualizedList
|
<VirtualizedList
|
||||||
@@ -331,7 +330,7 @@ describe('<VirtualizedList />', () => {
|
|||||||
keyExtractor={(item) => item}
|
keyExtractor={(item) => item}
|
||||||
estimatedItemHeight={() => 1}
|
estimatedItemHeight={() => 1}
|
||||||
initialScrollIndex={50}
|
initialScrollIndex={50}
|
||||||
copyModeEnabled={true}
|
scrollbar={false}
|
||||||
/>
|
/>
|
||||||
</Box>,
|
</Box>,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ export type VirtualizedListProps<T> = {
|
|||||||
overflowToBackbuffer?: boolean;
|
overflowToBackbuffer?: boolean;
|
||||||
scrollbar?: boolean;
|
scrollbar?: boolean;
|
||||||
stableScrollback?: boolean;
|
stableScrollback?: boolean;
|
||||||
copyModeEnabled?: boolean;
|
|
||||||
fixedItemHeight?: boolean;
|
fixedItemHeight?: boolean;
|
||||||
containerHeight?: number;
|
containerHeight?: number;
|
||||||
};
|
};
|
||||||
@@ -144,7 +143,6 @@ function VirtualizedList<T>(
|
|||||||
overflowToBackbuffer,
|
overflowToBackbuffer,
|
||||||
scrollbar = true,
|
scrollbar = true,
|
||||||
stableScrollback,
|
stableScrollback,
|
||||||
copyModeEnabled = false,
|
|
||||||
fixedItemHeight = false,
|
fixedItemHeight = false,
|
||||||
} = props;
|
} = props;
|
||||||
const dataRef = useRef(data);
|
const dataRef = useRef(data);
|
||||||
@@ -727,25 +725,20 @@ function VirtualizedList<T>(
|
|||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
ref={containerRefCallback}
|
ref={containerRefCallback}
|
||||||
overflowY={copyModeEnabled ? 'hidden' : 'scroll'}
|
overflowY="scroll"
|
||||||
overflowX="hidden"
|
overflowX="hidden"
|
||||||
scrollTop={copyModeEnabled ? 0 : scrollTop}
|
scrollTop={scrollTop}
|
||||||
scrollbarThumbColor={props.scrollbarThumbColor ?? theme.text.secondary}
|
scrollbarThumbColor={props.scrollbarThumbColor ?? theme.text.secondary}
|
||||||
backgroundColor={props.backgroundColor}
|
backgroundColor={props.backgroundColor}
|
||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
flexDirection="column"
|
flexDirection="column"
|
||||||
paddingRight={copyModeEnabled ? 0 : 1}
|
paddingRight={1}
|
||||||
overflowToBackbuffer={overflowToBackbuffer}
|
overflowToBackbuffer={overflowToBackbuffer}
|
||||||
scrollbar={scrollbar}
|
scrollbar={scrollbar}
|
||||||
stableScrollback={stableScrollback}
|
stableScrollback={stableScrollback}
|
||||||
>
|
>
|
||||||
<Box
|
<Box flexShrink={0} width="100%" flexDirection="column">
|
||||||
flexShrink={0}
|
|
||||||
width="100%"
|
|
||||||
flexDirection="column"
|
|
||||||
marginTop={copyModeEnabled ? -actualScrollTop : 0}
|
|
||||||
>
|
|
||||||
<Box height={topSpacerHeight} flexShrink={0} />
|
<Box height={topSpacerHeight} flexShrink={0} />
|
||||||
{renderedItems}
|
{renderedItems}
|
||||||
<Box height={bottomSpacerHeight} flexShrink={0} />
|
<Box height={bottomSpacerHeight} flexShrink={0} />
|
||||||
|
|||||||
Reference in New Issue
Block a user