diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx index 0691a25d18..76567dec6a 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx @@ -30,6 +30,16 @@ const HtmlArtifactsCard: FC = ({ html }) => { const trimmedHtml = htmlContent.trim() + // 提前检查:如果包含关键的结束标签,直接判断为完整文档 + if (/<\/html\s*>/i.test(trimmedHtml)) { + return false + } + + // 如果同时包含 DOCTYPE 和 ,通常也是完整文档 + if (//i.test(trimmedHtml)) { + return false + } + // 检查 HTML 是否看起来是完整的 const indicators = { // 1. 检查常见的 HTML 结构完整性 @@ -78,13 +88,31 @@ const HtmlArtifactsCard: FC = ({ html }) => { function checkUnmatchedTags(html: string): boolean { const stack: string[] = [] const tagRegex = /<\/?([a-zA-Z][a-zA-Z0-9]*)[^>]*>/g + + // HTML5 void 元素(自闭合元素)的完整列表 + const voidElements = [ + 'area', + 'base', + 'br', + 'col', + 'embed', + 'hr', + 'img', + 'input', + 'link', + 'meta', + 'param', + 'source', + 'track', + 'wbr' + ] + let match while ((match = tagRegex.exec(html)) !== null) { const [fullTag, tagName] = match const isClosing = fullTag.startsWith('') || ['img', 'br', 'hr', 'input', 'meta', 'link'].includes(tagName.toLowerCase()) + const isSelfClosing = fullTag.endsWith('/>') || voidElements.includes(tagName.toLowerCase()) if (isSelfClosing) continue @@ -350,7 +378,7 @@ const Content = styled.div` ` const ButtonContainer = styled.div` - margin: 16px; + margin: 16px !important; display: flex; flex-direction: row; gap: 8px; diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx index afba9f04e1..59988a0b1e 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx @@ -1,5 +1,6 @@ import CodeEditor from '@renderer/components/CodeEditor' import { isMac } from '@renderer/config/constant' +import { classNames } from '@renderer/utils' import { Button, Modal } from 'antd' import { Code, Maximize2, Minimize2, Monitor, MonitorSpeaker, X } from 'lucide-react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' @@ -68,7 +69,7 @@ const ModalHeaderComponent: React.FC = ({ }, [viewMode, onViewModeChange, t]) return ( - + {title} @@ -80,8 +81,9 @@ const ModalHeaderComponent: React.FC = ({ onClick={onToggleFullscreen} type="text" icon={isFullscreen ? : } + className="nodrag" /> -