diff --git a/README.md b/README.md
index 8fe818f..70c14cb 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,7 @@ DiffLens 是一款跨平台桌面文本对比工具,帮助你快速定位两
- **大文件防护**:超过 10MB 的文件自动拦截并提示;行数过多时给出耗时预警;超长单行自动跳过词级高亮,避免卡顿
- **二进制文件预警**:疑似二进制内容给出乱码提示,避免误读为差异
- **差异统计徽章**:实时统计新增 / 删除 / 修改行数
-- **导出一键报告**:将对比结果导出为 HTML / 纯文本 / Markdown 三种格式(始终包含全量行),便于分享与归档
+- **导出一键报告**:将对比结果导出为 HTML / 纯文本 / Markdown 三种格式(始终包含全量行)。HTML 报告为"给人看"设计:一句话结论、文件卡片、统计概览、图例说明、差异分块目录导航,相同内容段自动折叠(点击展开零丢失),并支持打印与窄屏
- **拖拽与按钮双入口**:支持文件拖拽导入,也可通过界面按钮选择文件或粘贴文本
- **滚动联动**:左右两个面板滚动位置自动同步
@@ -96,7 +96,7 @@ src/
- 作者:thzxx
- 组织:MetonaTeam
- 许可证:MIT License(见 [LICENSE](./LICENSE))
-- 版本:0.4.1
+- 版本:0.4.2
---
diff --git a/docs/应用开发与版本迭代规范.md b/docs/应用开发与版本迭代规范.md
index cd94df2..ddddfcb 100644
--- a/docs/应用开发与版本迭代规范.md
+++ b/docs/应用开发与版本迭代规范.md
@@ -20,7 +20,7 @@ z = 补丁版本号(Patch)
> **`x` 永远是 `0`,永远不要提升到 `1.0.0`。**
> 版本迭代**只允许修改 `y` 和 `z`**,`x` 保持 `0` 不变。
-当前基线版本:**`0.4.1`**
+当前基线版本:**`0.4.2`**
---
@@ -67,7 +67,8 @@ z = 补丁版本号(Patch)
0.3.1 ← 稳健性与测试补强(已发布):超长单行词级 diff 防护 · 打开对话框补“所有文件”筛选 · 虚拟滚动 minWidth 测试覆盖 · 滚动同步去锁化重构
0.3.2 ← 报告对齐与性能收尾(已发布):纯文本报告行号右对齐 · 超长行列宽封顶防卡顿 · 对话框与拖拽扩展名清单对齐 · 右键菜单打开时导航快捷键守卫 · 清理导出死属性
0.4.0 ← 差异计算 worker 化(已发布):大文件 diff 移入 Worker 后台计算不阻塞界面 · 输入去抖与过期任务丢弃(终止重建实现取消) · 小输入同步快路径 · 无 Worker 环境回退 · 计算中遮罩与导出禁用
-0.4.1 ← worker 创建开销优化与文档对齐(当前):worker 延迟至去抖到期才创建(去抖期内取消零创建开销,无 Worker 回退同样走去抖) · 质量门禁文档补 npm test · README 架构树补 hooks · Markdown 报告标题移出引用块 · 安装包排除开发期文件
+0.4.1 ← worker 创建开销优化与文档对齐(已发布):worker 延迟至去抖到期才创建(去抖期内取消零创建开销,无 Worker 回退同样走去抖) · 质量门禁文档补 npm test · README 架构树补 hooks · Markdown 报告标题移出引用块 · 安装包排除开发期文件
+0.4.2 ← 导出菜单溢出修复与报告可读性重设计(当前):底部工具栏导出菜单改向上弹出(修复超出窗口底边选不到) · HTML 报告全新设计(Hero 概览/人话结论/文件卡片/统计卡/图例/差异分块目录/相同段折叠可展开/打印亮色/窄屏适配,零 JS) · TXT 与 Markdown 报告头部补人话结论
0.5.0 ← 新增功能(候选:主进程解码 worker 化 · Playwright E2E · 文件夹对比)
...
0.y.z ← 长期停留,永不进入 1.x
diff --git a/docs/测试策略与方案.md b/docs/测试策略与方案.md
index f69cc78..9e752fc 100644
--- a/docs/测试策略与方案.md
+++ b/docs/测试策略与方案.md
@@ -60,7 +60,7 @@ src/renderer/src/
│ ├── ContextMenu.test.tsx # 右键菜单(含边缘防溢出)
│ ├── DiffView.test.tsx # 对比视图(渲染/滚动同步/右键/拖放)
│ ├── TextInputModal.test.tsx # 粘贴文本弹窗
-│ └── Toolbar.test.tsx # 工具栏(选项/导航/导出菜单/计算中禁用)
+│ └── Toolbar.test.tsx # 工具栏(选项/导航/导出菜单向上弹出/计算中禁用)
└── __tests__/
├── App.test.tsx # 应用集成级测试(加载/拖拽/粘贴/导出/容错/大输入回退)
└── main.test.ts # 入口挂载冒烟测试
@@ -82,9 +82,11 @@ src/renderer/src/
- 行切分:结尾换行不产生多余空行
### 5.2 报告生成(`report.ts`)
-- HTML:输出合法文档、含文件名/统计/类型高亮语义
-- 纯文本:含增删改标记(`+ -`)、行号按最大位数右对齐
-- Markdown:表格化、管道符转义保持表格结构
+- HTML:输出合法文档;Hero 区含人话结论(verdict)、文件卡片、统计卡与图例;差异分块(hunk-head)与块内行渲染;多块时输出目录锚点(toc/href="#seg-N"),单块不输出;相同内容段折叠(details/summary)且折叠段内保留全量行数据;一致场景 verdict ok;两侧空内容提示
+- 纯文本:含增删改标记(`+ -`)、行号按最大位数右对齐、头部人话结论
+- Markdown:表格化、管道符转义保持表格结构、引用块含人话结论
+- 差异分块(`buildSegments`):空行集 / 无变更单段全展示 / 上下文 3 行与边界收敛 / 相邻区间合并 / 远距分两段省略数正确 / 自定义 context
+- 人话总结(`plainSummary`):无差异一致结论 / 为 0 项省略 / 三类齐全完整罗列
- 格式分发与扩展名映射(`REPORT_EXT`)
### 5.3 文本工具(`textUtils.ts`)
diff --git a/package.json b/package.json
index 805cea2..b47b5d5 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "DiffLens",
- "version": "0.4.1",
+ "version": "0.4.2",
"description": "DiffLens — 精美酷炫的文本对比桌面应用",
"author": "thzxx",
"license": "MIT",
diff --git a/src/renderer/src/components/Toolbar.test.tsx b/src/renderer/src/components/Toolbar.test.tsx
index 9e1715a..0de8c32 100644
--- a/src/renderer/src/components/Toolbar.test.tsx
+++ b/src/renderer/src/components/Toolbar.test.tsx
@@ -86,6 +86,12 @@ describe('Toolbar', () => {
expect(onExport).toHaveBeenCalledWith('html')
})
+ it('导出菜单向上弹出(工具栏贴窗口底部,防止溢出底边)', () => {
+ const { container } = setup()
+ fireEvent.click(screen.getByText('导出报告'))
+ expect(container.querySelector('.export-menu.up')).not.toBeNull()
+ })
+
it('点击外部区域关闭导出菜单', () => {
setup()
fireEvent.click(screen.getByText('导出报告'))
diff --git a/src/renderer/src/components/Toolbar.tsx b/src/renderer/src/components/Toolbar.tsx
index 1858f11..d953216 100644
--- a/src/renderer/src/components/Toolbar.tsx
+++ b/src/renderer/src/components/Toolbar.tsx
@@ -125,7 +125,7 @@ export default function Toolbar({
导出报告
{exportOpen && (
-
+
{FORMATS.map((f) => (