Download packages/cli/src/ui/contexts/OverflowContext.tsx from SaylorTwift/gemini-cli: direct link, hf CLI and curl.
- Browser
- Download file 3.3 kB
-
https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/ui/contexts/OverflowContext.tsx
- Command line
-
hf download hf://SaylorTwift/gemini-cli/packages/cli/src/ui/contexts/OverflowContext.tsx
-
curl -L -o OverflowContext.tsx https://huggingface.co/SaylorTwift/gemini-cli/resolve/main/packages/cli/src/ui/contexts/OverflowContext.tsx
3.3 kB
| /** | |
| * @license | |
| * Copyright 2025 Google LLC | |
| * SPDX-License-Identifier: Apache-2.0 | |
| */ | |
| import type React from 'react'; | |
| import { | |
| createContext, | |
| useContext, | |
| useState, | |
| useCallback, | |
| useMemo, | |
| useRef, | |
| useEffect, | |
| } from 'react'; | |
| export interface OverflowState { | |
| overflowingIds: ReadonlySet<string>; | |
| } | |
| export interface OverflowActions { | |
| addOverflowingId: (id: string) => void; | |
| removeOverflowingId: (id: string) => void; | |
| reset: () => void; | |
| } | |
| const OverflowStateContext = createContext<OverflowState | undefined>( | |
| undefined, | |
| ); | |
| const OverflowActionsContext = createContext<OverflowActions | undefined>( | |
| undefined, | |
| ); | |
| export const useOverflowState = (): OverflowState | undefined => | |
| useContext(OverflowStateContext); | |
| export const useOverflowActions = (): OverflowActions | undefined => | |
| useContext(OverflowActionsContext); | |
| export const OverflowProvider: React.FC<{ children: React.ReactNode }> = ({ | |
| children, | |
| }) => { | |
| const [overflowingIds, setOverflowingIds] = useState(new Set<string>()); | |
| /** | |
| * We use a ref to track the current set of overflowing IDs and a timeout to | |
| * batch updates to the next tick. This prevents infinite render loops (layout | |
| * oscillation) where showing an overflow hint causes a layout shift that | |
| * hides the hint, which then restores the layout and shows the hint again. | |
| */ | |
| const idsRef = useRef(new Set<string>()); | |
| const timeoutRef = useRef<NodeJS.Timeout | null>(null); | |
| const syncState = useCallback(() => { | |
| if (timeoutRef.current) return; | |
| // Use a microtask to batch updates and break synchronous recursive loops. | |
| // This prevents "Maximum update depth exceeded" errors during layout shifts. | |
| timeoutRef.current = setTimeout(() => { | |
| timeoutRef.current = null; | |
| setOverflowingIds((prevIds) => { | |
| // Optimization: only update state if the set has actually changed | |
| if ( | |
| prevIds.size === idsRef.current.size && | |
| [...prevIds].every((id) => idsRef.current.has(id)) | |
| ) { | |
| return prevIds; | |
| } | |
| return new Set(idsRef.current); | |
| }); | |
| }, 0); | |
| }, []); | |
| useEffect( | |
| () => () => { | |
| if (timeoutRef.current) { | |
| clearTimeout(timeoutRef.current); | |
| } | |
| }, | |
| [], | |
| ); | |
| const addOverflowingId = useCallback( | |
| (id: string) => { | |
| if (!idsRef.current.has(id)) { | |
| idsRef.current.add(id); | |
| syncState(); | |
| } | |
| }, | |
| [syncState], | |
| ); | |
| const removeOverflowingId = useCallback( | |
| (id: string) => { | |
| if (idsRef.current.has(id)) { | |
| idsRef.current.delete(id); | |
| syncState(); | |
| } | |
| }, | |
| [syncState], | |
| ); | |
| const reset = useCallback(() => { | |
| if (idsRef.current.size > 0) { | |
| idsRef.current.clear(); | |
| syncState(); | |
| } | |
| }, [syncState]); | |
| const stateValue = useMemo( | |
| () => ({ | |
| overflowingIds, | |
| }), | |
| [overflowingIds], | |
| ); | |
| const actionsValue = useMemo( | |
| () => ({ | |
| addOverflowingId, | |
| removeOverflowingId, | |
| reset, | |
| }), | |
| [addOverflowingId, removeOverflowingId, reset], | |
| ); | |
| return ( | |
| <OverflowStateContext.Provider value={stateValue}> | |
| <OverflowActionsContext.Provider value={actionsValue}> | |
| {children} | |
| </OverflowActionsContext.Provider> | |
| </OverflowStateContext.Provider> | |
| ); | |
| }; | |