# 迭代日志

## 当前状态（修订）

- 封面/目录/页码/缩进/页眉统一由 frontmatter `disables` 控制
- `withcover` / `no-toc` / `no-indent` / `no-pagenumber` / `nocover` 已移除

## 2026-01-02

### pubinfo 配置聚合

**修改文件**:
- `src/mark2pdf/resources/frontmatter.yaml` (示例更新为 pubinfo 分组)
- `template/nb.typ` (参数读取从顶层改为 pubinfo.xxx)
- `template/back/gaozhi-book.typ` (edition 读取改为 pubinfo.edition)
- `src/markdown2pdf/core.py` (字体警告读取 pubinfo.font/titlefont)
- `src/markdown2pdf/utils.py` (新增 merge_frontmatter 深度合并函数)
- `src/mark2pdf/cli.py` (使用 merge_frontmatter 替代浅合并)
- `src/mark2pdf/conversion.py` (使用 merge_frontmatter 替代浅合并)
- `src/mark2pdf/tests/test_batch_conversion.py` (测试 fixtures 更新为 pubinfo 结构)
- `src/mark2pdf/tests/test_manager.py` (测试配置更新为 pubinfo 结构)

**变更内容**:

1. **配置字段聚合**: 将分散的出版信息相关字段聚合到 `pubinfo` 分组下:
   ```yaml
   pubinfo:
     edition: "AI 未来学校"
     info: "2026年1月出版"
     logo: ./images/qr.png
     font: "SourceHanSerif"
     titlefont: "SourceHanSans"
     watermark: "张华读书会"
   ```

2. **历史字段映射**:
   | 原字段 | 新字段 |
   |-------|--------|
   | `edition` | `pubinfo.edition` |
   | `publication-info` | `pubinfo.info` |
   | `logo` | `pubinfo.logo` |
   | `tyfont` | `pubinfo.font` |
   | `tyfont_title` | `pubinfo.titlefont` |
   | `watermark` | `pubinfo.watermark` |

3. **深度合并支持**: 新增 `merge_frontmatter()` 函数，支持嵌套字典递归合并。原 `{**base, **override}` 只能浅合并，无法正确处理 `pubinfo` 等嵌套结构。

4. **Pandoc 模板更新**: 使用 `$if(pubinfo.edition)$`、`$pubinfo.edition$` 等嵌套语法读取分组字段。

---

### footer 布局修复

**修改文件**:
- `template/nb-lib.typ` (页脚布局重构)

**问题**:
1. 页码 `align(center, counter(page).display("1"))` 因容器增宽 3.5cm 导致视觉偏右
2. `set image(width: 6.5cm)` 对已传入的 `footerright` 内容无效

**修复方案**:
- 页码: 使用 `move(dx: -1.75cm, ...)` 左移补偿
- footerright: 使用 `move(dx: 1.75cm, dy: -0.5cm, box(width: 5.5cm, ...))` 定位并限制宽度
- grid align 改为 `(left, center, right)` 明确各列对齐

**技术说明**: `set image(...)` 只影响作用域内新创建的 image 元素，对已渲染的 content 参数无效；需用 `box` 限制容器宽度来间接控制图片大小。

---



### headerfooter 配置重构

**修改文件**:
- `template/nb.typ` (参数从 leftheader/footer-image → headerleft/headerright/footerleft/footerright)
- `template/nb-lib.typ` (页眉页脚渲染逻辑重构，页脚从二列 grid 改为三列)
- `src/mark2pdf/resources/frontmatter.yaml` (示例更新为 headerfooter 分组)
- `_logs/docs/工作区使用指南.md` (文档同步更新)
- `_logs/frontmatter与nb配置分组分析.md` (配置字段说明更新)

**变更内容**:

1. **参数重命名与扩展**:
   - `leftheader` → `headerleft`
   - `footer-image` → `footerright`
   - 新增 `headerright`（缺省使用 title）
   - 新增 `footerleft`

2. **frontmatter 结构**:
   ```yaml
   headerfooter:
     headerleft: "页眉左侧"
     headerright: "页眉右侧"
     footerleft: "页脚左侧"
     footerright: ![](images/qr.png)
   ```

3. **页脚布局重构**:
   - 原: 二列 grid (页码居中、右侧图片使用 `place` 定位)
   - 新: 三列 grid (左/中/右各占一列，对齐更稳定)
   - 移除 `place()` 硬编码偏移，使用 `align()` 自然对齐

