video-flow-b/components/ai-suggestion-bar.tsx
2025-07-03 05:51:09 +08:00

220 lines
7.7 KiB
TypeScript

import { useState, useRef, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { Sparkles, Send, X, Lightbulb, ChevronUp } from 'lucide-react';
import { GlassIconButton } from "@/components/ui/glass-icon-button";
interface AISuggestionBarProps {
suggestions: string[];
onSuggestionClick: (suggestion: string) => void;
onSubmit: (text: string) => void;
placeholder?: string;
}
export function AISuggestionBar({
suggestions,
onSuggestionClick,
onSubmit,
placeholder = "输入你的想法,或点击预设词条获取 AI 建议..."
}: AISuggestionBarProps) {
const [inputText, setInputText] = useState('');
const [isFocused, setIsFocused] = useState(false);
const [showSuggestions, setShowSuggestions] = useState(false);
const [isCollapsed, setIsCollapsed] = useState(false);
const inputRef = useRef<HTMLTextAreaElement>(null);
// 自动调整输入框高度
useEffect(() => {
if (inputRef.current) {
inputRef.current.style.height = 'auto';
inputRef.current.style.height = `${inputRef.current.scrollHeight}px`;
}
}, [inputText]);
// 处理提交
const handleSubmit = () => {
if (inputText.trim()) {
onSubmit(inputText.trim());
setInputText('');
if (inputRef.current) {
inputRef.current.style.height = 'auto';
}
}
};
// 处理回车提交
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
handleSubmit();
}
};
// 切换折叠状态
const toggleCollapse = () => {
setIsCollapsed(!isCollapsed);
if (isCollapsed) {
// 展开时自动显示建议
setShowSuggestions(true);
}
};
return (
<motion.div
initial={{ y: 100, opacity: 0 }}
animate={{
y: isCollapsed ? 'calc(100% - 10px)' : 0,
opacity: 1,
transition: {
type: "spring",
stiffness: 300,
damping: 30
}
}}
className="fixed bottom-0 left-0 right-0 z-50 bg-gradient-to-t from-[#0C0E11] via-[#0C0E11] to-transparent pb-8"
>
{/* 折叠/展开按钮 */}
<div className="absolute -top-[1rem] left-1/2 -translate-x-1/2" style={{ zIndex: 9 }}>
<motion.div
animate={{ rotate: isCollapsed ? 180 : 0 }}
transition={{ type: "spring", stiffness: 200, damping: 20 }}
>
<GlassIconButton
icon={ChevronUp}
size='sm'
tooltip={isCollapsed ? "展开" : "收起"}
onClick={toggleCollapse}
/>
</motion.div>
</div>
<div className="max-w-5xl mx-auto px-6">
{/* 智能预设词条 英文 */}
<AnimatePresence>
{showSuggestions && !isCollapsed && (
<motion.div
initial={{ height: 0, opacity: 0 }}
animate={{ height: 'auto', opacity: 1 }}
exit={{ height: 0, opacity: 0 }}
transition={{
height: { type: "spring", stiffness: 300, damping: 30 },
opacity: { duration: 0.2 }
}}
className="mb-4 pt-4 px-4 overflow-hidden bg-black/40 rounded-xl backdrop-blur-sm"
>
<motion.div
className="flex items-center gap-3 mb-3"
initial={{ x: -20, opacity: 0 }}
animate={{ x: 0, opacity: 1 }}
transition={{ delay: 0.1 }}
>
<motion.div
animate={{
rotate: [0, 15, -15, 0],
scale: [1, 1.2, 1.2, 1]
}}
transition={{
duration: 1,
repeat: Infinity,
repeatDelay: 2
}}
>
<Lightbulb className="w-4 h-4 text-yellow-500" />
</motion.div>
<span className="text-sm text-white/60">Smart preset tags</span>
</motion.div>
<div className="flex flex-wrap gap-2">
{suggestions.map((suggestion, index) => (
<motion.button
key={suggestion}
initial={{ opacity: 0, scale: 0.8, y: 20 }}
animate={{
opacity: 1,
scale: 1,
y: 0,
transition: {
delay: index * 0.1,
type: "spring",
stiffness: 400,
damping: 25
}
}}
whileHover={{
scale: 1.05,
backgroundColor: "rgba(255, 255, 255, 0.15)"
}}
whileTap={{ scale: 0.95 }}
className="px-3 py-1.5 rounded-full bg-white/5 hover:bg-white/10 backdrop-blur-sm
text-sm text-white/70 hover:text-white transition-colors flex items-center gap-2"
onClick={() => onSuggestionClick(suggestion)}
>
<Sparkles className="w-3 h-3" />
{suggestion}
</motion.button>
))}
</div>
</motion.div>
)}
</AnimatePresence>
{/* 输入区域 */}
<motion.div
className={`
relative rounded-xl bg-white/5 backdrop-blur-sm transition-all duration-300
${isFocused ? 'ring-2 ring-blue-500/50 bg-white/10' : 'hover:bg-white/[0.07]'}
${isCollapsed ? 'opacity-50 hover:opacity-100' : ''}
`}
whileHover={isCollapsed ? { scale: 1.02 } : {}}
onClick={() => isCollapsed && toggleCollapse()}
>
<textarea
ref={inputRef}
value={inputText}
onChange={(e) => setInputText(e.target.value)}
onKeyDown={handleKeyDown}
onFocus={() => {
setIsFocused(true);
setShowSuggestions(true);
if (isCollapsed) {
toggleCollapse();
}
}}
onBlur={() => setIsFocused(false)}
placeholder={isCollapsed ? "点击展开..." : placeholder}
className="w-full resize-none bg-transparent border-none px-4 py-3 text-white placeholder:text-white/40
focus:outline-none min-h-[52px] max-h-[150px] pr-[100px]"
rows={1}
disabled={isCollapsed}
/>
{/* 操作按钮 */}
<div className="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-2">
<motion.button
className={`
p-2 rounded-lg transition-colors
${showSuggestions ? 'bg-white/10 text-white' : 'text-white/40 hover:text-white/60'}
`}
onClick={() => !isCollapsed && setShowSuggestions(!showSuggestions)}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
disabled={isCollapsed}
>
{showSuggestions ? <X className="w-5 h-5" /> : <Sparkles className="w-5 h-5" />}
</motion.button>
<motion.button
className={`
p-2 rounded-lg transition-colors
${inputText.trim() ? 'bg-blue-500 text-white' : 'bg-white/5 text-white/20'}
`}
onClick={handleSubmit}
disabled={!inputText.trim() || isCollapsed}
whileHover={inputText.trim() ? { scale: 1.1 } : {}}
whileTap={inputText.trim() ? { scale: 0.9 } : {}}
>
<Send className="w-5 h-5" />
</motion.button>
</div>
</motion.div>
</div>
</motion.div>
);
}