import { useState, useRef } from "react"; import { motion } from "framer-motion"; import { Button } from "@/components/ui/button"; import { Sparkles, X, Plus } from 'lucide-react'; import { cn } from "@/public/lib/utils"; import ContentEditable from 'react-contenteditable'; interface CharacterAttribute { key: string; label: string; value: string; type: 'text' | 'number' | 'select'; options?: string[]; } interface CharacterEditorProps { initialDescription?: string; onDescriptionChange?: (description: string) => void; onAttributesChange?: (attributes: CharacterAttribute[]) => void; } const mockParse = (text: string): CharacterAttribute[] => { // 模拟结构化解析结果 return [ { key: "age", label: "年龄", value: "20", type: "number" }, { key: "gender", label: "性别", value: "女性", type: "select", options: ["男性", "女性", "其他"] }, { key: "hair", label: "发型", value: "银白短发", type: "text" }, { key: "race", label: "种族", value: "精灵", type: "text" }, { key: "skin", label: "肤色", value: "白皙", type: "text" }, { key: "build", label: "体型", value: "高挑", type: "text" }, { key: "costume", label: "服装", value: "白色连衣裙", type: "text" }, ]; }; export default function CharacterEditor({ initialDescription = "一个银白短发的精灵女性,大约20岁,肤色白皙,身材高挑,身着白色连衣裙", onDescriptionChange, onAttributesChange, }: CharacterEditorProps) { const [inputText, setInputText] = useState(initialDescription); const [isOptimizing, setIsOptimizing] = useState(false); const [customTags, setCustomTags] = useState([]); const [newTag, setNewTag] = useState(""); const attributesRef = useRef(mockParse(initialDescription)); const contentEditableRef = useRef(null); const handleTextChange = (e: { target: { value: string } }) => { // 移除 HTML 标签,保留换行 const value = e.target.value; setInputText(value); onDescriptionChange?.(value); }; // 格式化文本为 HTML const formatTextToHtml = (text: string) => { return text .split('\n') .map(line => line || '
') .join('
'); }; const handleSmartPolish = async () => { setIsOptimizing(true); try { const polishedText = "一位拥有银白短发、白皙肌肤的高挑精灵少女,年龄约二十岁,气质神秘优雅。举手投足间散发着独特的精灵族气质,眼神中透露出智慧与沧桑。"; setInputText(polishedText); attributesRef.current = mockParse(polishedText); onDescriptionChange?.(polishedText); onAttributesChange?.(attributesRef.current); } finally { setIsOptimizing(false); } }; const handleAttributeChange = (attr: CharacterAttribute, newValue: string) => { // 移除 HTML 标签 newValue = newValue.replace(/<[^>]*>/g, ''); // 更新描述文本 let newText = inputText; if (attr.type === "number" && attr.key === "age") { newText = newText.replace(/\d+岁/, `${newValue}岁`); } else { newText = newText.replace(new RegExp(attr.value, 'g'), newValue); } // 更新属性值 const newAttr = { ...attr, value: newValue }; attributesRef.current = attributesRef.current.map(a => a.key === attr.key ? newAttr : a ); setInputText(newText); onDescriptionChange?.(newText); onAttributesChange?.(attributesRef.current); }; return (
{/* 自由输入区域 */}
{/* 智能润色按钮 */} {isOptimizing ? "优化中..." : "智能优化"}
{/* 结构化属性标签 */}
{attributesRef.current.map((attr) => ( {attr.label}: handleAttributeChange(attr, e.target.value)} className="text-sm text-white/90 min-w-[1em] focus:outline-none border-b border-transparent focus:border-white/30 hover:border-white/20" onKeyDown={(e) => { if (e.key === 'Enter') { e.preventDefault(); (e.target as HTMLElement).blur(); } }} /> ))}
{/* 自定义标签区域 */}
{customTags.map((tag) => ( {tag} ))}
setNewTag(e.target.value)} onKeyPress={(e) => { if (e.key === 'Enter' && newTag.trim()) { setCustomTags(tags => [...tags, newTag.trim()]); setInputText((text: string) => text + (text.endsWith("。") ? "" : ",") + newTag.trim()); setNewTag(""); } }} placeholder="添加自定义标签..." className="flex-1 px-3 py-2 bg-white/5 border-none rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 placeholder:text-white/30" />
); }