4. **Pandoc 模板更新**:
   - 使用 `$if(headerfooter.headerleft)$` 等嵌套语法读取分组字段
   - 移除 `$if(leftheader)$` 和 `$if(footer-image)$`

**历史字段**（不再支持）:
- `leftheader`、`footer-image`

---

### disables 统一方案落地

**修改文件**:
- `template/nb.typ` (新增 `disables` 参数，替代 `no-xxx` 布尔值)
- `template/nb-lib.typ` (参数重命名：`no-indent` → `disable-indent`，新增 `disable-header`、`disable-pagenumber`)
- `src/markdown2pdf/cli.py` (移除 `withcover` 读取逻辑)
- `src/markdown2pdf/core.py` (移除 `nocover` pandoc 参数)
- `src/markdown2pdf/options.py` (移除 `withcover` 字段)
- `src/mark2pdf/conversion.py` (移除 `withcover` 构建)
- `src/mark2pdf/commands/gaozhi.py` (移除 `withcover` 参数)
- `src/mark2pdf/resources/frontmatter.yaml` (更新示例为 `disables`)
- `web_ui/web_server.py` (移除 `cover` 选项)

**变更内容**:

1. **nb.typ 新增 disables 机制**:
   - 新增 `disables: ()` 参数（默认空数组 = 全部启用）
   - 新增 `disabled(name)` 辅助函数检查禁用项
   - 计算 `disable_cover`、`disable_toc`、`disable_indent`、`disable_pagenumber`、`disable_header`、`disable_backcover`

2. **nb-lib.typ 参数统一**:
   - `no-indent` → `disable-indent`
   - `no-pagenumber` → `disable-pagenumber`
   - 新增 `disable-header`（控制页眉渲染）

3. **pandoc 模板更新**:
   - 移除 `$if(no-toc)$`、`$if(no-indent)$`、`$if(nocover)$`、`$if(no-pagenumber)$`
   - 新增 `$if(disables)$` 输出数组格式

4. **Python 层清理**:
   - 移除 `ConversionOptions.withcover` 字段
   - 移除 CLI 中 frontmatter `withcover` 读取
   - 移除 `core.py` 中 `nocover` pandoc 参数注入

**可禁用项**:
| 禁用项 | 说明 |
|-------|------|
| `cover` | 封面 + 封底 |
| `backcover` | 仅封底 |
| `toc` | 目录页 |
| `indent` | 首行缩进 |
| `pagenumber` | 页码 |
| `header` | 页眉 |

---

### 配置系统重构

**修改文件**:
- `src/mark2pdf/config.py` (`BuildConfig` → `OptionsConfig`)
- `src/mark2pdf/config_loader.py` (解析 `[options]` 节，兼容旧 `[build]`)
- `src/mark2pdf/defaults.py` (默认配置模板)
- `src/mark2pdf/reporter.py` (配置报告输出)
- `src/mark2pdf/conversion.py` (使用新配置结构)
- `src/markdown2pdf/options.py`（移除 `withcover`，统一 `disables`）
- `src/markdown2pdf/core.py` (移除 `filename_with_title` 逻辑)
- `src/markdown2pdf/directory.py` (移除 `filename_with_title` 逻辑)
- `src/markdown2pdf/utils.py` (`get_output_filename` 增加 title 回退)
- `src/markdown2pdf/cli.py`（移除 `withcover` 读取）
- `src/markdown2pdf/defaults.py` (简化构建选项)
- `src/mark2pdf/commands/gaozhi.py` (适配新字段名)
- `mark2pdf.config.toml` (配置节重命名)
- `uv.lock` (版本 0.6.1 → 0.6.2)
- 相关测试文件

**变更内容**:

1. **配置节重命名**: `[build]` → `[options]`
   - 保留向后兼容：`config_loader.py` 优先读取 `[options]`，回退到 `[build]`

2. **封面开关统一**: `withcover` 方案已废弃，统一由 `disables` 控制
   - `ConversionOptions` 不再包含 `withcover`
   - frontmatter 仅识别 `disables`

3. **移除 `filename_with_title`**:
   - 原逻辑：显式开启 `filename_with_title` 才用 title 作为输出文件名
   - 新逻辑：`get_output_filename` 自动回退：`exportFilename` > `title` > 默认文件名
   - 移除 CLI 的 `-ft/--filename-with-title` 选项

