/** * @license * Copyright 2025 Vybestack LLC * SPDX-License-Identifier: Apache-2.0 */ import type React from 'react'; import { useCallback, useEffect, useMemo } from 'react'; import { Box, Text } from 'ink'; import { useTextBuffer } from '../shared/text-buffer.js'; import type { Direction } from '../shared/buffer-types.js'; import { logicalPosToOffset } from '../shared/buffer-operations.js'; import { Colors } from '../../colors.js'; import { useKeypress } from '../../hooks/useKeypress.js'; import { useTerminalSize } from '../../hooks/useTerminalSize.js'; import type { SubagentInfo } from './types.js'; import { getBorderStyle } from '../../contexts/UnicodeRenderingContext.js'; interface SubagentShowViewProps { subagent: SubagentInfo; onEdit: () => void; onBack: () => void; isFocused?: boolean; } function usePromptScroller(promptBuffer: ReturnType) { const { moveToOffset, move } = promptBuffer; return useCallback( (dir: Direction, amount: number) => { const lineCount = Math.max(1, Math.floor(amount)); if (lineCount === 1 || (dir !== 'up' && dir !== 'down')) { move(dir); return; } if (promptBuffer.allVisualLines.length === 0) { return; } const [currentVisualRow, currentVisualCol] = promptBuffer.visualCursor; const targetVisualRow = dir === 'down' ? Math.min( promptBuffer.allVisualLines.length - 1, currentVisualRow + lineCount, ) : Math.max(0, currentVisualRow - lineCount); const targetVisualMap = promptBuffer.visualToLogicalMap[targetVisualRow]; if (Object.is(targetVisualMap, undefined)) { return; } const [targetLogicalRow, targetLogicalStartCol] = targetVisualMap; const targetVisualLine = promptBuffer.allVisualLines[targetVisualRow] ?? ''; const targetVisualLineLength = Array.from(targetVisualLine).length; const targetLogicalCol = targetLogicalStartCol + Math.min(currentVisualCol, targetVisualLineLength); moveToOffset( logicalPosToOffset( promptBuffer.lines, targetLogicalRow, targetLogicalCol, ), ); }, [ move, moveToOffset, promptBuffer.allVisualLines, promptBuffer.lines, promptBuffer.visualCursor, promptBuffer.visualToLogicalMap, ], ); } function PromptViewport({ promptBuffer, promptViewportHeight, }: { promptBuffer: ReturnType; promptViewportHeight: number; }) { const visibleLines = promptBuffer.viewportVisualLines; const totalVisualLines = promptBuffer.allVisualLines.length; const startLine = totalVisualLines > 0 ? promptBuffer.visualScrollRow + 1 : 0; const endLine = totalVisualLines > 0 ? Math.min(startLine + visibleLines.length - 1, totalVisualLines) : 0; return ( System Prompt (lines {startLine}-{endLine} of {totalVisualLines}) {visibleLines.length > 0 ? ( visibleLines.map((line, idx) => ( {line || ' '} )) ) : ( )} ); } function useShowViewKeyHandler( promptBuffer: ReturnType, pageJump: number, onEdit: () => void, onBack: () => void, isFocused: boolean, ) { const { moveToOffset } = promptBuffer; const movePrompt = usePromptScroller(promptBuffer); useKeypress( (key) => { if (key.name === 'escape') { onBack(); return; } if (key.sequence === 'e') { onEdit(); return; } const isUp = key.name === 'up' || key.sequence === 'k'; const isDown = key.name === 'down' || key.sequence === 'j'; if (isUp) { movePrompt('up', 1); return; } if (isDown) { movePrompt('down', 1); return; } if (key.name === 'pageup') { movePrompt('up', pageJump); return; } if (key.name === 'pagedown') { movePrompt('down', pageJump); return; } if (key.name === 'home' || key.sequence === 'g') { moveToOffset(0); return; } if (key.name === 'end' || key.sequence === 'G') { moveToOffset(promptBuffer.text.length); } }, { isActive: isFocused }, ); } export const SubagentShowView: React.FC = ({ subagent, onEdit, onBack, isFocused = true, }) => { const { rows: terminalRows, columns: terminalColumns } = useTerminalSize(); const NON_PROMPT_HEIGHT = 14; const promptViewportHeight = Math.max( 4, Math.min(12, terminalRows - NON_PROMPT_HEIGHT), ); const promptViewportWidth = Math.max(20, terminalColumns - 24); const pageJump = Math.max(3, Math.floor(promptViewportHeight * 0.75)); const promptBuffer = useTextBuffer({ initialText: subagent.systemPrompt, viewport: { width: promptViewportWidth, height: promptViewportHeight }, isValidPath: () => false, }); const { setText, moveToOffset } = promptBuffer; useEffect(() => { setText(subagent.systemPrompt); moveToOffset(0); }, [setText, moveToOffset, subagent.name, subagent.systemPrompt]); useShowViewKeyHandler(promptBuffer, pageJump, onEdit, onBack, isFocused); const profileDetails = useMemo(() => { const parts = [subagent.profile]; if (subagent.profileInfo?.model) { parts.push(subagent.profileInfo.model); } return parts.join(' • '); }, [subagent.profile, subagent.profileInfo?.model]); return ( {subagent.name} Profile: {profileDetails} [↑↓/j/k] Scroll [PgUp/PgDn] Jump [Home/End] [e] Edit [ESC] Back ); };