fix(chat): refine markdown typography
This commit is contained in:
@@ -13,7 +13,15 @@ const markdown = new MarkdownIt({ breaks: true, html: false, linkify: true });
|
||||
const parsed = computed(() => splitReasoning(props.content));
|
||||
const answer = computed(() => parsed.value.answer.replace(/^\s+/, ""));
|
||||
const reasoning = computed(() => props.reasoning || parsed.value.reasoning);
|
||||
const renderedContent = computed(() => answer.value.trim() ? markdown.render(answer.value) : "");
|
||||
const renderedContent = computed(() => answer.value.trim() ? markdown.render(normalizeStandaloneHeadings(answer.value)) : "");
|
||||
|
||||
function normalizeStandaloneHeadings(content: string) {
|
||||
return content.replace(/^([ \t]*)\*\*(.+)\*\*[ \t]*$/gm, (line, indent: string, title: string) => {
|
||||
const normalizedTitle = title.trim();
|
||||
if (!normalizedTitle || normalizedTitle.length > 48 || normalizedTitle.includes("**")) return line;
|
||||
return `${indent}### ${normalizedTitle}`;
|
||||
});
|
||||
}
|
||||
|
||||
function splitReasoning(content: string) {
|
||||
const lower = content.toLowerCase();
|
||||
|
||||
@@ -1384,38 +1384,43 @@ textarea {
|
||||
overflow-wrap: anywhere;
|
||||
color: #263832;
|
||||
font-size: 14px;
|
||||
line-height: 1.75;
|
||||
line-height: 1.68;
|
||||
}
|
||||
|
||||
.agent-preview-markdown > :first-child { margin-top: 0; }
|
||||
.agent-preview-markdown > :last-child { margin-bottom: 0; }
|
||||
.agent-preview-markdown p { margin: 0 0 10px; }
|
||||
.agent-preview-markdown p { margin: 0 0 8px; }
|
||||
|
||||
.agent-preview-markdown h1,
|
||||
.agent-preview-markdown h2,
|
||||
.agent-preview-markdown h3,
|
||||
.agent-preview-markdown h4 {
|
||||
margin: 16px 0 8px;
|
||||
.agent-preview-markdown h4,
|
||||
.agent-preview-markdown h5,
|
||||
.agent-preview-markdown h6 {
|
||||
margin: 13px 0 5px;
|
||||
color: #1f352e;
|
||||
line-height: 1.45;
|
||||
line-height: 1.42;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.agent-preview-markdown h1 { font-size: 19px; }
|
||||
.agent-preview-markdown h1 { font-size: 18px; }
|
||||
.agent-preview-markdown h2 { font-size: 17px; }
|
||||
.agent-preview-markdown h3,
|
||||
.agent-preview-markdown h3 { font-size: 16px; }
|
||||
.agent-preview-markdown h4 { font-size: 15px; }
|
||||
.agent-preview-markdown h5,
|
||||
.agent-preview-markdown h6 { font-size: 14px; }
|
||||
|
||||
.agent-preview-markdown ul,
|
||||
.agent-preview-markdown ol {
|
||||
margin: 8px 0 10px;
|
||||
padding-left: 22px;
|
||||
margin: 6px 0 8px;
|
||||
padding-left: 21px;
|
||||
}
|
||||
|
||||
.agent-preview-markdown li + li { margin-top: 5px; }
|
||||
.agent-preview-markdown li + li { margin-top: 3px; }
|
||||
.agent-preview-markdown strong { color: #1f352e; font-weight: 700; }
|
||||
|
||||
.agent-preview-markdown blockquote {
|
||||
margin: 10px 0;
|
||||
margin: 8px 0;
|
||||
padding: 7px 10px;
|
||||
border-left: 3px solid #8cc7b6;
|
||||
background: #eef7f4;
|
||||
@@ -1464,6 +1469,12 @@ textarea {
|
||||
|
||||
.agent-preview-markdown a { color: #0f735d; font-weight: 700; }
|
||||
|
||||
.agent-preview-markdown hr {
|
||||
margin: 12px 0;
|
||||
border: 0;
|
||||
border-top: 1px solid #dbe7e3;
|
||||
}
|
||||
|
||||
.agent-preview-generation-state {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -23,9 +23,17 @@ const reasoning = computed(() => props.reasoning || parsed.value.reasoning);
|
||||
const hasAnswer = computed(() => answer.value.trim().length > 0);
|
||||
const renderedContent = computed(() => {
|
||||
if (props.role !== "assistant") return answer.value;
|
||||
return hasAnswer.value ? markdown.render(answer.value) : "";
|
||||
return hasAnswer.value ? markdown.render(normalizeStandaloneHeadings(answer.value)) : "";
|
||||
});
|
||||
|
||||
function normalizeStandaloneHeadings(content: string) {
|
||||
return content.replace(/^([ \t]*)\*\*(.+)\*\*[ \t]*$/gm, (line, indent: string, title: string) => {
|
||||
const normalizedTitle = title.trim();
|
||||
if (!normalizedTitle || normalizedTitle.length > 48 || normalizedTitle.includes("**")) return line;
|
||||
return `${indent}### ${normalizedTitle}`;
|
||||
});
|
||||
}
|
||||
|
||||
function splitReasoning(content: string) {
|
||||
const lower = content.toLowerCase();
|
||||
let answer = "";
|
||||
|
||||
@@ -1321,7 +1321,7 @@ textarea:focus-visible {
|
||||
white-space: pre-wrap;
|
||||
color: inherit;
|
||||
font-size: 15px;
|
||||
line-height: 1.78;
|
||||
line-height: 1.7;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
@@ -1369,35 +1369,39 @@ textarea:focus-visible {
|
||||
|
||||
.markdown-content > :first-child { margin-top: 0; }
|
||||
.markdown-content > :last-child { margin-bottom: 0; }
|
||||
.markdown-content p { margin: 0 0 12px; }
|
||||
.markdown-content p { margin: 0 0 8px; }
|
||||
|
||||
.markdown-content h1,
|
||||
.markdown-content h2,
|
||||
.markdown-content h3,
|
||||
.markdown-content h4 {
|
||||
margin: 20px 0 9px;
|
||||
.markdown-content h4,
|
||||
.markdown-content h5,
|
||||
.markdown-content h6 {
|
||||
margin: 15px 0 6px;
|
||||
color: var(--chat-text);
|
||||
line-height: 1.45;
|
||||
line-height: 1.42;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.markdown-content h1 { font-size: 20px; }
|
||||
.markdown-content h2 { font-size: 18px; }
|
||||
.markdown-content h3,
|
||||
.markdown-content h3 { font-size: 17px; }
|
||||
.markdown-content h4 { font-size: 16px; }
|
||||
.markdown-content h5,
|
||||
.markdown-content h6 { font-size: 15px; }
|
||||
|
||||
.markdown-content ul,
|
||||
.markdown-content ol {
|
||||
margin: 8px 0 13px;
|
||||
padding-left: 22px;
|
||||
margin: 6px 0 9px;
|
||||
padding-left: 21px;
|
||||
}
|
||||
|
||||
.markdown-content li + li { margin-top: 6px; }
|
||||
.markdown-content li + li { margin-top: 3px; }
|
||||
.markdown-content strong { color: var(--chat-text); font-weight: 700; }
|
||||
|
||||
.markdown-content blockquote {
|
||||
margin: 12px 0;
|
||||
padding: 8px 12px;
|
||||
margin: 9px 0;
|
||||
padding: 7px 11px;
|
||||
border-left: 3px solid #8cc7b6;
|
||||
background: var(--chat-brand-soft);
|
||||
color: #526b63;
|
||||
@@ -1405,6 +1409,12 @@ textarea:focus-visible {
|
||||
|
||||
.markdown-content blockquote p { margin: 0; }
|
||||
|
||||
.markdown-content hr {
|
||||
margin: 14px 0;
|
||||
border: 0;
|
||||
border-top: 1px solid var(--chat-border);
|
||||
}
|
||||
|
||||
.chat-composer {
|
||||
position: relative;
|
||||
z-index: 6;
|
||||
|
||||
@@ -51,6 +51,41 @@ final result: passed
|
||||
|
||||
---
|
||||
|
||||
# Markdown 长回答排版修复验收(2026-07-17)
|
||||
|
||||
## 对照范围
|
||||
|
||||
- 问题截图:`/var/folders/hn/vg5lxj0j60d2cp0k6vv_k0mw0000gn/T/codex-clipboard-dc66a8a3-c423-4661-b29a-9c917754b888.png`
|
||||
- 修复后完整页面:`test_records/screenshots/Markdown排版_修复后.png`
|
||||
- 视口:1135 × 874,用户端 430px 固定聊天容器。
|
||||
- 状态:真实历史消息接口,包含 H1、H2、独立加粗小标题、正文、列表和引用块。
|
||||
|
||||
## 比较历史
|
||||
|
||||
- [P1] 修复前:模型常用的独立 `**加粗文本**` 只显示为加粗正文,没有标题字号和上下间距层级。
|
||||
- [P1] 修复前:正文行高、段落底部间距、列表条目间距和引用块上下间距叠加,长回答出现过大断层。
|
||||
- 修复:独立且不超过 48 字的加粗行按 H3 小标题渲染;普通行内加粗保持原语义。
|
||||
- 修复:用户端与后台预览统一收紧段落、列表、引用和标题间距,并补齐 H5、H6 和分隔线样式。
|
||||
|
||||
## 必查视觉面
|
||||
|
||||
- 字体与排版:用户端实测 H1/H2/H3/正文为 20/18/17/15px,标题层级清楚;正文行高 25.5px。
|
||||
- 间距与布局:标题上下间距为 15/6px,段落底部 8px,列表条目 3px,引用块顶部 9px;没有大块空白或水平溢出。
|
||||
- 颜色与令牌:继续使用现有深墨绿正文、雾绿引用背景和边框令牌,未引入新色系。
|
||||
- 图像与资源:本次为文本排版修复,无新增图像、图标或外部资源。
|
||||
- 文案与内容:Markdown 原文未被更改,只在展示层识别独立加粗小标题。
|
||||
|
||||
## 交互与工程检查
|
||||
|
||||
- DOM 实测确认独立加粗行已渲染为 H3,H1、H2、列表和引用均为正确语义节点。
|
||||
- 完整视图已能清晰判断字号、间距、列表和引用块,无需额外放大局部截图。
|
||||
- 用户端与后台预览 `npm run build`:通过。
|
||||
- 未发现剩余 P0、P1、P2 级问题。
|
||||
|
||||
final result: passed
|
||||
|
||||
---
|
||||
|
||||
# 后台登录页视觉重构验收(2026-07-13)
|
||||
|
||||
## 对照范围
|
||||
|
||||
BIN
ai_knowledge_base_v2/test_records/screenshots/Markdown排版_修复后.png
Normal file
BIN
ai_knowledge_base_v2/test_records/screenshots/Markdown排版_修复后.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 47 KiB |
Reference in New Issue
Block a user