4. **移除 `cover` / `withcover` 配置项**:
   - 不再通过 `[options]` 或 frontmatter 的布尔值控制封面
   - 统一使用 `disables: [cover]` 关闭封面

5. **批量转换重构** (`run_batch_conversion`):
   - options 构建从循环外移到循环内
   - 每个文件独立读取 frontmatter，支持单独的 `disables`/`template` 设置

**测试**: 318 个测试全部通过

---

## 2026-01-01

### convert_from_string 功能

**修改文件**:
- `src/markdown2pdf/core.py` (新增 `convert_from_string` 函数)
- `src/markdown2pdf/__init__.py` (导出 `convert_from_string`)
- `src/mark2pdf/__init__.py` (re-export `convert_from_string`)
- `src/markdown2pdf/tests/test_convert_from_string.py` (新增：单元测试)

**功能**:
从内存字符串直接转换为 PDF/Typst 文件，支持服务端场景。

**参数**:
- `content`: Markdown 内容字符串
- `output_path`: 输出文件路径（可为 PDF 或 Typst）
- `options`: `ConversionOptions` 转换配置
- `default_frontmatter`: 默认 frontmatter 字典（可选）
- `postprocess`: 后处理函数（可选）
- `images_dir`: 图片目录（可选）
- `config`: Mark2pdfConfig 配置对象（可选）

**使用**:
```python
from markdown2pdf import convert_from_string, ConversionOptions
# 或者
from mark2pdf import convert_from_string

result = convert_from_string(
    content="# Hello\nWorld",
    output_path="./out/result.pdf",
    images_dir="./assets",
)
```

**设计要点**:
- 复用 `execute_in_sandbox` 沙箱执行机制
- 支持相对/绝对路径、自动补后缀 (`.pdf`/`.typ`)
- 独立模式：无 config 时使用 `get_project_root()` 获取 tmp_dir
- 图片目录处理：自动检测 `images/` 目录层级

**测试**: 1 个单元测试通过

---

### 字体路径支持与字体管理命令

**修改文件**:
- `src/markdown2pdf/core.py` (新增 `_collect_font_paths`、`_warn_missing_fonts`)
- `src/helper_typst/helper_typst.py` (新增 `font_paths` 参数与 `_add_font_paths`)
- `src/mark2pdf/commands/fonts.py` (新增: 字体管理命令)
- `src/mark2pdf/config.py` (PathsConfig 新增 `fonts` 字段、Mark2pdfConfig 新增 `fonts_dir` 属性)
- `src/mark2pdf/config_loader.py` (加载 `paths.fonts` 配置)
- `src/mark2pdf/commands/__init__.py`, `cli.py` (注册 fonts 命令)

**功能**:

1. **字体路径自动收集** (`_collect_font_paths`):
   - 优先级：`paths.fonts` (配置指定) → `{cwd}/fonts/` (独立模式) → `{template_dir}/fonts/` (模板同级)
   - 通过 `--pdf-engine-opt --font-path=` 传递给 Typst

2. **字体缺失警告** (`_warn_missing_fonts`):
   - 解析 frontmatter 中的 `tyfont`、`tyfont_title`
   - 在字体目录中搜索匹配文件（支持 .ttf/.otf/.ttc/.otc）
   - verbose 模式下输出警告

3. **fonts 命令组** (`mark2pdf fonts`):
   - `list`: 列出可安装字体（内置目录：思源黑体、思源宋体、霞鹜文楷、悠哉字体）
   - `install <name>`: 从 GitHub Releases 下载并安装到 `fonts/`
   - `status`: 查看已安装字体

**命令**:
```bash
mark2pdf fonts list
mark2pdf fonts install lxgw-wenkai
mark2pdf fonts install --url https://example.com/font.zip
mark2pdf fonts status
```

**设计要点**:
- 使用 GitHub API 获取最新 release，自动选择匹配的 asset
- 支持 zip 包解压和单字体文件下载
- 支持 `GITHUB_TOKEN` 环境变量避免 API 限流
- 安装目录自动创建，可通过 `--dir` 覆盖

**测试**: 316 个测试全部通过

---

### 模板打包与多层查找机制

