- #7a reveal 超时:长文按总时长 ≤8s 反推 interval(下限 12ms),短路径 30ms/字符不变(step=1);跳过按钮与 reduced-motion 行为不变 - #7b 标题编号:heading_open 注入 h1「一、」/h2「1.1」/h3「1.2.3」, 层级变化归零、自带编号跳过、md-hN class 恒挂、计数器逐次渲染重置; DocPane md 回退预览与私有参考块用 renderMarkdownPlain 关闭注入 (防止编号被序列化写回正本) - #27 会话聚合:删除前端二次聚合,会话域直接以后端 sources 为准; SourcesPane/SourceRow 以 paper_id/id 为 key 与高亮依据,徽标显示 真实 paper_id(截断+悬浮),无 paper_id 退 index+1; setHighlightSource 参数 index→key,chip 编号契约保持 - #31 节点详情:KgEntityDetail fetchExtra 序号守卫;KgPane :key 强制 重建;KgGraphView pointer 事件重构(pointerdown 不捕获、位移>4px 才捕获+标记 dragging、pointerup release、onClickNode dragging 拦截) - #39 日志刷新:打开总是重拉+「刷新日志」按钮+有 running 时 5s 独立 轮询(onBeforeUnmount 清理) - #40 轮询暂停:任何离开 extract 的切换 pausePolling;详情/日志展开态 提升到 store(detailOpenId/logOpen/runLog/logError),切 TAB 保留 - #42 模板选择:Composer docgen 模式模板下拉(自动推荐+内置+as_template 懒加载,失败降级),template_id 经 sendDocgen 传入 - #47b 会话切换 resetSession(selectSession 动态 import,只清不丢) - #55 文档 TAB:docgen store viewMode + DocPane「资料库」/「关闭文档」/ 「返回文档」按钮 - #28 检索文案:searchNoAnswer/searchTimeoutEmpty;200 降级体按 payload.degraded 分流,不再走 _errorMarker TIMEOUT 报错 - #52 异步轮询:api getTask/listTasks;sendDocgen 兼容 200/202, 202 后释放 busy 锁(生成期间可继续对话)、消息块记录 task_id、 2s 轮询 getTask、progress 驱动「生成中 x/y 节」、done 复用现有 artifact 挂块+落库+adoptArtifact+切 TAB、error 可重试; 会话恢复时 running 块含 task_id 续轮询(不误标「已中断」) 自测:29 个新增 r3debug 单测 + 23 个既有 private_docs 测试全绿; npm run build 通过;playwright 冒烟(dev server 5199)无 JS 错误, 模板下拉/提取面板/日志刷新按钮渲染正常。 未 push;Debug-2 9090 未就绪,#52/#28 联调留待 9090 就绪后执行。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
58 lines
2.7 KiB
JavaScript
58 lines
2.7 KiB
JavaScript
// tests/r3debug/markdown.test.js —— 缺陷 #7b 标题编号注入
|
|
import { describe, it, expect } from 'vitest';
|
|
import { renderMarkdown, renderMarkdownPlain } from '../../src/components/chat/markdown.js';
|
|
|
|
describe('#7b 标题编号注入', () => {
|
|
it('h1→「一、」,h2→「1.1」,h3→「1.2.3」;层级变化归零', () => {
|
|
const out = renderMarkdown(
|
|
'# 第一章\n\n## 节一\n\n### 小节一\n\n## 节二\n\n# 第二章\n\n## 节一\n\n### 小节一',
|
|
);
|
|
const hs = out.split('\n').filter((l) => l.startsWith('<h'));
|
|
expect(hs[0]).toBe('<h1 class="md-h1">一、 第一章</h1>');
|
|
expect(hs[1]).toBe('<h2 class="md-h2">1.1 节一</h2>');
|
|
expect(hs[2]).toBe('<h3 class="md-h3">1.1.1 小节一</h3>');
|
|
expect(hs[3]).toBe('<h2 class="md-h2">1.2 节二</h2>');
|
|
expect(hs[4]).toBe('<h1 class="md-h1">二、 第二章</h1>');
|
|
expect(hs[5]).toBe('<h2 class="md-h2">2.1 节一</h2>'); // h1 变化后 h2 归零
|
|
expect(hs[6]).toBe('<h3 class="md-h3">2.1.1 小节一</h3>');
|
|
});
|
|
|
|
it('已带数字/中文序号开头的标题跳过注入(原文保持)', () => {
|
|
const out = renderMarkdown('## 1. 概述\n\n### 1.1 目标\n\n## 一、背景');
|
|
const hs = out.split('\n').filter((l) => l.startsWith('<h'));
|
|
expect(hs[0]).toBe('<h2 class="md-h2">1. 概述</h2>');
|
|
expect(hs[1]).toBe('<h3 class="md-h3">1.1 目标</h3>');
|
|
expect(hs[2]).toBe('<h2 class="md-h2">一、背景</h2>');
|
|
});
|
|
|
|
it('计数器每次渲染入口重置(渲染器单例不跨消息延续编号)', () => {
|
|
const a = renderMarkdown('# A');
|
|
const b = renderMarkdown('# B');
|
|
expect(a).toContain('一、 A');
|
|
expect(b).toContain('一、 B'); // 若未重置会是「二、 B」
|
|
});
|
|
|
|
it('文档以 h2 开头:父级计数兜底 1(不出现 0.1)', () => {
|
|
const out = renderMarkdown('## 直接节\n\n### 小节');
|
|
const hs = out.split('\n').filter((l) => l.startsWith('<h'));
|
|
expect(hs[0]).toBe('<h2 class="md-h2">1.1 直接节</h2>');
|
|
expect(hs[1]).toBe('<h3 class="md-h3">1.1.1 小节</h3>');
|
|
});
|
|
|
|
it('行内强调(strong 等)保留', () => {
|
|
const out = renderMarkdown('## 加粗**重点**标题');
|
|
expect(out).toContain('<h2 class="md-h2">1.1 加粗<strong>重点</strong>标题</h2>');
|
|
});
|
|
|
|
it('renderMarkdownPlain 不注入编号(DocPane md 回退预览保护正本)', () => {
|
|
const out = renderMarkdownPlain('# 不应编号');
|
|
expect(out.trim()).toBe('<h1 class="md-h1">不应编号</h1>');
|
|
});
|
|
|
|
it('所有 h1-h6 均挂 md-hN class(含非 1-3 级)', () => {
|
|
const out = renderMarkdown('#### 四级\n\n##### 五级');
|
|
expect(out).toContain('<h4 class="md-h4">');
|
|
expect(out).toContain('<h5 class="md-h5">');
|
|
});
|
|
});
|