/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import type { RefObject } from 'react'; import type React from 'react'; import { createContext, useContext, useState, useMemo, useEffect, useRef, } from 'react'; import type { DOMElement } from 'ink'; import { measureElement } from 'ink'; import { useTerminalSize } from '../hooks/useTerminalSize.js'; interface LayoutContextValue { terminalHeight: number; terminalWidth: number; footerHeight: number; constrainHeight: boolean; availableTerminalHeight: number; setFooterHeight: (height: number) => void; setConstrainHeight: (value: boolean) => void; footerRef: RefObject; registerFooterDependency: () => void; } const LayoutContext = createContext(undefined); export const useLayout = () => { const context = useContext(LayoutContext); if (!context) { throw new Error('useLayout must be used within LayoutManager'); } return context; }; interface LayoutManagerProps { children: React.ReactNode; } export const LayoutManager: React.FC = ({ children }) => { const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize(); const [footerHeight, setFooterHeight] = useState(0); const [constrainHeight, setConstrainHeight] = useState(true); const footerRef = useRef(null); const [footerUpdateCounter, setFooterUpdateCounter] = useState(0); // Register additional dependencies that might affect footer height const registerFooterDependency = useRef(() => { setFooterUpdateCounter((prev) => prev + 1); }).current; // Measure footer element when it changes useEffect(() => { if (footerRef.current) { const measurement = measureElement(footerRef.current); setFooterHeight(measurement.height); } }, [terminalHeight, footerUpdateCounter]); // Re-measure when terminal height or dependencies change // Same calculation as in App.tsx const staticExtraHeight = /* margins and padding */ 3; const availableTerminalHeight = useMemo( () => terminalHeight - footerHeight - staticExtraHeight, [terminalHeight, footerHeight], ); const value = useMemo( () => ({ terminalHeight, terminalWidth, footerHeight, constrainHeight, availableTerminalHeight, setFooterHeight, setConstrainHeight, footerRef, registerFooterDependency, }), [ terminalHeight, terminalWidth, footerHeight, constrainHeight, availableTerminalHeight, registerFooterDependency, ], ); return ( {children} ); };