**修改文件**:
- `pyproject.toml` (wheel 打包 `template/` → `mark2pdf/templates/`)
- `src/helper_workingpath/helper_working_path.py` (模板多层查找)
- `src/mark2pdf/workspace_manager.py` (模板复制逻辑)
- `src/mark2pdf/commands/workspace.py` (新增 `template` 命令)
- `src/mark2pdf/commands/__init__.py`, `cli.py` (注册命令)
- `src/mark2pdf/config.py` (支持环境变量与绝对路径的 template_dir)
- `src/helper_typst/__init__.py` (导出 `parse_template_assets`)
- `template/nb.typ` (字体回退逻辑优化)
- 相关测试文件

**功能**:

1. **模板多层查找** (`resolve_template_path`):
   - config.paths.template (配置指定)
   - data_root/template/ (工作区默认)
   - ~/.config/mark2pdf/templates/ (全局模板)
   - mark2pdf.templates (包内置，importlib.resources)
   - code_root/template/ (开发回退)

2. **包内置模板打包**:
   - `pyproject.toml` 配置 `[tool.hatch.build.targets.wheel.force-include]`
   - 将 `template/` 目录打包到 wheel 中作为 `mark2pdf/templates/`
   - 使用 `importlib.resources.files()` 访问

3. **template 命令** (`mark2pdf template <name>`):
   - 将包内或开发目录中的模板复制到当前工作区
   - 支持文件和目录复制，目录优先
   - 依赖检测：自动复制模板引用的 `.typ` 依赖

4. **init --template 选项**:
   - 初始化时可指定复制单个模板

**设计要点**:
- `_get_template_source_context()` 使用 context manager 统一处理 traversable 和 Path
- 模板复制时自动解析 `#import` 和 `#image()` 依赖
- 错误信息列出所有已检查路径，便于调试

**测试**: 42 个相关测试全部通过

---

### 移除硬编码 `_working` 路径 + 独立模式

**修改文件**:
- `src/mark2pdf/config.py` (新增 `standalone` 字段)
- `src/mark2pdf/config_loader.py` (支持向上查找配置、独立模式)
- `src/mark2pdf/workspace_manager.py` (向上查找配置文件)
- `src/mark2pdf/cli.py` (重构为先加载配置再处理)
- `src/mark2pdf/conversion.py` (使用 `input_dir` 属性)
- `src/mark2pdf/commands/clean.py` (独立模式检测)
- `src/mark2pdf/commands/gaozhi.py` (Path 对象替代字符串拼接)
- `src/markdown2pdf/core.py`, `directory.py` (移除 `_working/in` 默认值)
- `src/helper_workingpath/helper_working_path.py` (配置驱动的目录结构)
- `src/helper_mdimage/mdimage_cli.py` (优先使用 ConfigManager)
- 相关测试文件 (使用真实 `Mark2pdfConfig` 替代 MagicMock)

**功能**:
1. **独立模式 (Standalone)**: 当未找到 `mark2pdf.config.toml` 时，自动使用当前目录作为工作区，`input`/`output` 默认为 `.`，支持无配置场景下直接转换。
2. **向上查找配置**: 配置解析从 CWD 开始向父目录逐级查找，支持在子目录中运行命令。
3. **消除硬编码**: 所有 `_working/in`、`_working/out` 硬编码路径替换为配置驱动。

**测试**: 89 个单元测试全部通过

---

## 2025-12-31

### core.py UnboundLocalError 修复

**修改文件**: `src/markdown2pdf/core.py`

**问题**: `success` 变量仅在 `try` 块内赋值，若 `run_pandoc_typst()` 抛出异常，`finally` 块中使用 `success` 会触发 `UnboundLocalError`

**修复**: 在 `try` 块前初始化 `success = False`

---

### createpdf -o/--open 选项

**修改文件**: 
- `src/markdown2pdf/core.py`, `directory.py` - 返回值从 `bool` 改为 `Path | None`
- `src/markdown2pdf/utils.py` - 新增 `open_with_system` 函数
- `src/config_manager/conversion.py` - 三个 `run_*` 函数添加 `open_file` 参数
- `src/markdown2pdf/cli.py`, `resources/createpdf.py` - CLI 添加 `-o` 选项
- `scripts/gaozhi.py` - 重构为 Click，支持 `--dir` 目录模式

**功能**: 转换完成后使用系统默认程序打开 PDF

**使用**:
```bash
createpdf test.md -o
md2pdf test.md -o
gaozhi.py sample.md -o
gaozhi.py --dir nlist -o  # 目录模式（逐个转换后合并）
```

