feat(ux) Expandable (ctrl-O) and scrollable approvals in alternate buffer mode. (#17640)

This commit is contained in:
Jacob Richman
2026-01-27 16:06:24 -08:00
committed by GitHub
parent ff6547857e
commit d165b6d4e7
34 changed files with 1177 additions and 496 deletions

View File

@@ -6,16 +6,20 @@
import { Box, Static } from 'ink';
import { HistoryItemDisplay } from './HistoryItemDisplay.js';
import { ShowMoreLines } from './ShowMoreLines.js';
import { OverflowProvider } from '../contexts/OverflowContext.js';
import { useUIState } from '../contexts/UIStateContext.js';
import { useAppContext } from '../contexts/AppContext.js';
import { AppHeader } from './AppHeader.js';
import { useAlternateBuffer } from '../hooks/useAlternateBuffer.js';
import { SCROLL_TO_ITEM_END } from './shared/VirtualizedList.js';
import {
SCROLL_TO_ITEM_END,
type VirtualizedListRef,
} from './shared/VirtualizedList.js';
import { ScrollableList } from './shared/ScrollableList.js';
import { useMemo, memo, useCallback } from 'react';
import { useMemo, memo, useCallback, useEffect, useRef } from 'react';
import { MAX_GEMINI_MESSAGE_LINES } from '../constants.js';
import { useConfirmingTool } from '../hooks/useConfirmingTool.js';
import { ToolConfirmationQueue } from './ToolConfirmationQueue.js';
import { useConfig } from '../contexts/ConfigContext.js';
const MemoizedHistoryItemDisplay = memo(HistoryItemDisplay);
const MemoizedAppHeader = memo(AppHeader);
@@ -27,8 +31,21 @@ const MemoizedAppHeader = memo(AppHeader);
export const MainContent = () => {
const { version } = useAppContext();
const uiState = useUIState();
const config = useConfig();
const isAlternateBuffer = useAlternateBuffer();
const confirmingTool = useConfirmingTool();
const showConfirmationQueue =
config.isEventDrivenSchedulerEnabled() && confirmingTool !== null;
const scrollableListRef = useRef<VirtualizedListRef<unknown>>(null);
useEffect(() => {
if (showConfirmationQueue) {
scrollableListRef.current?.scrollToEnd();
}
}, [showConfirmationQueue, confirmingTool]);
const {
pendingHistoryItems,
mainAreaWidth,
@@ -59,27 +76,27 @@ export const MainContent = () => {
const pendingItems = useMemo(
() => (
<OverflowProvider>
<Box flexDirection="column">
{pendingHistoryItems.map((item, i) => (
<HistoryItemDisplay
key={i}
availableTerminalHeight={
uiState.constrainHeight && !isAlternateBuffer
? availableTerminalHeight
: undefined
}
terminalWidth={mainAreaWidth}
item={{ ...item, id: 0 }}
isPending={true}
isFocused={!uiState.isEditorDialogOpen}
activeShellPtyId={uiState.activePtyId}
embeddedShellFocused={uiState.embeddedShellFocused}
/>
))}
<ShowMoreLines constrainHeight={uiState.constrainHeight} />
</Box>
</OverflowProvider>
<Box flexDirection="column">
{pendingHistoryItems.map((item, i) => (
<HistoryItemDisplay
key={i}
availableTerminalHeight={
uiState.constrainHeight && !isAlternateBuffer
? availableTerminalHeight
: undefined
}
terminalWidth={mainAreaWidth}
item={{ ...item, id: 0 }}
isPending={true}
isFocused={!uiState.isEditorDialogOpen}
activeShellPtyId={uiState.activePtyId}
embeddedShellFocused={uiState.embeddedShellFocused}
/>
))}
{showConfirmationQueue && confirmingTool && (
<ToolConfirmationQueue confirmingTool={confirmingTool} />
)}
</Box>
),
[
pendingHistoryItems,
@@ -90,6 +107,8 @@ export const MainContent = () => {
uiState.isEditorDialogOpen,
uiState.activePtyId,
uiState.embeddedShellFocused,
showConfirmationQueue,
confirmingTool,
],
);
@@ -128,6 +147,7 @@ export const MainContent = () => {
if (isAlternateBuffer) {
return (
<ScrollableList
ref={scrollableListRef}
hasFocus={!uiState.isEditorDialogOpen}
width={uiState.terminalWidth}
data={virtualizedData}