修正 Markdown 代码块 HTML 转义
移除整段 Markdown 的 HTML 预转义并继续使用 skipHtml 补充行内与围栏代码 HTML 字面量回归测试 同步项目开发工作台安全 Markdown 渲染约定
This commit is contained in:
@@ -200,12 +200,6 @@ const streamingMarkdownComponents: Components = {
|
||||
),
|
||||
};
|
||||
|
||||
function escapeHtmlTagsForDisplay(text: string) {
|
||||
return text.replace(/<\/?[A-Za-z][^>]*>/gu, (tag) =>
|
||||
tag.replaceAll('<', '<').replaceAll('>', '>'),
|
||||
);
|
||||
}
|
||||
|
||||
export function ChatMarkdownMessage({
|
||||
text,
|
||||
role,
|
||||
@@ -224,7 +218,7 @@ export function ChatMarkdownMessage({
|
||||
streaming ? streamingMarkdownComponents : markdownComponents
|
||||
}
|
||||
>
|
||||
{escapeHtmlTagsForDisplay(text)}
|
||||
{text}
|
||||
</ReactMarkdown>
|
||||
</MarkdownErrorBoundary>
|
||||
);
|
||||
|
||||
@@ -105,6 +105,30 @@ describe('ChatMarkdownMessage', () => {
|
||||
expect(code?.className).not.toContain('rounded');
|
||||
});
|
||||
|
||||
it('保留行内代码中的 HTML 字面量', () => {
|
||||
const { container } = render(
|
||||
<ChatMarkdownMessage role="assistant" text={'`<span>`'} />,
|
||||
);
|
||||
|
||||
expect(container.querySelector('code')?.textContent).toBe('<span>');
|
||||
});
|
||||
|
||||
it('保留围栏代码中的 HTML 字面量而不双重转义', () => {
|
||||
const { container } = render(
|
||||
<ChatMarkdownMessage
|
||||
role="assistant"
|
||||
text={'```html\n<div class="card"></div>\n```'}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(container.querySelector('pre code')?.textContent).toContain(
|
||||
'<div class="card"></div>',
|
||||
);
|
||||
expect(container.querySelector('pre code')?.textContent).not.toContain(
|
||||
'<div',
|
||||
);
|
||||
});
|
||||
|
||||
it('累计文本变化后可从 Markdown 错误回退中恢复', () => {
|
||||
const consoleError = vi
|
||||
.spyOn(console, 'error')
|
||||
|
||||
@@ -185,7 +185,7 @@ completed -> starting(nextSlice)
|
||||
idle -> focused(document|art|audio|version) -> idle
|
||||
```
|
||||
|
||||
- 文档:合法 Agent 文本回执直接使用对话投影内容;项目文件只允许读取当前 manifest 已登记资产或已完成任务产物中的 Markdown、文本、JSON、YAML、TOML,必须经过 `file.read` auto 权限、相对路径、项目边界、普通文件、符号链接 / 硬链接、读取漂移、2 MiB、UTF-8 与扩展名白名单校验。正文使用不执行 HTML、不加载远程图片、不产生可点击外链的安全 Markdown 渲染,并在中央画布内独立滚动;读取失败显示错误空态。
|
||||
- 文档:合法 Agent 文本回执直接使用对话投影内容;项目文件只允许读取当前 manifest 已登记资产或已完成任务产物中的 Markdown、文本、JSON、YAML、TOML,必须经过 `file.read` auto 权限、相对路径、项目边界、普通文件、符号链接 / 硬链接、读取漂移、2 MiB、UTF-8 与扩展名白名单校验。正文使用不执行 HTML、不加载远程图片、不产生可点击外链的安全 Markdown 渲染,并在中央画布内独立滚动;读取失败显示错误空态。Markdown 渲染使用 `react-markdown` 的 `skipHtml`,依赖库对代码 span / fenced code 的文本转义;不得在整段 Markdown 上预转义 HTML,否则会把代码中的 `<tag>` 双重转义为字面量 `<tag>`。
|
||||
- 美术:PNG、JPEG、WEBP、GIF、SVG、AVIF、BMP、MP4、WebM、MOV 只在资源卡本体中按既有受控读取、文件签名与解码门禁展示;中央详情不重复加载或放大图片 / 视频本体。SVG 继续拒绝脚本、事件处理器、外部资源引用和实体声明。
|
||||
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
|
||||
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源替换仍留给后续切片。
|
||||
|
||||
Reference in New Issue
Block a user