**设计变更**: `convert_file`/`convert_directory` 返回 `Path | None` 而非 `bool`，成功时返回输出文件路径，便于后续操作（如打开文件）

---

### overview-fill frontmatter 控制

**修改文件**: `template/nb-lib.typ`, `template/nb.typ`

**功能**: 通过 frontmatter 的 `overview-fill` 字段统一控制 overview 块的默认背景色。

**使用**:
```yaml
---
overview-fill: lightblue  # 或 #E4F6F6 或 default
---
```

**优先级**: 块内 `fill=` > frontmatter `overview-fill` > 系统默认 `#FFF5F5`

---

### toc 禁用逻辑统一

**修改文件**: `template/nb.typ`

**变更**: 目录开关统一为 `disables: [toc]`

**原因**: pandoc 对布尔 `false` 不会输出变量，列表型 `disables` 仅在显式设置时输出，默认显示目录且可可靠禁用。

**同时修复**: `toc-depth` 默认值从 `0` 改为 `3`

---

### pagenumber 禁用逻辑统一

**修改文件**: `template/nb-lib.typ`, `template/nb.typ`

**功能**: 通过 frontmatter 的 `disables` 控制是否隐藏页码

**使用**:
```yaml
---
disables:
  - pagenumber
---
```

**实现**: 在页脚渲染逻辑中增加 `disable-pagenumber` 条件判断，同时处理有/无 `footer-image` 两种场景

---

### mark2pdf CLI 统一方案

**修改文件**:
- `pyproject.toml` - 移除 `md2pdf` 入口
- `src/config_manager/` → `src/mark2pdf/` - 包重命名
- `src/mark2pdf/cli.py` - 添加 `convert`, `version` 命令
- `src/markdown2pdf/postprocessors/` - 新建插件机制预留模块

**功能**:
- 统一使用 `mark2pdf` 命令入口
- `mark2pdf convert` 吸收原 `md2pdf` 功能
- `--show-config` 显示合并后的完整配置
- `--dry-run` 试运行模式
- `--postprocess` 插件接口预留
- 工作区自动检测（当前目录有 `mark2pdf.config.toml`）

**使用**:
```bash
mark2pdf version
mark2pdf convert sample.md
mark2pdf convert --show-config   # 调试配置
mark2pdf convert --dry-run       # 仅显示执行计划
```

**已知限制**: `--removelink` 参数已声明但 `convert` 命令尚未传递给底层函数


---

### postprocess 插件化与 CLI 重构

**修改文件**:
- `src/markdown2pdf/postprocessors/` (删除)
- `src/markdown2pdf/postprocess.py` (新增: 插件加载器)
- `src/postprocess/` (新增: 默认插件包)
  - `remove_links.py`
  - `to_traditional_chinese.py`
- `src/mark2pdf/cli.py` (修改: 组装插件链)
- `src/mark2pdf/conversion.py` (修改: 统一 postprocess 参数)
- `template/nb.typ` (修改: 标题字体回退列表)

**功能**:
- 将 `removelink` 和 `tc` (繁体转换) 逻辑从核心转换流中剥离，转为独立的可插拔 `postprocess` 处理器。
- `cli.py` 负责根据参数 (`--removelink`, `--tc`, `--postprocess`) 动态构建处理器链 (Chain of Responsibility)。
- `conversion.py` 不再感知具体的业务开关，仅接受一个 `Callable[[str], str]` 的 `postprocess` 函数。

**逻辑验证**:
- `src/postprocess/__init__.py` 显式导出 `remove_links` 和 `to_traditional_chinese` 函数，确保 `load_postprocessor` 中 `getattr(postprocess, name)` 能正确获取 Callable。


---

### mark2pdf CLI 深度重构

**修改文件**:
- `src/mark2pdf/cli.py` (拆分)
- `src/mark2pdf/manager.py` (删除)
- `src/mark2pdf/config_loader.py` (新增: 配置加载)
- `src/mark2pdf/workspace_manager.py` (新增: 工作区管理)
- `src/mark2pdf/process_builder.py` (新增: 处理器构建)
- `src/mark2pdf/reporter.py` (新增: 报告输出)
- `src/mark2pdf/utils.py` (新增: 工具函数)
- `src/mark2pdf/commands/` (新增: 子命令包)
  - `workspace.py` (init, update)
  - `image.py` (imageclearing)

