/** * Dialog components for modals, confirmations, and input dialogs. */ import React, { useRef, useState } from 'react'; import { Box, Text, useInput, measureElement } from 'ink'; import TextInput from './TextInput.js'; import { colors } from '../theme.js'; /** * Base dialog wrapper props. */ interface DialogWrapperProps { /** Dialog title */ title: string; /** Border color */ borderColor?: string; /** Dialog content */ children: React.ReactNode; /** Dialog width */ width?: number; /** Dialog Background Color */ backgroundColor?: string; } export function DialogWrapper({ title, borderColor = colors.primary, children, width = 60, backgroundColor = colors.bg, }: DialogWrapperProps): React.ReactElement { const ref = useRef(null); const [height, setHeight] = useState(null); // measure after render React.useLayoutEffect(() => { if (ref.current) { const { height } = measureElement(ref.current); setHeight(height); } }, [children, title, width]); return ( {/* Opaque backing layer */} {height !== null && ( {Array.from({ length: height }).map((_, i) => ( {' '.repeat(width)} ))} )} {/* Actual dialog */} {title} {children} ); } /** * Props for InputDialog component. */ interface InputDialogProps { /** Dialog title */ title: string; /** Input prompt/label */ prompt: string; /** Initial value */ initialValue?: string; /** Placeholder text */ placeholder?: string; /** Submit handler */ onSubmit: (value: string) => void; /** Cancel handler */ onCancel: () => void; /** Whether dialog is visible/active */ isActive?: boolean; } /** * Input dialog for getting text input from user. */ export function InputDialog({ title, prompt, initialValue = '', placeholder, onSubmit, onCancel, isActive = true, }: InputDialogProps): React.ReactElement { const [value, setValue] = useState(initialValue); useInput((input, key) => { if (!isActive) return; if (key.escape) { onCancel(); } }, { isActive }); const handleSubmit = (val: string) => { onSubmit(val); }; return ( {prompt} Enter to submit • Esc to cancel ); } /** * Props for ConfirmDialog component. */ interface ConfirmDialogProps { /** Dialog title */ title: string; /** Confirmation message */ message: string; /** Confirm handler */ onConfirm: () => void; /** Cancel handler */ onCancel: () => void; /** Whether dialog is visible/active */ isActive?: boolean; /** Confirm button label */ confirmLabel?: string; /** Cancel button label */ cancelLabel?: string; } /** * Confirmation dialog with Yes/No options. */ export function ConfirmDialog({ title, message, onConfirm, onCancel, isActive = true, confirmLabel = 'Yes', cancelLabel = 'No', }: ConfirmDialogProps): React.ReactElement { const [selected, setSelected] = useState<'confirm' | 'cancel'>('confirm'); useInput((input, key) => { if (!isActive) return; if (key.leftArrow || key.rightArrow || key.tab) { setSelected(prev => prev === 'confirm' ? 'cancel' : 'confirm'); } else if (key.return) { if (selected === 'confirm') { onConfirm(); } else { onCancel(); } } else if (key.escape || input === 'n' || input === 'N') { onCancel(); } else if (input === 'y' || input === 'Y') { onConfirm(); } }, { isActive }); return ( {message} {` ${confirmLabel} `} {` ${cancelLabel} `} Y/N or Tab to switch • Enter to select ); } /** * Props for MessageDialog component. */ interface MessageDialogProps { /** Dialog title */ title: string; /** Message content */ message: string; /** Close handler */ onClose: () => void; /** Dialog type for styling */ type?: 'info' | 'error' | 'success'; /** Whether dialog is visible/active */ isActive?: boolean; } /** * Simple message dialog (info, error, success). */ export function MessageDialog({ title, message, onClose, type = 'info', isActive = true, }: MessageDialogProps): React.ReactElement { useInput((input, key) => { if (!isActive) return; if (key.return || key.escape) { onClose(); } }, { isActive }); const borderColor = type === 'error' ? colors.error : type === 'success' ? colors.success : colors.info; const icon = type === 'error' ? '✗' : type === 'success' ? '✓' : 'ℹ'; return ( {message} Press Enter or Esc to close ); }