Files
resolutionflow/frontend/src/components/script-builder/ScriptBuilderInput.tsx
Michael Chihlas 689776afd9 feat: add Script Builder page with chat UI, code blocks, preview modal, and save dialog
- ScriptCodeBlock: collapsed code preview with syntax highlighting (first 5 lines)
- ScriptBuilderInput: auto-resize chat input with Enter-to-send
- ScriptBuilderChat: message list with markdown rendering and code blocks
- ScriptPreviewModal: fullscreen script viewer with line numbers
- SaveToLibraryDialog: save script with name, description, category, team sharing
- ScriptBuilderPage: language selector, session management, FlowPilot handoff
- Added route, sidebar nav item (fuchsia), and mobile nav entry

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 17:11:32 -04:00

79 lines
2.2 KiB
TypeScript

import { useState, useRef, useCallback, useEffect } from 'react'
import { Send } from 'lucide-react'
import { cn } from '@/lib/utils'
interface ScriptBuilderInputProps {
onSend: (content: string) => void
disabled: boolean
placeholder?: string
}
export function ScriptBuilderInput({
onSend,
disabled,
placeholder = 'Describe the script you need...',
}: ScriptBuilderInputProps) {
const [value, setValue] = useState('')
const textareaRef = useRef<HTMLTextAreaElement>(null)
const adjustHeight = useCallback(() => {
const textarea = textareaRef.current
if (!textarea) return
textarea.style.height = 'auto'
textarea.style.height = `${Math.min(textarea.scrollHeight, 120)}px`
}, [])
useEffect(() => {
adjustHeight()
}, [value, adjustHeight])
const handleSend = () => {
const trimmed = value.trim()
if (!trimmed || disabled) return
onSend(trimmed)
setValue('')
}
const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault()
handleSend()
}
}
const canSend = value.trim().length > 0 && !disabled
return (
<div className="flex items-end gap-2 p-3 border-t" style={{ borderColor: 'var(--glass-border)' }}>
<textarea
ref={textareaRef}
value={value}
onChange={(e) => setValue(e.target.value)}
onKeyDown={handleKeyDown}
placeholder={placeholder}
disabled={disabled}
rows={1}
className={cn(
"flex-1 resize-none rounded-xl px-4 py-2.5 text-sm",
"bg-card border border-border text-foreground placeholder:text-muted-foreground",
"focus:outline-none focus:border-[rgba(6,182,212,0.3)] transition-colors",
"disabled:opacity-50"
)}
style={{ maxHeight: 120 }}
/>
<button
onClick={handleSend}
disabled={!canSend}
className={cn(
"shrink-0 flex items-center justify-center w-10 h-10 rounded-xl transition-all",
canSend
? "bg-gradient-brand text-[#101114] hover:opacity-90 active:scale-[0.97]"
: "bg-[rgba(255,255,255,0.04)] text-[#5a6170] cursor-not-allowed"
)}
>
<Send size={18} />
</button>
</div>
)
}