**目的**:
解决 `cli.py` 职责过重的问题，实现配置加载、业务逻辑、UI展示、命令定义的彻底分离。

**变更点**:
1.  **逻辑拆分**:
    - 配置相关 -> `config_loader.py`
    - 工作区操作 -> `workspace_manager.py`
    - 后处理器组装 -> `process_builder.py`
    - `get_version` -> `utils.py`
2.  **命令归类**:
    - `init`, `update` 移至 `commands/workspace.py`
    - `imageclearing` 移至 `commands/image.py`
    - `cli.py` 仅保留注册入口和 `convert` 主命令。
3.  **测试适配**:
    - 更新 `test_cli.py` 和 `test_update.py` 中的 mock 路径，指向新的模块位置。

**结果**:
所有 34 个单元测试通过，模块结构清晰，易于扩展新命令。

### gaozhi 脚本 -> 命令迁移

**修改文件**:
- `scripts/gaozhi.py` (删除)
- `src/mark2pdf/commands/gaozhi.py` (新增)
- `src/mark2pdf/commands/__init__.py`
- `src/mark2pdf/cli.py`

**目的**:
将散落在 `scripts/` 的工具统一集成到 `mark2pdf` CLI 中，方便管理和使用。

**变更点**:
1.  **迁移**: 逻辑原样迁移，但改用 `mark2pdf.config_loader` 获取路径，不再硬编码 `_working/in`。
2.  **修复**:
    - 目录模式 (`--dir .`) 下，自动排除 `index.md`，避免重复转换或错误。
    - 目录模式 (`--dir .`) 下，合并输出文件名默认为 `gaozhi_merged.pdf`，不再是 `._gaozhi.pdf`。
3.  **注册**: 作为 `mark2pdf gaozhi` 子命令使用。

**限制**:
- 依然依赖 `pymupdf` 进行 PDF 合并。

---

### checkimagesize 脚本 -> coverprepare 命令迁移

**修改文件**:
- `scripts/checkimagesize.py` (删除)
- `src/mark2pdf/commands/coverprepare.py` (新增)
- `src/mark2pdf/commands/__init__.py`
- `src/mark2pdf/cli.py`
- `src/mark2pdf/tests/test_cli.py`

**功能**:
将图片尺寸检查工具集成到 `mark2pdf` CLI。

**命令**:
```bash
mark2pdf coverprepare check <image>
mark2pdf coverprepare list
```

**变更点**:
1.  **重命名**: 从 `checkimagesize` 变更为 `coverprepare`，意图更明确（封面准备）。
2.  **集成**: 只有 `mark2pdf` 一个入口，不再需要 `uv run scripts/...`。

---

### clean 命令与 version 重构

**修改文件**:
- `src/mark2pdf/commands/clean.py` (新增)
- `src/mark2pdf/commands/version.py` (新增)
- `src/mark2pdf/utils.py` (删除)
- `src/mark2pdf/cli.py`
- `src/mark2pdf/commands/__init__.py`
- `src/mark2pdf/tests/test_clean.py` (新增)

**功能**:
1.  **新增 `clean` 命令**:
    - 用于清理输出目录中的 PDF 文件。
    - **安全机制**: 仅删除 `.pdf` 文件，保留 `.log`、`.txt` 及子目录配置等。
    - **交互保障**: 默认需确认 (`y/n`)，支持 `--dry-run` 预览，强制删除需 `-f`。
2.  **重构 `version` 命令**:
    - 将 `version` 命令从 `cli.py` 移出至独立模块 `commands/version.py`。
    - 移除仅被 version 使用的 `utils.py`，将 `get_version` 函数就近放置。

**命令**:
```bash
# 清理 PDF
mark2pdf clean [--dry-run] [-f]


# 查看版本
mark2pdf version
```

---

### imageclearing → mdimage 命令组重构

**修改文件**:
- `src/mark2pdf/commands/image.py` (删除)
- `src/mark2pdf/commands/mdimage.py` (新增: Click Group，包含 download/todir/tofile)
- `src/mark2pdf/commands/__init__.py` (导出 mdimage 替换 imageclearing)
- `src/mark2pdf/cli.py` (注册 mdimage group)
- `src/helper_mdimage/mdimage_cli.py` (新增 `process_path_auto` 支持目录模式)
- `src/helper_mdimage/mdimage_migrate.py` (新增: StructureMigrator 结构迁移)
- `src/helper_mdimage/__init__.py` (导出新函数和类)
- `src/helper_mdimage/tests/test_mdimage.py` (新增 3 个测试)
- `src/helper_mdimage/tests/test_mdimage_migrate.py` (新增: 迁移逻辑测试)

