Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 0 additions & 130 deletions packages/cli/src/ui/AppContainer.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2544,136 +2544,6 @@ describe('AppContainer State Management', () => {
});
});

describe('Expansion Persistence', () => {
let rerender: () => void;
let unmount: () => void;
let stdin: ReturnType<typeof render>['stdin'];

const setupExpansionPersistenceTest = async (
HighPriorityChild?: React.FC,
) => {
const getTree = () => (
<SettingsContext.Provider value={mockSettings}>
<KeypressProvider config={mockConfig}>
<OverflowProvider>
<AppContainer
config={mockConfig}
version="1.0.0"
initializationResult={mockInitResult}
/>
{HighPriorityChild && <HighPriorityChild />}
</OverflowProvider>
</KeypressProvider>
</SettingsContext.Provider>
);

const renderResult = render(getTree());
stdin = renderResult.stdin;
await act(async () => {
vi.advanceTimersByTime(100);
});
rerender = () => renderResult.rerender(getTree());
unmount = () => renderResult.unmount();
};

const writeStdin = async (sequence: string) => {
await act(async () => {
stdin.write(sequence);
// Advance timers to allow escape sequence parsing and broadcasting
vi.advanceTimersByTime(100);
});
rerender();
};

beforeEach(() => {
vi.useFakeTimers();
});

afterEach(() => {
vi.useRealTimers();
vi.restoreAllMocks();
});

it('should reset expansion when a key is NOT handled by anyone', async () => {
await setupExpansionPersistenceTest();

// Expand first
act(() => capturedUIActions.setConstrainHeight(false));
rerender();
expect(capturedUIState.constrainHeight).toBe(false);

// Press a random key that no one handles (hits Low priority fallback)
await writeStdin('x');

// Should be reset to true (collapsed)
expect(capturedUIState.constrainHeight).toBe(true);

unmount();
});

it('should toggle expansion when Ctrl+O is pressed', async () => {
await setupExpansionPersistenceTest();

// Initial state is collapsed
expect(capturedUIState.constrainHeight).toBe(true);

// Press Ctrl+O to expand (Ctrl+O is sequence \x0f)
await writeStdin('\x0f');
expect(capturedUIState.constrainHeight).toBe(false);

// Press Ctrl+O again to collapse
await writeStdin('\x0f');
expect(capturedUIState.constrainHeight).toBe(true);

unmount();
});

it('should NOT collapse when a high-priority component handles the key (e.g., up/down arrows)', async () => {
const NavigationHandler = () => {
// use real useKeypress
useKeypress(
(key: Key) => {
if (key.name === 'up' || key.name === 'down') {
return true; // Handle navigation
}
return false;
},
{ isActive: true, priority: true }, // High priority
);
return null;
};

await setupExpansionPersistenceTest(NavigationHandler);

// Expand first
act(() => capturedUIActions.setConstrainHeight(false));
rerender();
expect(capturedUIState.constrainHeight).toBe(false);

// 1. Simulate Up arrow (handled by high priority child)
// CSI A is Up arrow
await writeStdin('\u001b[A');

// Should STILL be expanded
expect(capturedUIState.constrainHeight).toBe(false);

// 2. Simulate Down arrow (handled by high priority child)
// CSI B is Down arrow
await writeStdin('\u001b[B');

// Should STILL be expanded
expect(capturedUIState.constrainHeight).toBe(false);

// 3. Sanity check: press an unhandled key
await writeStdin('x');

// Should finally collapse
expect(capturedUIState.constrainHeight).toBe(true);

unmount();
});
});

describe('Shortcuts Help Visibility', () => {
let handleGlobalKeypress: (key: Key) => boolean;
let mockedUseKeypress: Mock;
Expand Down
5 changes: 1 addition & 4 deletions packages/cli/src/ui/AppContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1873,10 +1873,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
],
);

useKeypress(handleGlobalKeypress, {
isActive: true,
priority: KeypressPriority.Low,
});
useKeypress(handleGlobalKeypress, { isActive: true, priority: true });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

This revert appears to be incomplete and will likely cause a significant regression.

By changing the priority to true without also reverting the logic inside handleGlobalKeypress, the expanded view will now collapse on every keypress, not just unhandled ones.

The problematic logic is the if (!constrainHeight) block within handleGlobalKeypress (around line 1713), which unconditionally collapses the view. With high priority, this will run before other key handlers, making it impossible to interact with the UI in an expanded state.

To complete the revert, the logic that was introduced to auto-collapse the view should also be removed from handleGlobalKeypress.

References
  1. Maintain consistency with existing UI behavior across components. Defer non-standard UX pattern improvements to be addressed holistically rather than in a single component.


useKeypress(
() => {
Expand Down
Loading