diff --git a/app/src/components/Generation/FloatingGenerateBox.tsx b/app/src/components/Generation/FloatingGenerateBox.tsx index 2d6a91c9..f5e768f3 100644 --- a/app/src/components/Generation/FloatingGenerateBox.tsx +++ b/app/src/components/Generation/FloatingGenerateBox.tsx @@ -34,6 +34,7 @@ export function FloatingGenerateBox({ isPlayerOpen = false, showVoiceSelector = const [isExpanded, setIsExpanded] = useState(false); const [isInstructMode, setIsInstructMode] = useState(false); const containerRef = useRef(null); + const textareaRef = useRef(null); const matchRoute = useMatchRoute(); const isStoriesRoute = matchRoute({ to: '/stories' }); const selectedStoryId = useStoryStore((state) => state.selectedStoryId); @@ -100,6 +101,66 @@ export function FloatingGenerateBox({ isPlayerOpen = false, showVoiceSelector = }; }, [isExpanded]); + // Set first voice as default if none selected + useEffect(() => { + if (!selectedProfileId && profiles && profiles.length > 0) { + setSelectedProfileId(profiles[0].id); + } + }, [selectedProfileId, profiles, setSelectedProfileId]); + + // Get current form value to trigger resize when it changes + const formValue = form.watch(isInstructMode ? 'instruct' : 'text'); + + // Auto-resize textarea based on content (only when expanded) + useEffect(() => { + if (!isExpanded) { + // Reset textarea height after collapse animation completes + const timeoutId = setTimeout(() => { + const textarea = textareaRef.current; + if (textarea) { + textarea.style.height = '32px'; + textarea.style.overflowY = 'hidden'; + } + }, 200); // Wait for animation to complete + return () => clearTimeout(timeoutId); + } + + const textarea = textareaRef.current; + if (!textarea) return; + + const adjustHeight = () => { + textarea.style.height = 'auto'; + const scrollHeight = textarea.scrollHeight; + const minHeight = 100; // Expanded minimum + const maxHeight = 300; // Max height in pixels + const targetHeight = Math.max(minHeight, Math.min(scrollHeight, maxHeight)); + textarea.style.height = `${targetHeight}px`; + + // Show scrollbar if content exceeds max height + if (scrollHeight > maxHeight) { + textarea.style.overflowY = 'auto'; + } else { + textarea.style.overflowY = 'hidden'; + } + }; + + // Small delay to let framer animation complete + const timeoutId = setTimeout(() => { + adjustHeight(); + }, 200); + + // Adjust on mount and when value changes + adjustHeight(); + + // Watch for input changes + textarea.addEventListener('input', adjustHeight); + + return () => { + clearTimeout(timeoutId); + textarea.removeEventListener('input', adjustHeight); + }; + }, [formValue, isInstructMode, isExpanded]); + async function onSubmit(data: Parameters[0]) { await handleSubmit(data, selectedProfileId); } @@ -146,26 +207,42 @@ export function FloatingGenerateBox({ isPlayerOpen = false, showVoiceSelector = render={({ field }) => ( -