diff --git a/app/service/adapter/textToShot.ts b/app/service/adapter/textToShot.ts index 6de8cf2..8a6ad2b 100644 --- a/app/service/adapter/textToShot.ts +++ b/app/service/adapter/textToShot.ts @@ -131,36 +131,45 @@ export class TextToShotAdapter { public static parseHighlight(text: string, tags: TagValueObject[]): ContentNode[] { const nodes: ContentNode[] = []; let currentText = text; - // 按内容长度降序排序,避免短名称匹配到长名称的一部分 - const sortedTags = [...tags].sort((a, b) => String(b.content).length - String(a.content).length); - - while (currentText.length > 0) { + + // 1. 确保 tags 是有效的数组 + if (!Array.isArray(tags)) { + console.warn('Invalid tags:', tags); + return [{ type: 'text', text: currentText }]; + } + + // 2. 恢复排序逻辑,并添加安全检查 + const sortedTags = [...tags] + .filter(tag => tag && tag.content) // 过滤无效的 tag + .sort((a, b) => String(b.content).length - String(a.content).length); + + // 3. 添加安全检查,防止无限循环 + let iterationCount = 0; + const MAX_ITERATIONS = 1000; // 设置一个合理的最大迭代次数 + + while (currentText.length > 0 && iterationCount < MAX_ITERATIONS) { + iterationCount++; let matchFound = false; - // 尝试匹配 for (const tag of sortedTags) { - if (currentText.startsWith(String(tag.content))) { - // 如果当前文本以tag内容开头 - if (currentText.length > String(tag.content).length) { - // 添加标记节点 + const tagContent = String(tag.content); + if (currentText.startsWith(tagContent)) { + if (currentText.length > tagContent.length) { nodes.push({ type: 'highlightText', attrs: { - text: String(tag.content), + text: tagContent, color: tag?.color || 'yellow' } }); - // 移除已处理的tag内容 - currentText = currentText.slice(String(tag.content).length); + currentText = currentText.slice(tagContent.length); matchFound = true; break; } } } - + if (!matchFound) { - // 如果没有找到tag匹配,处理普通文本 - // 查找下一个可能的tag内容位置 let nextTagIndex = currentText.length; for (const tag of sortedTags) { const index = currentText.indexOf(String(tag.content)); @@ -168,8 +177,7 @@ export class TextToShotAdapter { nextTagIndex = index; } } - - // 添加文本节点 + const textContent = currentText.slice(0, nextTagIndex); if (textContent) { nodes.push({ @@ -177,11 +185,18 @@ export class TextToShotAdapter { text: textContent }); } - // 移除已处理的文本 currentText = currentText.slice(nextTagIndex); } } - + + // 4. 如果达到最大迭代次数,处理剩余文本 + if (currentText.length > 0) { + nodes.push({ + type: 'text', + text: currentText + }); + } + return nodes; } private readonly ShotData: Shot; diff --git a/components/ui/character-editor.tsx b/components/ui/character-editor.tsx index 01b8fa9..08b10af 100644 --- a/components/ui/character-editor.tsx +++ b/components/ui/character-editor.tsx @@ -46,11 +46,18 @@ export const CharacterEditor = forwardRef(({ const paragraphs = TextToShotAdapter.fromTextToRole(description, highlight); console.log('-==========paragraphs===========-', paragraphs); setContent(paragraphs); - setTimeout(() => { + + // 保存定时器ID + const timerId = setTimeout(() => { setIsInit(false); setIsOptimizing(false); }, 100); - }, [highlight]); + + // 清理函数:组件卸载时清理定时器 + return () => { + clearTimeout(timerId); + }; + }, [description]); // 暴露方法给父组件 React.useImperativeHandle(ref, () => ({