/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import React from 'react'; import { Box, type DOMElement, Static } from 'ink'; import type { LoadedSettings } from '../../config/settings.js'; import type { UpdateObject } from '../utils/updateCheck.js'; import { useUIState } from '../contexts/UIStateContext.js'; import { useUIActions } from '../contexts/UIActionsContext.js'; import type { UIActions } from '../contexts/UIActionsContext.js'; import { StreamingContext } from '../contexts/StreamingContext.js'; import { OverflowProvider } from '../contexts/OverflowContext.js'; import { ShowMoreLines } from '../components/ShowMoreLines.js'; import { ScrollableList } from '../components/shared/ScrollableList.js'; import { SCROLL_TO_ITEM_END } from '../components/shared/VirtualizedList.js'; import { type ScrollableMainContentItem, renderScrollableMainContentItem, keyExtractorScrollableMainContentItem, estimateScrollableMainContentItemHeight, hasActiveDialog, useLayoutSettings, useScrollableContent, type MainControlsProps, MainControls, QuittingDisplay, } from './DefaultAppLayoutHelpers.js'; import type { SlashCommandRuntime, UiRuntime } from '../cliUiRuntime.js'; interface DefaultAppLayoutProps { uiRuntime: UiRuntime; slashCommandRuntime: SlashCommandRuntime; settings: LoadedSettings; startupWarnings: string[]; version: string; nightly: boolean; mainControlsRef: React.RefObject; availableTerminalHeight: number; contextFileNames: string[]; updateInfo: UpdateObject | null; } function useDerivedState( uiState: ReturnType, uiRuntime: UiRuntime, slashCommandRuntime: SlashCommandRuntime, settings: LoadedSettings, availableTerminalHeight: number, version: string, nightly: boolean, ) { const layoutSettings = useLayoutSettings( uiRuntime, settings, availableTerminalHeight, uiState.terminalHeight, uiState.constrainHeight, uiState.availableTerminalHeight, uiState.isNarrow, ); const dialogsVisible = hasActiveDialog(uiState); const { listItems, staticItems, pendingItems } = useScrollableContent( slashCommandRuntime, settings, version, nightly, uiState.terminalWidth, uiState.mainAreaWidth, layoutSettings.staticAreaMaxItemHeight, uiState.constrainHeight, layoutSettings.effectiveAvailableHeight, layoutSettings.showTodoPanelSetting, uiState, uiState.slashCommands, uiState.activeShellPtyId, uiState.embeddedShellFocused, ); return { layoutSettings, dialogsVisible, listItems, staticItems, pendingItems, }; } export const DefaultAppLayout = ({ uiRuntime, slashCommandRuntime, settings, startupWarnings, version, nightly, mainControlsRef, availableTerminalHeight, contextFileNames, updateInfo, }: DefaultAppLayoutProps) => { const uiState = useUIState(); const uiActions = useUIActions(); const [, setSuggestionsVisible] = React.useState(false); const { layoutSettings, dialogsVisible, listItems, staticItems, pendingItems, } = useDerivedState( uiState, uiRuntime, slashCommandRuntime, settings, availableTerminalHeight, version, nightly, ); const mainControlsSharedProps = buildMainControlsProps( uiState, layoutSettings, slashCommandRuntime, settings, startupWarnings, updateInfo, contextFileNames, nightly, uiActions, setSuggestionsVisible, ); if (uiState.quittingMessages) { return ( ); } return renderLayout( uiState, layoutSettings, dialogsVisible, listItems, staticItems, pendingItems, mainControlsRef, mainControlsSharedProps, ); }; function renderLayout( uiState: ReturnType, layoutSettings: ReturnType, dialogsVisible: boolean, listItems: ScrollableMainContentItem[], staticItems: React.ReactElement[], pendingItems: React.ReactElement[], mainControlsRef: React.RefObject, mainControlsSharedProps: MainControlsProps, ) { if (layoutSettings.useAlternateBuffer) { return ( ); } return ( ); } function buildMainControlsProps( uiState: ReturnType, layoutSettings: ReturnType, slashCommandRuntime: SlashCommandRuntime, settings: LoadedSettings, startupWarnings: string[], updateInfo: UpdateObject | null, contextFileNames: string[], nightly: boolean, uiActions: UIActions, onSuggestionsVisibilityChange: (visible: boolean) => void, ): MainControlsProps { return { config: slashCommandRuntime, settings, startupWarnings, updateInfo, history: uiState.history, inputWidth: uiState.inputWidth, isTodoPanelCollapsed: uiState.isTodoPanelCollapsed, isQueuedMessagesPanelCollapsed: uiState.isQueuedMessagesPanelCollapsed, queuedSubmissions: uiState.queuedSubmissions, showTodoPanelSetting: layoutSettings.showTodoPanelSetting, dialogsVisible: hasActiveDialog(uiState), hideContextSummary: layoutSettings.hideContextSummary, hideFooter: layoutSettings.hideFooter, showMemoryUsage: layoutSettings.showMemoryUsage, currentThemeName: layoutSettings.currentThemeName, nightly, constrainHeight: uiState.constrainHeight, debugConsoleMaxHeight: layoutSettings.debugConsoleMaxHeight, effectiveAvailableHeight: layoutSettings.effectiveAvailableHeight, disableLoadingPhrases: layoutSettings.disableLoadingPhrases, streamingState: uiState.streamingState, thought: uiState.thought, currentLoadingPhrase: uiState.currentLoadingPhrase, elapsedTime: uiState.elapsedTime, isNarrow: layoutSettings.isNarrow, ctrlCPressedOnce: uiState.ctrlCPressedOnce, ctrlDPressedOnce: uiState.ctrlDPressedOnce, showEscapePrompt: uiState.showEscapePrompt, ideContextState: uiState.ideContextState, llxprtMdFileCount: uiState.llxprtMdFileCount, coreMemoryFileCount: uiState.coreMemoryFileCount, contextFileNames, showToolDescriptions: uiState.showToolDescriptions, showAutoAcceptIndicator: uiState.showAutoAcceptIndicator, shellModeActive: uiState.shellModeActive, showErrorDetails: uiState.showErrorDetails, consoleMessages: uiState.consoleMessages, isInputActive: uiState.isInputActive, vimModeEnabled: uiState.vimModeEnabled, vimMode: uiState.vimMode, currentModel: uiState.currentModel, currentModelLabel: uiState.currentModelLabel, contextLimit: uiState.contextLimit, branchName: uiState.branchName, debugMessage: uiState.debugMessage, errorCount: uiState.errorCount, historyTokenCount: uiState.historyTokenCount, tokenMetrics: uiState.tokenMetrics, uiActions, terminalWidth: uiState.terminalWidth, onSuggestionsVisibilityChange, }; } function AlternateBufferLayout({ terminalWidth, terminalHeight, rootUiRef, dialogsVisible, listItems, mainControlsRef, mainControlsSharedProps, }: { terminalWidth: number; terminalHeight: number; rootUiRef: React.RefObject; dialogsVisible: boolean; listItems: ScrollableMainContentItem[]; mainControlsRef: React.RefObject; mainControlsSharedProps: MainControlsProps; }) { return ( ); } function StandardBufferLayout({ rootUiRef, staticKey, staticItems, pendingHistoryItemRef, pendingItems, constrainHeight, mainControlsRef, mainControlsSharedProps, }: { rootUiRef: React.RefObject; staticKey: number; staticItems: React.ReactElement[]; pendingHistoryItemRef: React.RefObject; pendingItems: React.ReactElement[]; constrainHeight: boolean; mainControlsRef: React.RefObject; mainControlsSharedProps: MainControlsProps; }) { return ( {staticItems.length > 0 ? ( {(item) => item} ) : null} {pendingItems} ); }