import React, { forwardRef, useRef, useState } from "react"; import { useDeepCompareEffect } from "@/hooks/useDeepCompareEffect"; import { Plus, X, UserRoundPlus, MessageCirclePlus, MessageCircleMore, ClipboardType } from "lucide-react"; import ShotEditor from "./ShotEditor"; import { toast } from "sonner"; import { TextToShotAdapter } from "@/app/service/adapter/textToShot"; interface Shot { name: string; shotDescContent: any[]; shotDialogsContent: any[]; } interface CharacterToken { type: 'characterToken'; attrs: { name: string; gender: string; age: string; avatar: string; }; } const createEmptyShot = (): Shot => ({ name: `shot${Date.now()}`, shotDescContent: [{ type: 'paragraph', content: [{ type: 'text', text: 'Add shot description here...' }] }], shotDialogsContent: [{ type: 'paragraph', content: [{ type: 'text', text: 'Add shot dialogue here...' }] }] }); interface ShotsEditorProps { roles: any[]; shotInfo: any[]; style?: React.CSSProperties; } export const ShotsEditor = forwardRef(function ShotsEditor({ roles, shotInfo, style }, ref) { const [currentShotIndex, setCurrentShotIndex] = useState(0); const [shots, setShots] = useState([]); const descEditorRef = useRef(null); const dialogEditorRef = useRef(null); useDeepCompareEffect(() => { if (shotInfo) { console.log('-==========shotInfo===========-', shotInfo); const shots = shotInfo.map((shot) => { return TextToShotAdapter.fromLensType(shot, roles); }); console.log('-==========shots===========-', shots); setShots(shots as Shot[]); } }, [shotInfo, roles]); const handleDescContentChange = (content: any) => { const shot = shots[currentShotIndex]; shot.shotDescContent = content; setShots(prevShots => prevShots.map((item, index) => index === currentShotIndex ? shot : item ) ); } const handleDialogContentChange = (content: any) => { const shot = shots[currentShotIndex]; shot.shotDialogsContent = content; setShots(prevShots => prevShots.map((item, index) => index === currentShotIndex ? shot : item ) ); } const handleShotTabClick = (index: number) => { setCurrentShotIndex(index); } const getShotInfo = () => { console.log('-==========getShotInfo shots===========-', shots); const shotInfo = shots.map((shot) => { return TextToShotAdapter.toLensType(shot); }); return shotInfo; } const addShot = () => { if (shots.length > 3) { toast.error('No more than 4 shots', { duration: 3000, position: 'top-center', richColors: true, }); return; } const newShot = createEmptyShot(); setShots([...shots, newShot]); // 自动切换到新创建的分镜 setCurrentShotIndex(shots.length); }; const handleDeleteShot = (index: number) => { if (shots.length <= 1) return; // 保留最后一个分镜 const newShots = shots.filter((_, i) => i !== index); setShots(newShots); // onShotsChange(newShots); // 如果删除的是当前选中的分镜,或者删除的是最后一个分镜 if (currentShotIndex === index || currentShotIndex >= newShots.length) { setCurrentShotIndex(Math.max(0, newShots.length - 1)); } else if (currentShotIndex > index) { // 如果删除的分镜在当前选中分镜之前,需要更新索引 setCurrentShotIndex(currentShotIndex - 1); } }; const handleAddCharacterToDesc = () => { if (!descEditorRef.current) return; // 创建一个默认角色Token const defaultCharacter: CharacterToken = { type: 'characterToken', attrs: { name: 'Select Role', gender: '', age: '', avatar: '' } }; // 在当前位置插入角色Token descEditorRef.current.insertCharacter(defaultCharacter); }; const handleAddCharacterToDialog = () => { if (!dialogEditorRef.current) return; // 创建一个默认角色Token const defaultCharacter: CharacterToken = { type: 'characterToken', attrs: { name: 'Select Role', gender: '', age: '', avatar: '' } }; // 在当前位置插入角色Token dialogEditorRef.current.insertCharacter(defaultCharacter); }; const handleAddNewDialog = () => { if (!dialogEditorRef.current) return; // 创建一个新的对话行 const newDialog = { type: 'paragraph', content: [ { type: 'characterToken', attrs: { name: 'Select Role', gender: '', age: '', avatar: '' } }, { type: 'text', text: ' says:' } ] }; // 在编辑器末尾添加新对话 dialogEditorRef.current.editor?.commands.focus('end'); dialogEditorRef.current.insertContent([ { type: 'text', text: '\n' }, newDialog ]); }; // 暴露方法给父组件 React.useImperativeHandle(ref, () => ({ addShot, getShotInfo })); return (
{/* 分镜标签(可删除)、新增分镜标签 */}
{shots.map((shot, index) => (
handleShotTabClick(index)} > Shot {index + 1} {shots.length > 1 && ( )}
))}
{/* 分镜内容 */} {shots[currentShotIndex] && (
{/* 分镜描述 添加角色 */}
Shot Description
{/* 分镜描述内容 可视化编辑 */} {}} roles={roles} placeholder="Add shot description here..." onChangeContent={(content) => {handleDescContentChange(content)}} />
{/* 分镜对话 添加角色 添加对话 */}
Shot Dialogue
{/* 分镜对话内容 可视化编辑 */} {}} roles={roles} placeholder="Add shot dialogue here..." onChangeContent={(content) => {handleDialogContentChange(content)}} />
)}
); });