feat(flowpilot): add always-visible message bar, remove hidden free text escape hatch

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-21 16:49:46 -04:00
parent ad64f26883
commit ec7df50064
3 changed files with 85 additions and 52 deletions

View File

@@ -0,0 +1,75 @@
import { useState, useRef, useCallback } from 'react'
import { Send } from 'lucide-react'
import { cn } from '@/lib/utils'
import type { StepResponseRequest } from '@/types/ai-session'
interface FlowPilotMessageBarProps {
onRespond: (response: StepResponseRequest) => void
disabled?: boolean
isProcessing?: boolean
}
export function FlowPilotMessageBar({ onRespond, disabled = false, isProcessing = false }: FlowPilotMessageBarProps) {
const [message, setMessage] = useState('')
const textareaRef = useRef<HTMLTextAreaElement>(null)
const isDisabled = disabled || isProcessing
const handleSubmit = useCallback(() => {
const trimmed = message.trim()
if (!trimmed || isDisabled) return
onRespond({ free_text_input: trimmed })
setMessage('')
if (textareaRef.current) {
textareaRef.current.style.height = 'auto'
}
}, [message, isDisabled, onRespond])
const handleKeyDown = useCallback((e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSubmit()
}
}, [handleSubmit])
const handleInput = useCallback((e: React.ChangeEvent<HTMLTextAreaElement>) => {
setMessage(e.target.value)
const textarea = e.target
textarea.style.height = 'auto'
textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`
}, [])
return (
<div className="px-3 sm:px-4 lg:px-5 pb-3">
<div className={cn(
'flex items-end gap-2 rounded-xl border bg-card p-2 transition-colors',
isDisabled
? 'border-border/50 opacity-50'
: 'border-border focus-within:border-[rgba(6,182,212,0.3)]'
)}>
<textarea
ref={textareaRef}
value={message}
onChange={handleInput}
onKeyDown={handleKeyDown}
placeholder={isProcessing ? 'FlowPilot is thinking...' : 'Type a message...'}
disabled={isDisabled}
rows={1}
className="flex-1 resize-none bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed py-1.5 px-2"
/>
<button
onClick={handleSubmit}
disabled={isDisabled || !message.trim()}
className={cn(
'flex h-8 w-8 shrink-0 items-center justify-center rounded-lg transition-all',
message.trim() && !isDisabled
? 'bg-gradient-brand text-[#101114] hover:opacity-90 active:scale-[0.97]'
: 'bg-[rgba(255,255,255,0.04)] text-muted-foreground cursor-not-allowed'
)}
>
<Send size={14} />
</button>
</div>
</div>
)
}