fix(ui): unify Ctrl+O expansion hint experience across buffer modes (#21474)

This commit is contained in:
Jarrod Whelan
2026-03-07 11:04:22 -08:00
committed by GitHub
parent e5d58c2b5a
commit 54b0344fc5
19 changed files with 184 additions and 451 deletions

View File

@@ -5,13 +5,22 @@
*/
import type React from 'react';
import { useState, useRef, useCallback, useMemo, useLayoutEffect } from 'react';
import {
useState,
useRef,
useCallback,
useMemo,
useLayoutEffect,
useEffect,
useId,
} from 'react';
import { Box, ResizeObserver, type DOMElement } from 'ink';
import { useKeypress, type Key } from '../../hooks/useKeypress.js';
import { useScrollable } from '../../contexts/ScrollProvider.js';
import { useAnimatedScrollbar } from '../../hooks/useAnimatedScrollbar.js';
import { useBatchedScroll } from '../../hooks/useBatchedScroll.js';
import { keyMatchers, Command } from '../../keyMatchers.js';
import { useOverflowActions } from '../../contexts/OverflowContext.js';
interface ScrollableProps {
children?: React.ReactNode;
@@ -22,6 +31,7 @@ interface ScrollableProps {
hasFocus: boolean;
scrollToBottom?: boolean;
flexGrow?: number;
reportOverflow?: boolean;
}
export const Scrollable: React.FC<ScrollableProps> = ({
@@ -33,10 +43,13 @@ export const Scrollable: React.FC<ScrollableProps> = ({
hasFocus,
scrollToBottom,
flexGrow,
reportOverflow = false,
}) => {
const [scrollTop, setScrollTop] = useState(0);
const viewportRef = useRef<DOMElement | null>(null);
const contentRef = useRef<DOMElement | null>(null);
const overflowActions = useOverflowActions();
const id = useId();
const [size, setSize] = useState({
innerHeight: typeof height === 'number' ? height : 0,
scrollHeight: 0,
@@ -52,6 +65,27 @@ export const Scrollable: React.FC<ScrollableProps> = ({
scrollTopRef.current = scrollTop;
}, [scrollTop]);
useEffect(() => {
if (reportOverflow && size.scrollHeight > size.innerHeight) {
overflowActions?.addOverflowingId?.(id);
} else {
overflowActions?.removeOverflowingId?.(id);
}
}, [
reportOverflow,
size.scrollHeight,
size.innerHeight,
id,
overflowActions,
]);
useEffect(
() => () => {
overflowActions?.removeOverflowingId?.(id);
},
[id, overflowActions],
);
const viewportObserverRef = useRef<ResizeObserver | null>(null);
const contentObserverRef = useRef<ResizeObserver | null>(null);