1
0
Files
PPTX/openspec/changes/archive/2026-03-05-refactor-external-template-system/design.md
lanyuanxiaoyao f1aae96a04 refactor: 重构外部模板系统,改为单文件模板库模式
主要变更:
- 将 templates_dir 参数改为 template_file,支持单个模板库 YAML 文件
- 添加模板库 YAML 验证功能
- 为模板添加 base_dir 支持,正确解析相对路径资源
- 内联模板与外部模板同名时改为警告(内联优先)
- 移除模板缓存机制,直接使用模板库字典
- 更新所有相关测试以适配新的模板加载方式

此重构简化了模板管理,使模板资源的路径解析更加清晰明确。
2026-03-05 13:27:12 +08:00

239 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## Context
### 当前状态
当前项目有两种模板系统:
1. **内联模板**:在文档 YAML 的 `templates` 字段定义,使用字典结构
2. **外部模板**:通过 `--template-dir` 指定文件夹,每个模板是单独的 `.yaml` 文件
两种系统的查询逻辑不一致:
- 内联模板:字典键查找 `data['templates'][name]`
- 外部模板:文件系统查找 `(templates_dir / name).yaml.exists()`
### 约束条件
- 必须保持中文注释和文档
- 使用 uv 运行 Python 脚本
- 不能修改主机环境配置
- 需要更新 README.md 和 README_DEV.md
### 相关利益方
- 使用外部模板的现有用户(需要迁移)
- 模板开发者(需要了解新的模板库格式)
## Goals / Non-Goals
**Goals:**
1. 统一内联模板和外部模板的数据结构和查询逻辑
2. 支持模板库元数据description、version、author
3. 统一资源路径解析,避免相对路径错误
4. 简化模板的分发和管理(单个文件 vs 多个文件)
5. 优化错误提示,区分不同类型的错误
**Non-Goals:**
1. 不支持向后兼容 `--template-dir` 参数
2. 不支持模板库文件的热重载(文件监听仅在文档级别)
3. 不改变模板的 vars 和 elements 结构
## Decisions
### 1. 命令行参数命名:使用 `--template`
**决策**: 命令行参数从 `--template-dir` 改为 `--template`
**理由**:
- `--template` 更简洁,直接表示指定模板文件
- 与内联模板的概念更一致(都是指定模板来源)
- 避免与 slide 中的 `template` 字段混淆(上下文清晰区分)
**考虑的替代方案**:
- `--template-file`: 更明确但冗长
- `--template-lib`: 表示模板库,但不如 `--template` 直观
### 2. 模板库文件格式:包含 `templates` 字典
**决策**: 模板库文件使用以下格式:
```yaml
# 元数据字段(可选)
description: "模板库描述"
version: "1.0.0"
author: "作者"
# 必需字段
templates:
template-name-1:
vars: ...
elements: ...
template-name-2:
vars: ...
elements: ...
```
**理由**:
- `templates` 作为必需字段,验证时检查其存在性
- 元数据字段放在顶层,便于人类阅读和工具解析
- 与内联模板的结构保持一致
**考虑的替代方案**:
- 将所有内容放在 `templates` 下:会增加嵌套层级
- 使用不同的必需字段名:`templates` 语义最清晰
### 3. 资源路径解析:提前解析为绝对路径
**决策**: 在模板渲染阶段就将图片相对路径解析为绝对路径
**实现位置**:
- 外部模板:在 `Template.render()` 方法中解析
- 自定义元素:在 `Presentation.render_slide()` 中解析
**路径规则**:
- 外部模板元素:`base_dir = 模板库文件所在目录`
- 内联模板元素:`base_dir = 文档 YAML 所在目录`
- 自定义元素:`base_dir = 文档 YAML 所在目录`
**理由**:
- 避免在渲染器中传递多个 `base_path`
- 元素数据在渲染时就是绝对路径,避免后续错误
- 符合"尽早解析"的设计原则
**考虑的替代方案**:
- 在渲染时根据元素来源动态选择 base_path需要在元素对象上标记来源增加复杂度
- 在渲染器中检查元素路径是否已解析:增加运行时开销
### 4. 移除外部模板缓存机制
**决策**: 移除 `Presentation.template_cache`,每次都从模板库创建新模板
**理由**:
- 模板库文件是单个 YAML加载开销小
- 简化代码逻辑,减少状态管理
- 避免缓存一致性问题(模板库文件修改后)
**考虑的替代方案**:
- 保留缓存并添加失效机制:增加复杂度,收益不大
- 使用 LRU 缓存:对于少量模板没有必要
### 5. 同名冲突处理WARNING + 优先内联
**决策**:
- 检测到同名冲突时,返回 `ValidationIssue` 级别 `WARNING`
- 优先使用内联模板
- 记录警告信息到验证结果
**理由**:
- 内联模板是文档的一部分,优先级更高
- WARNING 不会阻止验证,但提醒用户注意
- 保持向后兼容(之前的行为是 ERROR
**考虑的替代方案**:
- 抛出 ERROR过于严格与之前行为不一致
- 优先外部模板:内联模板更贴近文档,应该优先
### 6. 错误类型区分
**决策**: 区分三种错误类型
| 错误类型 | 错误代码 | 级别 | 条件 |
|---------|---------|------|------|
| 模板库文件不存在 | `TEMPLATE_LIBRARY_FILE_NOT_FOUND` | ERROR | `--template` 指定的文件不存在 |
| 缺少 templates 字段 | `TEMPLATE_LIBRARY_MISSING_TEMPLATES_FIELD` | ERROR | 模板库文件没有 `templates` 字段 |
| 模板名称不存在 | `TEMPLATE_NOT_FOUND_IN_LIBRARY` | ERROR | 模板库中找不到指定的模板名称 |
| 同名冲突 | `TEMPLATE_NAME_CONFLICT` | WARNING | 内联和外部模板同名 |
**理由**:
- 帮助用户快速定位问题
- 错误消息更精确,减少调试时间
## Risks / Trade-offs
### Risk 1: 现有用户迁移成本
**风险**: 使用 `--template-dir` 的用户需要手动迁移模板
**缓解措施**:
- 错误消息中提示使用新的参数格式
### Risk 2: 模板库文件格式错误
**风险**: 用户创建的模板库文件格式不正确
**缓解措施**:
- 递归验证每个模板的结构
- 提供详细的错误消息,指出具体位置
- 在文档中提供完整示例
### Risk 3: 资源路径解析错误
**风险**: 相对路径解析后,模板库文件移动导致路径失效
**缓解措施**:
- 错误消息中显示解析后的绝对路径
- 建议用户使用绝对路径或将资源放在相对稳定的位置
### Trade-off 1: 不支持向后兼容
**权衡**: 简化代码 vs 用户迁移成本
**决策**: 选择简化代码,不向后兼容
**理由**:
- 项目处于活跃开发阶段,破坏性变更可接受
- 统一的架构带来长期收益
- 迁移成本可控(单个模板库文件)
### Trade-off 2: 移除缓存 vs 性能
**权衡**: 简化代码 vs 略微的性能损失
**决策**: 选择简化代码
**理由**:
- 模板数量通常不多(< 100
- YAML 加载开销很小
- 代码简洁性更重要
## Migration Plan
### 迁移步骤
1. **代码变更**
- 按照任务列表依次修改各模块
- 每个模块修改后运行对应测试
2. **测试更新**
- 移除旧模板系统的相关测试
- 设计使用新模板系统的测试用例
- 添加模板库文件的测试用例
- 添加资源路径解析的测试用例
3. **文档更新**
- 更新 README.md新的命令行参数格式
- 更新 README_DEV.md模板库文件格式说明
- 添加迁移指南
### 回滚策略
如果发现严重问题:
1. 回滚代码到变更前的 commit
2. 重新发布旧版本
3. 修复问题后再次发布
## Open Questions
1. **是否需要提供模板库文件生成工具?**
- 当前未计划,手动创建 YAML 文件即可
- 如果有大量需求,未来可考虑
2. **是否需要支持模板库文件的远程 URL**
- 当前未计划,仅支持本地文件
- 未来可通过 HTTP 下载支持
3. **模板库元数据字段是否需要验证?**
- 当前不验证,用户可自定义
- 未来可考虑定义标准字段(如 license、homepage