import React, { useState, useCallback, useEffect } from 'react'; import { EditorContent, useEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import { motion } from "framer-motion"; import { CharacterTokenExtension } from './CharacterToken'; import { ShotTitle } from './ShotTitle'; import { ReadonlyText } from './ReadonlyText'; import { Sparkles } from 'lucide-react'; import { toast } from 'sonner'; const initialContent = { type: 'doc', content: [ { type: 'shotTitle', attrs: { title: `分镜1` }, }, { type: 'paragraph', content: [ { type: 'text', text: '镜头聚焦在' }, { type: 'characterToken', attrs: { name: '"沙利"·沙利文中士', gender: '男', age: '28', avatar: 'https://i.pravatar.cc/40?u=z3' }}, { type: 'text', text: ' 愤怒的脸上,他被压制住了。他发出一声绝望的吼叫,盖过了枪声。' }, ] }, { type: 'paragraph', content: [ { type: 'shotTitle', attrs: { title: `对话` } }, { type: 'characterToken', attrs: { name: '"沙利"·沙利文中士', gender: '男', age: '28', avatar: 'https://i.pravatar.cc/40?u=z3' }}, { type: 'text', text: ':' }, { type: 'text', text: '掩护!趴下!' } ] }, { type: 'shotTitle', attrs: { title: `分镜2` }, }, { type: 'paragraph', content: [ { type: 'characterToken', attrs: { name: '李四', gender: '女', age: '26', avatar: 'https://i.pravatar.cc/40?u=l4' }}, { type: 'text', text: ' 微微低头,没有说话。' } ] } ] }; interface ShotEditorProps { content: any[]; roles?: any[]; onCharacterClick?: (attrs: any) => void; } declare module '@tiptap/core' { interface Commands { characterToken: { setCharacterToken: (attrs: any) => ReturnType; } } } interface CharacterToken { type: 'characterToken'; attrs: { name: string; gender: string; age: string; avatar: string; }; } interface EditorRef { editor: any; insertCharacter: (character: CharacterToken) => void; insertContent: (content: any) => void; } const ShotEditor = React.forwardRef( function ShotEditor({ content, onCharacterClick, roles }, ref) { const [segments, setSegments] = useState(content); const [isOptimizing, setIsOptimizing] = useState(false); const handleSmartPolish = () => { setIsOptimizing(true); setTimeout(() => { setIsOptimizing(false); }, 3000); }; const editor = useEditor({ extensions: [ StarterKit, CharacterTokenExtension.configure({ roles }), ShotTitle, ReadonlyText, ], content: { type: 'doc', content: segments }, editorProps: { attributes: { class: 'prose prose-invert max-w-none focus:outline-none' } }, immediatelyRender: false, onCreate: ({ editor }) => { editor.setOptions({ editable: true }) }, }) // 暴露方法给父组件 React.useImperativeHandle(ref, () => ({ editor, insertCharacter: (character: CharacterToken) => { editor?.commands.insertContent([ { type: 'text', text: ' ' }, character, { type: 'text', text: ' ' } ]); }, insertContent: (content: any) => { editor?.commands.insertContent(content); } })); if (!editor) { return null } return ( ) } ); export default ShotEditor;