import { Animated, View, Text, Pressable, ActivityIndicator } from 'react-native' import { saveTerminalTextScale } from '../storage/preferences' import { MobileBrowserPane } from '../browser/MobileBrowserPane' import { TerminalPaneView } from './TerminalPaneView' import { TerminalEnginePrewarm } from './TerminalEnginePrewarm' import { MOBILE_SESSION_TAB_BAR_HEIGHT } from './mobile-session-frame-styles' import { MobileNativeChatOverlay } from './MobileNativeChatOverlay' import { colors } from '../theme/mobile-theme' import { styles } from './mobile-session-styles' import type { MobileSessionController } from './use-mobile-session-controller' import { FileReader } from './MobileSessionFileReader' import { MarkdownReader } from './MobileSessionMarkdownReader' export function MobileSessionActiveContent({ controller }: { controller: MobileSessionController }) { const { worktreeId, insets, connState, client, terminals, terminalTextScale, setTerminalTextScale, activeHandle, markdownDocs, fileDocs, diffComments, diffCommentBusy, createError, setCreateError, setShowCreateTabDrawer, dictationMode, toastMessage, terminalFrameHeightRef, setTerminalFrameWidth, handleTerminalTap, browserScreencastSupported, showToast, nativeChatSendError, nativeChatOverlayInputLockReason, nativeChatController, dictation, handleDictationToggle, handleDictationPressIn, handleDictationPressOut, readMarkdownTab, addDiffCommentForFile, deleteDiffCommentForFile, copyDiffCommentsToClipboard, sendDiffCommentsToAgent, updateMarkdownLocalContent, copyMarkdownLocalContent, discardMarkdownLocalContent, saveMarkdownTab, notifyTerminalFrameHeight, setTerminalWebViewRef, handleTerminalWebReady, handleFileTap, handleNativeChatFileTap, handleTerminalOpenUrl, handleTerminalInput, handleTerminalQueryReply, handleSelectionMode, handleSelectionCopy, handleSelectionEvicted, handleModesChanged, handleKeyboardAvoidanceMetrics, handleHaptic, nativeChatImages, activeMarkdownTab, activeFileTab, activeBrowserTab, activePendingTerminalTab, isPendingTerminalRecoveryParked, retryPendingTerminalRecovery, showLoadingState, measurePrewarmViewport, visibleTabs, showEmptyState, keyboardLift, activeTerminalKeyboardLift, toastAnimatedStyle, createTabBusy } = controller // Why the same list the header gates on: an unmounted tab bar gives the content row its band // back, so the pre-warm would measure a taller box than the pane ever gets. Reading the header's // own condition keeps the two from drifting when what counts as a visible tab changes. const prewarmReservedTabBarHeight = visibleTabs.length > 0 ? 0 : MOBILE_SESSION_TAB_BAR_HEIGHT return showLoadingState ? ( // Why: the engine boots inside the real terminal frame while the startup RPCs are still in // flight, so the first pane inherits a warm WebView and a measured viewport (see prewarm). ) : showEmptyState ? ( No tabs in this session {createError ? {createError} : null} { setCreateError('') setShowCreateTabDrawer(true) }} > {createTabBusy ? 'Creating...' : 'Create Tab'} ) : activeMarkdownTab ? ( void readMarkdownTab(activeMarkdownTab)} onChange={(content) => updateMarkdownLocalContent(activeMarkdownTab.id, content)} onSave={() => void saveMarkdownTab(activeMarkdownTab)} onCopy={() => void copyMarkdownLocalContent(activeMarkdownTab.id)} onDiscard={() => discardMarkdownLocalContent(activeMarkdownTab)} keyboardLift={keyboardLift} /> {toastMessage && ( {toastMessage} )} ) : activeFileTab ? ( {toastMessage && ( {toastMessage} )} ) : activeBrowserTab ? ( {/* Why: pane owns imperative frame refs; don't render a stale frame while the old stream effect cleans up. */} {toastMessage && ( {toastMessage} )} ) : activePendingTerminalTab ? ( {!isPendingTerminalRecoveryParked && ( )} {isPendingTerminalRecoveryParked ? 'Terminal is taking longer than expected' : activePendingTerminalTab.title || 'Loading terminal'} {isPendingTerminalRecoveryParked && ( [ styles.createButton, pressed && styles.newTerminalButtonPressed ]} onPress={() => void retryPendingTerminalRecovery()} > Retry )} ) : ( { terminalFrameHeightRef.current = e.nativeEvent.layout.height // Why: notify height imperatively so dock settling re-fits the PTY without rerendering SessionScreen. const nextWidth = Math.round(e.nativeEvent.layout.width) const nextHeight = Math.round(e.nativeEvent.layout.height) setTerminalFrameWidth((prev) => (prev === nextWidth ? prev : nextWidth)) notifyTerminalFrameHeight(nextHeight) }} > {terminals.map((terminal) => ( { // Why: pinch-to-zoom reports a new preset; persist it so the size sticks across panes and launches. setTerminalTextScale(scale) void saveTerminalTextScale(scale) }} onRef={setTerminalWebViewRef} onWebReady={handleTerminalWebReady} onSelectionMode={handleSelectionMode} onSelectionCopy={handleSelectionCopy} onSelectionEvicted={handleSelectionEvicted} onModesChanged={handleModesChanged} onKeyboardAvoidanceMetrics={handleKeyboardAvoidanceMetrics} onHaptic={handleHaptic} onTerminalInput={handleTerminalInput} onTerminalQueryReply={handleTerminalQueryReply} onTerminalTap={handleTerminalTap} onFileTap={handleFileTap} onOpenUrl={handleTerminalOpenUrl} /> ))} {toastMessage && ( {toastMessage} )} ) }