import { AlternateScreen, Box, NoSelect, ScrollBox, Text } from '@hermes/ink'
import { useStore } from '@nanostores/react'
import { memo } from 'react'
import type { AppLayoutProgressProps, AppLayoutProps } from '../app/interfaces.js'
import { $isBlocked } from '../app/overlayStore.js'
import { $uiState } from '../app/uiStore.js'
import { PLACEHOLDER } from '../content/placeholders.js'
import type { Theme } from '../theme.js'
import type { DetailsMode } from '../types.js'
import { GoodVibesHeart, StatusRule, StickyPromptTracker, TranscriptScrollbar } from './appChrome.js'
import { FloatingOverlays, PromptZone } from './appOverlays.js'
import { Banner, Panel, SessionPanel } from './branding.js'
import { MessageLine } from './messageLine.js'
import { QueuedMessages } from './queuedMessages.js'
import { TextInput } from './textInput.js'
import { ToolTrail } from './thinking.js'
const StreamingAssistant = memo(function StreamingAssistant({
busy,
cols,
compact,
detailsMode,
progress,
t
}: StreamingAssistantProps) {
if (!progress.showProgressArea && !progress.showStreamingArea) {
return null
}
return (
<>
{progress.showProgressArea && (
)}
{progress.showStreamingArea && (
)}
>
)
})
const TranscriptPane = memo(function TranscriptPane({
actions,
composer,
progress,
transcript
}: Pick) {
const ui = useStore($uiState)
return (
<>
{transcript.virtualHistory.topSpacer > 0 ? : null}
{transcript.virtualRows.slice(transcript.virtualHistory.start, transcript.virtualHistory.end).map(row => (
{row.msg.kind === 'intro' ? (
{row.msg.info?.version && }
) : row.msg.kind === 'panel' && row.msg.panelData ? (
) : (
)}
))}
{transcript.virtualHistory.bottomSpacer > 0 ? : null}
>
)
})
const ComposerPane = memo(function ComposerPane({
actions,
composer,
status
}: Pick) {
const ui = useStore($uiState)
const isBlocked = useStore($isBlocked)
const sh = (composer.inputBuf[0] ?? composer.input).startsWith('!')
const pw = sh ? 2 : 3
return (
{ui.bgTasks.size > 0 && (
{ui.bgTasks.size} background {ui.bgTasks.size === 1 ? 'task' : 'tasks'} running
)}
{status.showStickyPrompt ? (
↳
{status.stickyPrompt}
) : (
)}
{ui.statusBar && (
)}
{!isBlocked && (
{composer.inputBuf.map((line, i) => (
{i === 0 ? `${ui.theme.brand.prompt} ` : ' '}
{line || ' '}
))}
{sh ? (
$
) : (
{composer.inputBuf.length ? ' ' : `${ui.theme.brand.prompt} `}
)}
)}
{!composer.empty && !ui.sid && ⚕ {ui.status}}
)
})
export const AppLayout = memo(function AppLayout({
actions,
composer,
mouseTracking,
progress,
status,
transcript
}: AppLayoutProps) {
return (
)
})
interface StreamingAssistantProps {
busy: boolean
cols: number
compact?: boolean
detailsMode: DetailsMode
progress: AppLayoutProgressProps
t: Theme
}