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}
)}
)
}