**功能**:
将原本单一的 `imageclearing` 命令升级为 `mdimage` 命令组，支持图片下载（清理）和目录结构迁移。

**命令**:
```bash
# 1. 下载/清理图片 (原 imageclearing)
# 支持单文件 (Flat Mode) 和 文件夹 (Folder Mode) 自动识别
mark2pdf mdimage download input/abc.md
mark2pdf mdimage download input/abc/

# 2. 结构迁移: 单文件 -> 文件夹
# 转换: abc.md + images/abc/ -> abc/index.md + abc/images/
mark2pdf mdimage todir input/abc.md

# 3. 结构迁移: 文件夹 -> 单文件
# 转换: abc/index.md + abc/images/ -> abc.md + images/abc/
mark2pdf mdimage tofile input/abc/
```

**设计**:
- **Flat Mode**: Markdown 文件位于父目录，图片位于 `images/{stem}/`。
- **Folder Mode**: Markdown 文件位于子目录，图片位于同级 `images/`。
- **StructureMigrator**: 负责文件移动和 Markdown 内容中图片链接的批量重写。
- **process_path_auto**: 自动检测输入路径类型（文件/目录），统一处理逻辑。


### Web UI 重构与代码清理

**修改文件**:
- `web_server.py` (删除)
- `web_ui/web_server.py` (新增: 移动并重构)
- `web_ui/web_home.html` (新增: 提取 HTML 模板)
- `src/markdown2pdf/utils.py` (新增 `convert_to_traditional` 辅助函数)
- `src/markdown2pdf/core.py`, `directory.py`, `postprocess/to_traditional_chinese.py` (使用辅助函数)
- `docker-compose.yml` (配置优化: 优先使用内置字体路径)
- `pyproject.toml` (配置优化: 迁移 dev 依赖到 dependency-groups)

**功能**:
1.  **Web Server 结构优化**:
    - 逻辑与视图分离：HTML 代码从 Python 文件中剥离到 `web_ui/web_home.html`。
    - 服务端代码移动到 `web_ui/web_server.py`，保持根目录整洁。
    - HTML 模板支持动态注入变量 (`__OPTIONS_HTML_PLACEHOLDER__` 等)。

2.  **代码复用与清理**:
    - 在 `utils.py` 中集中封装 `OpenCC` 调用，消除重复代码。
    - 修复潜在的 `OpenCC` 导入分散问题。

3.  **构建与部署优化**:
    - `docker-compose.yml` 新增 `/usr/share/fonts/mark2pdf` 字体路径，适配容器化部署。
    - `pyproject.toml` 采纳 `uv` 推荐的 `[dependency-groups]` 格式管理开发依赖。

### force-filename 强制文件名选项

**修改文件**:
- `src/mark2pdf/cli.py` (新增 `--force-filename` 选项)
- `src/markdown2pdf/options.py` (新增 `force_filename` 字段)
- `src/markdown2pdf/utils.py` (更新 `get_output_filename` 逻辑)
- `src/markdown2pdf/core.py` (传递选项)
- `src/mark2pdf/conversion.py` (传递选项)

**功能**:
新增 CLI 选项 `--force-filename`，用于强制使用原始文件名作为输出文件名，忽略 frontmatter 中的 `exportFilename` 和 `title` 配置。

**使用**:
```bash
mark2pdf convert sample.md --force-filename
```

**场景**:
在某些自动化流程或调试中，希望输出文件名保持可预测，不受文件内部配置干扰。

---

### postprocess 加载机制优化

**修改文件**: `src/markdown2pdf/postprocess.py`

**功能**:
优化 `load_postprocessor` 函数，优先从当前工作区的 `postprocess/` 目录加载后处理器，未找到时再回退到内置的 `postprocess` 包。增强了其扩展性。

---

### moonspace postprocess 清理

**修改文件**: `moonspace/postprocess/__init__.py`

**功能**:
移除已被核心库支持的冗余处理器加载代码 (`remove_links`, `to_traditional_chinese`)，仅保留自定义的 `convert_divs`。

