From 5e6ead0f0078c976a78af381642c6f63c8a66fd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8C=97=E6=9E=B3?= <7854742+wang_rumeng@user.noreply.gitee.com> Date: Sun, 10 Aug 2025 18:39:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=96=87=E6=9C=AC=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=E9=80=82=E9=85=8D=E5=99=A8=EF=BC=8C=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E5=86=85=E5=AE=B9=E5=A4=84=E7=90=86=E9=80=BB?= =?UTF-8?q?=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E8=A7=92=E8=89=B2=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E7=9A=84=E6=AD=A3=E7=A1=AE=E8=AE=B0=E5=BD=95=E3=80=82?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=88=86=E9=95=9C=E7=BC=96=E8=BE=91=E5=99=A8?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E5=86=85=E5=AE=B9=E5=8F=98=E5=8C=96?= =?UTF-8?q?=E7=9A=84=E5=9B=9E=E8=B0=83=EF=BC=8C=E6=94=B9=E8=BF=9B=E5=88=86?= =?UTF-8?q?=E9=95=9C=E6=8F=8F=E8=BF=B0=E5=92=8C=E5=AF=B9=E8=AF=9D=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=9A=84=E5=8A=A8=E6=80=81=E5=8A=A0=E8=BD=BD=E3=80=82?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E8=A7=86=E9=A2=91=E6=AE=B5=E7=94=9F=E6=88=90?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E7=A1=AE=E4=BF=9D=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E7=BC=96=E8=BE=91=E6=95=B0=E6=8D=AE=E7=9A=84=E5=87=86=E7=A1=AE?= =?UTF-8?q?=E6=80=A7=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/service/adapter/textToShot.ts | 27 +++++++----- components/ui/shot-editor/ShotEditor.tsx | 27 +++++++----- components/ui/shot-editor/ShotsEditor.tsx | 54 ++++++++++++++--------- components/ui/shot-tab-content.tsx | 2 +- 4 files changed, 67 insertions(+), 43 deletions(-) diff --git a/app/service/adapter/textToShot.ts b/app/service/adapter/textToShot.ts index 828f012..3c6d5ca 100644 --- a/app/service/adapter/textToShot.ts +++ b/app/service/adapter/textToShot.ts @@ -190,18 +190,25 @@ export class TextToShotAdapter { shotData.shotDialogsContent.forEach(paragraph => { let dialogRoleName = ''; let dialogContent = ''; + let firstFindRole = false; // 遍历段落内容 - paragraph.content.forEach((node, index) => { - if (node.type === 'characterToken') { - // 记录说话角色的名称 - index === 0 && (dialogRoleName = node.attrs.name); - index !== 0 && (dialogContent += node.attrs.name); - } else if (node.type === 'text') { - // 累积对话内容 - dialogContent += node.text; - } - }); + if (paragraph.content) { + paragraph.content.forEach((node, index) => { + if (node.type === 'characterToken') { + // 记录说话角色的名称 + if (!firstFindRole) { + dialogRoleName = node.attrs.name; + firstFindRole = true; + } else { + dialogContent += node.attrs.name; + } + } else if (node.type === 'text') { + // 累积对话内容 + dialogContent += node.text; + } + }); + } // 如果有角色名和对话内容,添加到结果中 if (dialogRoleName && dialogContent) { diff --git a/components/ui/shot-editor/ShotEditor.tsx b/components/ui/shot-editor/ShotEditor.tsx index 0d7592e..883f6da 100644 --- a/components/ui/shot-editor/ShotEditor.tsx +++ b/components/ui/shot-editor/ShotEditor.tsx @@ -1,4 +1,5 @@ import React, { useState, useCallback, useEffect } from 'react'; +import { flushSync } from 'react-dom'; import { EditorContent, useEditor } from '@tiptap/react'; import StarterKit from '@tiptap/starter-kit'; import Placeholder from '@tiptap/extension-placeholder' @@ -12,6 +13,7 @@ interface ShotEditorProps { roles?: any[]; onCharacterClick?: (attrs: any) => void; placeholder?: string; + onChangeContent?: (content: any) => void; } declare module '@tiptap/core' { @@ -36,14 +38,17 @@ interface EditorRef { editor: any; insertCharacter: (character: CharacterToken) => void; insertContent: (content: any) => void; - getContent: () => any; } const ShotEditor = React.forwardRef( - function ShotEditor({ content, onCharacterClick, roles, placeholder }, ref) { + function ShotEditor({ content, onCharacterClick, roles, placeholder, onChangeContent }, ref) { const [segments, setSegments] = useState(content); const [isOptimizing, setIsOptimizing] = useState(false); + useEffect(() => { + onChangeContent?.(segments); + }, [segments]); + const handleSmartPolish = () => { setIsOptimizing(true); setTimeout(() => { @@ -60,9 +65,11 @@ const ShotEditor = React.forwardRef( ShotTitle, ReadonlyText, Placeholder.configure({ - placeholder: placeholder || 'Add shot description here...', - showOnlyWhenEditable: true, - showOnlyCurrent: false, + placeholder: ({ node }) => { + console.log('-==========placeholder node===========-', segments); + + return placeholder || 'Add shot description here...'; + }, }), ], content: { type: 'doc', content: segments }, @@ -76,9 +83,10 @@ const ShotEditor = React.forwardRef( editor.setOptions({ editable: true }) }, onUpdate: ({ editor }) => { - const json = editor.getJSON() - console.log('-==========json===========-', json); - setSegments(json.content); + const json = editor.getJSON(); + flushSync(() => { + setSegments(json.content); + }); }, }) @@ -94,9 +102,6 @@ const ShotEditor = React.forwardRef( }, insertContent: (content: any) => { editor?.commands.insertContent(content); - }, - getContent: () => { - return segments; } }), [editor]) // 依赖 editor,确保更新 diff --git a/components/ui/shot-editor/ShotsEditor.tsx b/components/ui/shot-editor/ShotsEditor.tsx index b3ecc93..dfdc50d 100644 --- a/components/ui/shot-editor/ShotsEditor.tsx +++ b/components/ui/shot-editor/ShotsEditor.tsx @@ -23,8 +23,20 @@ interface CharacterToken { const createEmptyShot = (): Shot => ({ name: `shot${Date.now()}`, - shotDescContent: [], - shotDialogsContent: [] + shotDescContent: [{ + type: 'paragraph', + content: [{ + type: 'text', + text: 'Add shot description here...' + }] + }], + shotDialogsContent: [{ + type: 'paragraph', + content: [{ + type: 'text', + text: 'Add shot dialogue here...' + }] + }] }); interface ShotsEditorProps { @@ -50,32 +62,31 @@ export const ShotsEditor = forwardRef(({ roles, shotInfo, } }, [shotInfo]); - // 更新当前分镜内容 - const updateShot = () => { + const handleDescContentChange = (content: any) => { const shot = shots[currentShotIndex]; - if (descEditorRef.current) { - const content = descEditorRef.current.getContent(); - console.log('-==========descEditorcontent===========-', content); - shot.shotDescContent = content; - } - if (dialogEditorRef.current) { - const content = dialogEditorRef.current.getContent(); - console.log('-==========dialogEditorcontent===========-', content); - shot.shotDialogsContent = content; - } - setShots([...shots]); + 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) => { - // 切换前更新当前分镜内容 - updateShot(); setCurrentShotIndex(index); } const getShotInfo = () => { - // 生成前 更新当前分镜内容 - updateShot(); - console.log('-==========shots===========-', shots); + console.log('-==========getShotInfo shots===========-', shots); const shotInfo = shots.map((shot) => { return TextToShotAdapter.toLensType(shot); }); @@ -93,7 +104,6 @@ export const ShotsEditor = forwardRef(({ roles, shotInfo, } const newShot = createEmptyShot(); setShots([...shots, newShot]); - // onShotsChange([...shots, newShot]); // 自动切换到新创建的分镜 setCurrentShotIndex(shots.length); }; @@ -250,6 +260,7 @@ export const ShotsEditor = forwardRef(({ roles, shotInfo, onCharacterClick={() => {}} roles={roles} placeholder="Add shot description here..." + onChangeContent={(content) => {handleDescContentChange(content)}} /> @@ -281,6 +292,7 @@ export const ShotsEditor = forwardRef(({ roles, shotInfo, onCharacterClick={() => {}} roles={roles} placeholder="Add shot dialogue here..." + onChangeContent={(content) => {handleDialogContentChange(content)}} /> diff --git a/components/ui/shot-tab-content.tsx b/components/ui/shot-tab-content.tsx index f62c29e..1bfc3b5 100644 --- a/components/ui/shot-tab-content.tsx +++ b/components/ui/shot-tab-content.tsx @@ -113,7 +113,7 @@ export function ShotTabContent({ ...shotData[selectedIndex], lens: shotInfo }); - // regenerateVideoSegment(); + regenerateVideoSegment(); }; // 新增分镜