aura-ai
Version:
AI-powered marketing strategist CLI tool for developers
154 lines (143 loc) • 4.24 kB
JSX
import React, { useState, useEffect, useRef } from 'react'
import { Box, Text, useInput, Newline } from 'ink'
import TextInput from 'ink-text-input'
import SelectInput from 'ink-select-input'
/**
* Simplified composite input component - show menu OR text input, not both
*/
const CompositeInput = ({
options = [],
onSubmit,
placeholder = 'Type your answer...',
initialValue = '',
onBack = null,
isReview = false,
onPrevious = null,
showTypeYourAnswer = true,
directSubmitValues = [],
}) => {
const [mode, setMode] = useState(options.length > 0 ? 'select' : 'text')
const [inputValue, setInputValue] = useState('')
const [exitPrompt, setExitPrompt] = useState(false)
const lastCtrlC = useRef(0)
// Handle Ctrl+C to exit init and return to main menu
useInput((input, key) => {
if (key.ctrl && input === 'c') {
const now = Date.now()
if (now - lastCtrlC.current < 2000) {
// Double Ctrl+C within 2 seconds - exit to main menu
if (onBack) onBack()
} else {
setExitPrompt(true)
lastCtrlC.current = now
setTimeout(() => {
setExitPrompt(false)
}, 2000)
}
}
})
// Reset when options or initial value changes
useEffect(() => {
setMode(options.length > 0 ? 'select' : 'text')
setInputValue(initialValue)
setExitPrompt(false)
}, [options, initialValue])
// Prepare select items with optional "Type your answer..." as last option
const selectItems =
options.length > 0
? [
...options.map(opt => ({
label: opt.label,
value: opt.value,
})),
...(showTypeYourAnswer
? [
{
label: 'Type your answer...',
value: '__custom_input__',
},
]
: []),
]
: []
// Handle selection from menu
const handleSelect = item => {
if (item.value === '__custom_input__') {
// For "Type your answer..." option, switch to text mode
setMode('text')
setInputValue('')
} else if (directSubmitValues.includes(item.value)) {
// For direct submit values, submit immediately without text mode
onSubmit(item.value)
} else {
// For other options, switch to text mode and pre-fill
setMode('text')
setInputValue(item.label)
}
}
// Handle text input submission
const handleTextSubmit = value => {
if (value.trim()) {
// Handle /back command to go to previous question
if (value.toLowerCase() === '/back' && !isReview && onPrevious) {
onPrevious()
return
}
onSubmit(value)
}
}
return (
<Box flexDirection='column'>
{/* Show either select menu or text input */}
<Box flexDirection='column'>
{mode === 'select' && options.length > 0 ? (
<Box
paddingX={1}
paddingY={0}
borderColor='whiteBright'
borderStyle='round'
width={'100%'}
>
<SelectInput items={selectItems} onSelect={handleSelect} />
</Box>
) : (
<Box
paddingX={1}
paddingY={0}
borderColor='whiteBright'
borderStyle='round'
width={'100%'}
>
<Text color='blue'>{'> '}</Text>
<TextInput
value={inputValue}
onChange={setInputValue}
onSubmit={handleTextSubmit}
placeholder={placeholder}
focus={true}
/>
</Box>
)}
</Box>
{/* Help text */}
<Box marginTop={1}>
<Text dimColor fontSize={12}>
{mode === 'select' ? (
<>Use ↑↓ to navigate, Enter to select</>
) : isReview ? (
<>Use /1, /2, etc. to jump to specific questions</>
) : (
<>Use /back to go to previous question</>
)}
</Text>
</Box>
{/* Exit prompt */}
{exitPrompt && (
<Box marginTop={1}>
<Text color='yellow'>Press Ctrl+C again to return to main menu</Text>
</Box>
)}
</Box>
)
}
export default CompositeInput