fix video bug
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 2m52s

This commit is contained in:
zyc 2026-03-13 15:47:11 +08:00
parent 566c3a476f
commit 7aa1788035
2 changed files with 88 additions and 17 deletions

View File

@ -29,13 +29,19 @@ export function PromptInput() {
editorRef.current?.focus(); editorRef.current?.focus();
}, []); }, []);
// Sync editor when editorHtml resets (e.g. after submit) // Sync editor when editorHtml changes (e.g. after submit or reEdit)
useEffect(() => { useEffect(() => {
const el = editorRef.current; const el = editorRef.current;
if (!el) return; if (!el) return;
if (editorHtml === '' && el.innerHTML !== '') { if (el.innerHTML !== editorHtml) {
el.innerHTML = ''; el.innerHTML = editorHtml;
// If the HTML is plain text but we have references, rebuild mention spans
// This handles the case where editorHtml comes from backend (plain text only)
if (editorHtml && !editorHtml.includes('data-ref-id') && references.length > 0) {
rebuildMentionSpans(el);
}
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [editorHtml]); }, [editorHtml]);
// Handle @ button from toolbar // Handle @ button from toolbar
@ -47,6 +53,57 @@ export function PromptInput() {
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [insertAtTrigger]); }, [insertAtTrigger]);
// Rebuild mention spans from plain text @label patterns
const rebuildMentionSpans = useCallback((el: HTMLElement) => {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT);
const replacements: { node: Text; matches: { start: number; end: number; ref: UploadedFile }[] }[] = [];
let textNode: Text | null;
while ((textNode = walker.nextNode() as Text | null)) {
const text = textNode.textContent || '';
const matches: { start: number; end: number; ref: UploadedFile }[] = [];
for (const ref of references) {
const pattern = `@${ref.label}`;
let idx = text.indexOf(pattern);
while (idx !== -1) {
matches.push({ start: idx, end: idx + pattern.length, ref });
idx = text.indexOf(pattern, idx + pattern.length);
}
}
if (matches.length > 0) {
matches.sort((a, b) => a.start - b.start);
replacements.push({ node: textNode, matches });
}
}
for (const { node, matches } of replacements) {
const text = node.textContent || '';
const frag = document.createDocumentFragment();
let lastIdx = 0;
for (const m of matches) {
if (m.start > lastIdx) {
frag.appendChild(document.createTextNode(text.slice(lastIdx, m.start)));
}
const span = document.createElement('span');
span.className = styles.mention;
span.contentEditable = 'false';
span.dataset.refId = m.ref.id;
span.dataset.refType = m.ref.type;
span.textContent = `@${m.ref.label}`;
frag.appendChild(span);
lastIdx = m.end;
}
if (lastIdx < text.length) {
frag.appendChild(document.createTextNode(text.slice(lastIdx)));
}
node.parentNode?.replaceChild(frag, node);
}
if (replacements.length > 0) {
setEditorHtml(el.innerHTML);
}
}, [references, setEditorHtml]);
const openMentionPopup = useCallback(() => { const openMentionPopup = useCallback(() => {
const el = editorRef.current; const el = editorRef.current;
if (!el) return; if (!el) return;

View File

@ -314,20 +314,34 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
inputStore.switchMode(task.mode); inputStore.switchMode(task.mode);
} }
const references: UploadedFile[] = task.references.map((r) => ({ if (task.mode === 'universal') {
id: r.id, const references: UploadedFile[] = task.references.map((r) => ({
type: r.type, id: r.id,
previewUrl: r.previewUrl, type: r.type,
label: r.label, previewUrl: r.previewUrl,
})); label: r.label,
tosUrl: r.previewUrl,
useInputBarStore.setState({ }));
prompt: task.prompt, useInputBarStore.setState({
editorHtml: task.editorHtml || task.prompt, prompt: task.prompt,
aspectRatio: task.aspectRatio, editorHtml: task.editorHtml || task.prompt,
duration: task.duration, aspectRatio: task.aspectRatio,
references: task.mode === 'universal' ? references : [], duration: task.duration,
}); references,
});
} else {
// Keyframe mode: restore firstFrame and lastFrame
const firstRef = task.references.find((r) => r.label === '首帧');
const lastRef = task.references.find((r) => r.label === '尾帧');
useInputBarStore.setState({
prompt: task.prompt,
editorHtml: task.editorHtml || task.prompt,
aspectRatio: task.aspectRatio,
duration: task.duration,
firstFrame: firstRef ? { id: firstRef.id, type: firstRef.type, previewUrl: firstRef.previewUrl, label: '首帧', tosUrl: firstRef.previewUrl } : null,
lastFrame: lastRef ? { id: lastRef.id, type: lastRef.type, previewUrl: lastRef.previewUrl, label: '尾帧', tosUrl: lastRef.previewUrl } : null,
});
}
}, },
regenerate: (id) => { regenerate: (id) => {