import * as React from 'react'; import { ArrowUp, Square } from 'lucide-react'; import { cn } from '@/lib/utils'; /** * PromptInput — the assistant's primary action. * * A textarea rather than an input, because these prompts are sentences: it grows * with the content up to a cap, keeps Enter as send, and leaves Shift+Enter for a * deliberate newline. That combination is what makes typing a real question feel * natural instead of cramped. * * `placeholders` rotate while the field is empty and unfocused, which teaches * the range of what can be asked without spending vertical space on a legend. */ export function PromptInput({ value, onChange, onSubmit, onStop, busy = false, placeholders = ['Ask anything…'], autoFocus = false, align = 'left', className, }) { const textareaRef = React.useRef(null); const [focused, setFocused] = React.useState(false); const [placeholderIndex, setPlaceholderIndex] = React.useState(0); /* Grow to fit the content, up to a cap, then scroll internally. */ React.useEffect(() => { const el = textareaRef.current; if (!el) return; el.style.height = 'auto'; el.style.height = `${Math.min(el.scrollHeight, 148)}px`; }, [value]); /* Rotate the examples only while the field is idle — changing the placeholder under someone who is mid-thought is distracting. The crossfade is done with a real element rather than the native placeholder, which cannot animate. */ const [fading, setFading] = React.useState(false); React.useEffect(() => { if (value || focused || placeholders.length < 2) return undefined; const timer = setInterval(() => { setFading(true); // Swap the text at the midpoint of the fade so neither string is visible // while it changes. setTimeout(() => { setPlaceholderIndex((i) => (i + 1) % placeholders.length); setFading(false); }, 220); }, 4000); return () => clearInterval(timer); }, [value, focused, placeholders.length]); /* A shorter list on a context change must not leave the index out of range. */ React.useEffect(() => { setPlaceholderIndex(0); }, [placeholders]); React.useEffect(() => { if (autoFocus) textareaRef.current?.focus(); }, [autoFocus]); const submit = () => { if (!value.trim() || busy) return; onSubmit(value); }; const onKeyDown = (event) => { if (event.key === 'Enter' && !event.shiftKey) { event.preventDefault(); submit(); } }; const isCenter = align === 'center'; return (
{ e.preventDefault(); submit(); }} className={cn( 'relative flex items-center gap-2 rounded-2xl border bg-surface transition-all duration-base ease-out', 'px-3 py-2', focused ? 'border-krow-blue/50 shadow-[0_8px_28px_-8px_rgba(10,57,223,0.28)] ring-2 ring-krow-blue/15' : 'border-border shadow-md hover:border-krow-blue/30', className )} >