aura-ai
Version:
AI-powered marketing strategist CLI tool for developers
146 lines (135 loc) • 4.32 kB
JSX
import React, { useState, useEffect } from 'react'
import { Box, Newline, Spacer, Text, useInput } from 'ink'
import TextInput from 'ink-text-input'
/**
* Command input component with autocomplete suggestions like Claude Code
*/
const CommandInput = ({
value,
onChange,
onSubmit,
placeholder = 'Type a command or message...',
commands = [],
exitPrompt = false,
onClearInput = null,
onExit = null,
}) => {
const [suggestions, setSuggestions] = useState([])
const [showSuggestions, setShowSuggestions] = useState(false)
const [selectedIndex, setSelectedIndex] = useState(0)
// Handle keyboard input for autocomplete and Ctrl+C
useInput((input, key) => {
// Handle Ctrl+C
if (key.ctrl && input === 'c') {
if (value.length > 0 && onClearInput) {
onClearInput()
return
}
if (onExit) {
onExit()
return
}
}
// Handle autocomplete when suggestions are shown
if (showSuggestions && suggestions.length > 0) {
if (key.upArrow) {
setSelectedIndex(prev => Math.max(0, prev - 1))
return
}
if (key.downArrow) {
setSelectedIndex(prev => Math.min(suggestions.length - 1, prev + 1))
return
}
if (key.tab) {
// Auto-complete with selected suggestion
const selectedCmd = suggestions[selectedIndex]
if (selectedCmd) {
onChange(`/${selectedCmd.name}`)
setShowSuggestions(false)
}
return
}
}
})
// Update suggestions when input value changes
useEffect(() => {
if (value.startsWith('/')) {
const query = value.substring(1).toLowerCase()
const filteredCommands = commands.filter(
cmd =>
cmd.name.toLowerCase().includes(query) || cmd.description.toLowerCase().includes(query)
)
setSuggestions(filteredCommands)
setShowSuggestions(filteredCommands.length > 0)
setSelectedIndex(0) // Reset selection
} else {
setSuggestions([])
setShowSuggestions(false)
setSelectedIndex(0)
}
}, [value, commands])
const handleSubmit = inputValue => {
// If there are suggestions showing and user presses Enter,
// auto-complete with the selected suggestion
if (showSuggestions && suggestions.length > 0) {
const selectedCmd = suggestions[selectedIndex]
if (selectedCmd) {
setShowSuggestions(false)
onSubmit(`/${selectedCmd.name}`)
return
}
}
// Normal submit
setShowSuggestions(false)
onSubmit(inputValue)
}
return (
<Box flexDirection='column' width='100%'>
{/* Input field */}
<Box paddingX={1} paddingY={0} borderColor='whiteBright' borderStyle='round' width={'100%'}>
<Text color='blue'>{'> '}</Text>
<TextInput
value={value}
onChange={onChange}
onSubmit={handleSubmit}
placeholder={placeholder}
focus={true}
/>
</Box>
{/* Autocomplete suggestions */}
{showSuggestions && suggestions.length > 0 && (
<Box flexDirection='column' marginTop={1} marginBottom={1} paddingLeft={2}>
{suggestions.map((cmd, index) => (
<Box flexDirection='row' flexWrap='wrap' key={index}>
<Box width={16}>
<Text color={index === selectedIndex ? 'blueBright' : 'gray'}>
<Text color={index === selectedIndex ? 'blueBright' : 'gray'}>/{cmd.name}</Text>
</Text>
</Box>
<Box paddingLeft={3}>
<Text color={index === selectedIndex ? 'blueBright' : 'gray'}>
{cmd.description}
</Text>
</Box>
</Box>
))}
</Box>
)}
{/* Help text with integrated Ctrl+C status */}
<Box width='100%'>
<Text dimColor>
{exitPrompt ? (
<Text color='yellow'>Press Ctrl+C again to exit</Text>
) : value.startsWith('/') ? (
<>Press Enter to continue</>
) : (
<>Type / for commands</>
)}
</Text>
<Spacer />
<Text dimColor>{!exitPrompt && <> Ctrl+C to {value ? 'clear input' : 'exit'}</>}</Text>
</Box>
</Box>
)
}
export default CommandInput