/** * 从 LLM 生成的原始文本里抠出可运行的 HTML 文档。 * * gemma 大多数时候会乖乖只吐 HTML,但偶尔会: * - 用 ```html ... ``` 围栏包起来 * - 在前面/后面加一两句寒暄 * 这里尽量稳地把中间那坨 HTML 提出来。提不出来就返回空串(让调用方报错)。 */ export function extractHtml(raw: string): string { if (!raw) return '' let s = raw.trim() // 1. 优先取 markdown 代码围栏里的内容 const fence = s.match(/```(?:html|HTML)?\s*\n([\s\S]*?)```/) if (fence && fence[1].trim()) { s = fence[1].trim() } else if (s.startsWith('```')) { // 围栏没闭合(流式被截断 / 只有开头)—— 去掉开头那行围栏 s = s.replace(/^```[a-zA-Z]*\s*\n?/, '').replace(/```\s*$/, '').trim() } // 2. 从第一个 |]/i) if (startMatch && startMatch.index !== undefined && startMatch.index > 0) { s = s.slice(startMatch.index) } // 3. 截到最后一个 结束(丢掉后面的寒暄) const endIdx = s.toLowerCase().lastIndexOf('') if (endIdx !== -1) { s = s.slice(0, endIdx + ''.length) } // 兜底:至少得像个 HTML(有标签),否则当作没提到 return /<[a-z!][\s\S]*>/i.test(s) ? s.trim() : '' }