mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-28 05:11:24 +08:00
feat: add HTML code detection utility and integrate into CodeBlockView
- Introduced `isHtmlCode` function to identify HTML content based on DOCTYPE and tag presence. - Updated `CodeBlockView` to utilize `isHtmlCode` for conditional rendering of HTML artifacts. - Added comprehensive tests for `isHtmlCode` to ensure accurate detection of HTML structures.
This commit is contained in:
parent
5b9ff3053b
commit
84a6c2da59
@ -250,7 +250,7 @@ const Container = styled.div<{ $isStreaming: boolean }>`
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
margin: 16px 0;
|
||||
margin: 10px 0;
|
||||
`
|
||||
|
||||
const GeneratingContainer = styled.div`
|
||||
|
||||
@ -142,7 +142,7 @@ const MermaidPreview: React.FC<BasicPreviewProps> = ({ children, setTools }) =>
|
||||
<Spin spinning={isLoading} indicator={<SvgSpinners180Ring color="var(--color-text-2)" />}>
|
||||
<Flex vertical style={{ minHeight: isLoading ? '2rem' : 'auto' }}>
|
||||
{(mermaidError || error) && <PreviewError>{mermaidError || error}</PreviewError>}
|
||||
<StyledMermaid ref={mermaidRef} className="mermaid" />
|
||||
<StyledMermaid ref={mermaidRef} className="mermaid special-preview" />
|
||||
</Flex>
|
||||
</Spin>
|
||||
)
|
||||
|
||||
@ -4,7 +4,7 @@ import { CodeTool, CodeToolbar, TOOL_SPECS, useCodeTool } from '@renderer/compon
|
||||
import { useSettings } from '@renderer/hooks/useSettings'
|
||||
import { pyodideService } from '@renderer/services/PyodideService'
|
||||
import { extractTitle } from '@renderer/utils/formats'
|
||||
import { getExtensionByLanguage, isValidPlantUML } from '@renderer/utils/markdown'
|
||||
import { getExtensionByLanguage, isHtmlCode, isValidPlantUML } from '@renderer/utils/markdown'
|
||||
import dayjs from 'dayjs'
|
||||
import { CirclePlay, CodeXml, Copy, Download, Eye, Square, SquarePen, SquareSplitHorizontal } from 'lucide-react'
|
||||
import React, { memo, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
@ -229,7 +229,7 @@ export const CodeBlockView: React.FC<Props> = memo(({ children, language, onSave
|
||||
}, [specialView, sourceView, viewMode])
|
||||
|
||||
// HTML 代码块特殊处理 - 在所有 hooks 调用之后
|
||||
if (language === 'html') {
|
||||
if (language === 'html' && isHtmlCode(children)) {
|
||||
return <HtmlArtifactsCard html={children} />
|
||||
}
|
||||
|
||||
|
||||
@ -95,7 +95,7 @@ const MessageItem: FC<Props> = ({
|
||||
stopEditing()
|
||||
}, [stopEditing])
|
||||
|
||||
const isLastMessage = index === 0
|
||||
const isLastMessage = index === 0 || !!isGrouped
|
||||
const isAssistantMessage = message.role === 'assistant'
|
||||
const showMenubar = !hideMenuBar && !isStreaming && !message.status.includes('ing') && !isEditing
|
||||
|
||||
@ -190,6 +190,7 @@ const MessageContainer = styled.div`
|
||||
transform: translateZ(0);
|
||||
will-change: transform;
|
||||
padding: 10px;
|
||||
padding-bottom: 0;
|
||||
border-radius: 10px;
|
||||
&.message-highlight {
|
||||
background-color: var(--color-primary-mute);
|
||||
|
||||
@ -14,7 +14,7 @@ const MessageContent: React.FC<Props> = ({ message }) => {
|
||||
return (
|
||||
<>
|
||||
{!isEmpty(message.mentions) && (
|
||||
<Flex gap="8px" wrap>
|
||||
<Flex gap="8px" wrap style={{ marginBottom: '10px' }}>
|
||||
{message.mentions?.map((model) => <MentionTag key={getModelUniqId(model)}>{'@' + model.name}</MentionTag>)}
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
findCitationInChildren,
|
||||
getCodeBlockId,
|
||||
getExtensionByLanguage,
|
||||
isHtmlCode,
|
||||
markdownToPlainText,
|
||||
processLatexBrackets,
|
||||
removeTrailingDoubleSpaces,
|
||||
@ -698,4 +699,31 @@ $$
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('isHtmlCode', () => {
|
||||
it('should detect HTML with DOCTYPE', () => {
|
||||
expect(isHtmlCode('<!DOCTYPE html>')).toBe(true)
|
||||
expect(isHtmlCode('<!doctype html>')).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect HTML with html/head/body tags', () => {
|
||||
expect(isHtmlCode('<html>')).toBe(true)
|
||||
expect(isHtmlCode('</html>')).toBe(true)
|
||||
expect(isHtmlCode('<head>')).toBe(true)
|
||||
expect(isHtmlCode('<body>')).toBe(true)
|
||||
})
|
||||
|
||||
it('should detect complete HTML structure', () => {
|
||||
const html = '<html><head><title>Test</title></head><body>Hello</body></html>'
|
||||
expect(isHtmlCode(html)).toBe(true)
|
||||
})
|
||||
|
||||
it('should return false for non-HTML content', () => {
|
||||
expect(isHtmlCode(null)).toBe(false)
|
||||
expect(isHtmlCode('')).toBe(false)
|
||||
expect(isHtmlCode('Hello world')).toBe(false)
|
||||
expect(isHtmlCode('a < b')).toBe(false)
|
||||
expect(isHtmlCode('<div>')).toBe(false)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -267,6 +267,47 @@ export function isValidPlantUML(code: string | null): boolean {
|
||||
return diagramType !== undefined && code.search(`@end${diagramType}`) !== -1
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查代码是否具有HTML特征
|
||||
* @param code 输入的代码字符串
|
||||
* @returns 是HTML代码 true,否则 false
|
||||
*/
|
||||
export function isHtmlCode(code: string | null): boolean {
|
||||
if (!code || !code.trim()) {
|
||||
return false
|
||||
}
|
||||
|
||||
const trimmedCode = code.trim()
|
||||
|
||||
// 检查是否包含HTML文档类型声明
|
||||
if (trimmedCode.includes('<!DOCTYPE html>') || trimmedCode.includes('<!doctype html>')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否包含html标签
|
||||
if (trimmedCode.includes('<html') || trimmedCode.includes('</html>')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否包含head标签
|
||||
if (trimmedCode.includes('<head>') || trimmedCode.includes('</head>')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否包含body标签
|
||||
if (trimmedCode.includes('<body') || trimmedCode.includes('</body>')) {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查是否以HTML标签开头和结尾的完整HTML结构
|
||||
const htmlTagPattern = /^\s*<html[^>]*>[\s\S]*<\/html>\s*$/i
|
||||
if (htmlTagPattern.test(trimmedCode)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 将 Markdown 字符串转换为纯文本。
|
||||
* @param markdown Markdown 字符串。
|
||||
|
||||
Loading…
Reference in New Issue
Block a user