---

### bugfix: compile_overview 逻辑增强

**修改文件**: `src/postprocess/compile_overview.py`

**问题**:
原实现简单粗暴地将所有 `</div>` 替换为 `:::`，导致文档中其他 HTML div（如 `floatcomment`）闭合标签被错误转换，Pandoc 解析时因标签不匹配报错。

**修复**:
引入栈机制实现简易解析器：
1. 识别并跟踪 `overview` 和其他 div 的嵌套关系。
2. 仅当闭合 `overview` 块时，才将 `</div>` 转换为 `:::`。
3. 其他 div 的闭合标签保持原样。

---

### postprocess 健壮性修复

**修改文件**:
- `src/postprocess/compile_overview.py` (正则优化)
- `src/postprocess/tests/test_compile_overview.py` (新增测试)
- `pyproject.toml` (构建配置)

**变更内容**:

1. **正则优化**:
   - 优化 `compile_overview` 的正则匹配逻辑，支持 `className` 属性前后存在其他属性（例: `<div id="x" className="overview">`）。
   - 解决原正则仅能匹配 `className` 位于首位属性的局限性。

2. **构建修复**:
   - 在 `pyproject.toml` 中注册 `src/postprocess` 包。
   - 解决 `postprocess` 模块未包含在 wheel 构建中的问题。

3. **测试补充**:
   - 新增单元测试，覆盖属性顺序、空格差异及嵌套结构等场景。

---

### 配置聚合：pubinfo 结构化 (Frontmatter & Typst)

**修改文件**:
- `src/mark2pdf/resources/frontmatter.yaml`
- `template/nb.typ`
- `template/nb-lib.typ`

**变更内容**:
将分散的出版物相关配置聚合到 `pubinfo` 命名空间下，清理根级配置。

1. **聚合字段**:
   - `edition` -> `pubinfo.edition`
   - `logo` -> `pubinfo.logo`
   - `watermark` -> `pubinfo.watermark`
   - `tyfont` -> `pubinfo.font`
   - `tyfont_title` -> `pubinfo.titlefont`
   - `info` (新) -> `pubinfo.info` (对应原 `publication-info`)

2. **Typst 模板更新**:
   - `nb.typ` 模板参数调整，解构 `pubinfo` 字典。
   - 移除旧的平铺参数，统一通过 `pubinfo` 字典传入。

---

### 配置统一：Disables 机制 (Frontmatter & Typst)

**修改文件**:
- `src/mark2pdf/resources/frontmatter.yaml`
- `template/nb.typ`

**变更内容**:
废弃散乱的 `no-xxx` 布尔开关，统一使用 `disables` 数组列表。

1. **新配置格式**:
   ```yaml
   disables:
     - toc
     - cover
     - backcover
     - indent
     - pagenumber
     - header
   ```

2. **Typst 实现**:
   - `nb.typ` 中新增 `disabled(name)` 辅助函数。
   - 统一判断逻辑，替代原有的 `if not no-toc` 等判断。

---

### UI 优化：页脚布局调整

**修改文件**:
- `template/nb-lib.typ`

**变更内容**:
- 优化页脚 Grid 布局，确保页码和页脚文字垂直居中对齐。
- 调整页脚图片尺寸和间距，增强视觉平衡。

---

### Bugfix: 繁体中文文件名转换失效

**修改文件**:
- `src/markdown2pdf/core.py`
- `src/markdown2pdf/utils.py`

**问题**:
使用 `--tc` 参数时，`exportFilename` 未被正确转换为繁体中文。


---

### Refactor: compile_overview 正则增强与测试修复

**修改文件**:
- `src/postprocess/compile_overview.py`
- `src/postprocess/tests/test_compile_overview.py`

**变更内容**:
1. **正则增强**:
   - 恢复并增强 `OVERVIEW_OPEN_RE` 正则，支持 `className` 属性前存在其他属性（如 `id="foo"`）。
   - 解决因回退操作导致的正则倒退问题。

2. **测试修复**:
   - 修正测试文件中的导入路径，解决 `ModuleNotFoundError: No module named 'src'` 错误。
   - 验证所有测试用例通过。

3. **逻辑决策**:
   - 保留基于栈的嵌套解析逻辑，因为检测到项目中存在 `floatcomment` 等其他 div，简化逻辑会导致这些 div 闭合标签被错误转换。
