refactor: 重构外部模板系统,改为单文件模板库模式
主要变更: - 将 templates_dir 参数改为 template_file,支持单个模板库 YAML 文件 - 添加模板库 YAML 验证功能 - 为模板添加 base_dir 支持,正确解析相对路径资源 - 内联模板与外部模板同名时改为警告(内联优先) - 移除模板缓存机制,直接使用模板库字典 - 更新所有相关测试以适配新的模板加载方式 此重构简化了模板管理,使模板资源的路径解析更加清晰明确。
This commit is contained in:
143
openspec/specs/template-library/spec.md
Normal file
143
openspec/specs/template-library/spec.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Template Library
|
||||
|
||||
## Purpose
|
||||
|
||||
Template library 提供集中的模板管理机制,允许将多个模板定义存储在单个 YAML 文件中,通过模板名称引用。
|
||||
|
||||
## Requirements
|
||||
|
||||
### Requirement: 模板库文件必须包含 templates 字段
|
||||
|
||||
模板库文件 SHALL 包含必需的 `templates` 字段,该字段为字典类型,键为模板名称,值为模板定义。
|
||||
|
||||
#### Scenario: 验证模板库文件格式
|
||||
|
||||
- **WHEN** 系统加载模板库文件
|
||||
- **THEN** 系统验证文件包含 `templates` 字段
|
||||
- **AND** `templates` 字段必须为字典类型
|
||||
|
||||
#### Scenario: 模板库文件缺少 templates 字段时报错
|
||||
|
||||
- **WHEN** 模板库文件不包含 `templates` 字段
|
||||
- **THEN** 系统抛出错误,错误代码为 `TEMPLATE_LIBRARY_MISSING_TEMPLATES_FIELD`
|
||||
- **AND** 错误消息包含"缺少必需字段 'templates'"
|
||||
|
||||
#### Scenario: templates 字段类型错误时报错
|
||||
|
||||
- **WHEN** 模板库文件的 `templates` 字段不是字典类型
|
||||
- **THEN** 系统抛出错误,错误代码为 `TEMPLATE_LIBRARY_MISSING_TEMPLATES_FIELD`
|
||||
- **AND** 错误消息包含"'templates' 必须是字典"
|
||||
|
||||
### Requirement: 模板库文件必须支持元数据字段
|
||||
|
||||
模板库文件 SHALL 支持可选的元数据字段,包括 `description`、`version`、`author` 等。
|
||||
|
||||
#### Scenario: 加载包含元数据的模板库文件
|
||||
|
||||
- **WHEN** 模板库文件包含 `description`、`version`、`author` 等字段
|
||||
- **THEN** 系统成功加载这些元数据字段
|
||||
- **AND** 元数据不影响模板的加载和使用
|
||||
|
||||
#### Scenario: 不包含元数据字段时正常加载
|
||||
|
||||
- **WHEN** 模板库文件仅包含 `templates` 字段,不包含任何元数据
|
||||
- **THEN** 系统正常加载,不要求元数据字段
|
||||
|
||||
### Requirement: 模板库文件必须递归验证每个模板的结构
|
||||
|
||||
系统 SHALL 对模板库中的每个模板递归验证其结构,包括 `vars` 和 `elements` 字段的正确性。
|
||||
|
||||
#### Scenario: 验证模板的 vars 字段
|
||||
|
||||
- **WHEN** 模板库中的模板定义了 `vars` 字段
|
||||
- **THEN** 系统验证 `vars` 为列表类型
|
||||
- **AND** 验证每个变量定义包含 `name` 字段
|
||||
- **AND** 如果 `vars` 存在但不为列表,抛出错误
|
||||
|
||||
#### Scenario: 验证模板的 elements 字段
|
||||
|
||||
- **WHEN** 模板库中的模板定义了 `elements` 字段
|
||||
- **THEN** 系统验证 `elements` 为列表类型
|
||||
- **AND** 验证 `elements` 字段存在且不为空
|
||||
- **AND** 如果 `elements` 不存在或为空,抛出错误
|
||||
|
||||
#### Scenario: 验证多个模板的结构
|
||||
|
||||
- **WHEN** 模板库包含多个模板定义
|
||||
- **THEN** 系统验证每个模板的结构完整性
|
||||
- **AND** 如果某个模板结构错误,错误消息包含模板名称和具体位置
|
||||
|
||||
### Requirement: 系统必须从模板库文件按名称查询模板
|
||||
|
||||
系统 SHALL 支持通过模板名称从模板库文件中查询模板,查询方式为字典键查找。
|
||||
|
||||
#### Scenario: 通过名称从模板库加载模板
|
||||
|
||||
- **WHEN** 幻灯片指定 `template: title-slide`
|
||||
- **AND** 用户提供 `--template /path/to/templates.yaml`
|
||||
- **THEN** 系统从模板库文件的 `templates.title-slide` 加载模板定义
|
||||
|
||||
#### Scenario: 模板名称不存在时报错
|
||||
|
||||
- **WHEN** 幻灯片引用的模板名称在模板库中不存在
|
||||
- **THEN** 系统抛出错误,错误代码为 `TEMPLATE_NOT_FOUND_IN_LIBRARY`
|
||||
- **AND** 错误消息包含"模板库中找不到指定模板名称: <模板名>"
|
||||
|
||||
#### Scenario: 模板库文件不存在时报错
|
||||
|
||||
- **WHEN** 用户提供的 `--template` 文件路径不存在
|
||||
- **THEN** 系统抛出错误,错误代码为 `TEMPLATE_LIBRARY_FILE_NOT_FOUND`
|
||||
- **AND** 错误消息包含"模板库文件不存在"
|
||||
|
||||
### Requirement: 模板库中的图片资源路径必须相对于模板库文件所在目录
|
||||
|
||||
系统 SHALL 在渲染外部模板时,将图片元素的相对路径解析为相对于模板库文件所在目录的绝对路径。
|
||||
|
||||
#### Scenario: 解析模板中的图片相对路径
|
||||
|
||||
- **WHEN** 模板库文件位于 `/lib/theme.yaml`
|
||||
- **AND** 模板中的图片元素定义 `src: "./assets/logo.png"`
|
||||
- **THEN** 系统将路径解析为 `/lib/assets/logo.png`
|
||||
|
||||
#### Scenario: 图片绝对路径保持不变
|
||||
|
||||
- **WHEN** 模板中的图片元素定义绝对路径 `src: "/usr/share/images/logo.png"`
|
||||
- **THEN** 系统不修改该路径
|
||||
|
||||
#### Scenario: 图片路径解析失败时显示完整路径
|
||||
|
||||
- **WHEN** 解析后的图片路径不存在
|
||||
- **THEN** 错误消息显示解析后的绝对路径
|
||||
- **AND** 用户可以准确找到问题文件位置
|
||||
|
||||
### Requirement: 模板库加载不应缓存模板
|
||||
|
||||
系统 SHALL 每次从模板库加载模板时创建新的模板对象,不使用缓存机制。
|
||||
|
||||
#### Scenario: 每次加载创建新对象
|
||||
|
||||
- **WHEN** 多个幻灯片使用同一个模板名称
|
||||
- **THEN** 系统每次都从模板库重新加载模板定义
|
||||
- **AND** 每次创建新的 Template 对象
|
||||
|
||||
#### Scenario: 模板库文件修改后立即生效
|
||||
|
||||
- **WHEN** 模板库文件在运行时被修改
|
||||
- **THEN** 下一次加载模板时使用新的定义
|
||||
- **AND** 不需要重启程序
|
||||
|
||||
### Requirement: 命令行参数必须支持 --template 指定模板库文件
|
||||
|
||||
系统 SHALL 支持 `--template` 参数指定模板库文件路径,替代原有的 `--template-dir` 参数。
|
||||
|
||||
#### Scenario: 使用 --template 参数
|
||||
|
||||
- **WHEN** 用户执行 `yaml2pptx.py convert input.yaml --template /path/to/theme.yaml`
|
||||
- **THEN** 系统加载 `/path/to/theme.yaml` 作为模板库文件
|
||||
- **AND** 可以从该文件引用模板
|
||||
|
||||
#### Scenario: --template 参数可选
|
||||
|
||||
- **WHEN** 演示文稿仅使用内联模板或自定义元素
|
||||
- **THEN** 用户可以不提供 `--template` 参数
|
||||
- **AND** 系统正常处理
|
||||
@@ -113,53 +113,91 @@ Template system 提供可复用的幻灯片布局和样式定义。模板包含
|
||||
- **THEN** 系统使用新的 simpleeval 引擎正确评估该表达式
|
||||
|
||||
|
||||
### Requirement: 模板文件必须可从指定目录加载
|
||||
### Requirement: 模板文件必须可从指定位置加载
|
||||
|
||||
系统 SHALL 从用户通过 `--template-dir` 参数指定的目录加载模板文件,支持通过模板名称引用。模板名称必须是纯文件名,不能包含路径分隔符。
|
||||
系统 SHALL 支持从两个位置加载模板:内联模板(在文档 YAML 的 `templates` 字段中定义)和外部模板(通过 `--template` 参数指定的模板库文件)。
|
||||
|
||||
#### Scenario: 通过名称加载模板
|
||||
#### Scenario: 从内联模板加载
|
||||
|
||||
- **WHEN** 幻灯片指定 `template: title-slide`,且用户提供 `--template-dir /path/to/templates`
|
||||
- **THEN** 系统从 `/path/to/templates/title-slide.yaml` 加载模板文件
|
||||
- **WHEN** 幻灯片指定 `template: title-slide`
|
||||
- **AND** 文档 YAML 的 `templates` 字段定义了 `title-slide` 模板
|
||||
- **THEN** 系统从内联模板字典中加载模板定义
|
||||
|
||||
#### Scenario: 模板文件不存在时报错
|
||||
#### Scenario: 从模板库文件加载
|
||||
|
||||
- **WHEN** 幻灯片引用不存在的模板名称
|
||||
- **THEN** 系统抛出错误,提示"模板文件不存在: <模板名>",并显示查找位置和期望文件路径
|
||||
- **WHEN** 幻灯片指定 `template: content-slide`
|
||||
- **AND** 用户提供 `--template /path/to/theme.yaml`
|
||||
- **AND** 模板库文件的 `templates.content-slide` 存在
|
||||
- **THEN** 系统从模板库文件中加载模板定义
|
||||
|
||||
#### Scenario: 缓存已加载的模板
|
||||
#### Scenario: 模板库文件不存在时报错
|
||||
|
||||
- **WHEN** 多个幻灯片使用同一个模板
|
||||
- **THEN** 系统仅加载一次模板文件,后续使用缓存
|
||||
- **WHEN** 幻灯片引用外部模板,但 `--template` 指定的文件不存在
|
||||
- **THEN** 系统抛出错误,错误代码为 `TEMPLATE_LIBRARY_FILE_NOT_FOUND`
|
||||
|
||||
#### Scenario: 错误信息包含详细的查找信息
|
||||
#### Scenario: 模板名称在两者中都不存在时报错
|
||||
|
||||
- **WHEN** 模板文件未找到
|
||||
- **THEN** 错误信息包含:模板名称、查找位置(template_dir)、期望文件的完整路径、解决建议
|
||||
- **WHEN** 幻灯片引用的模板名称既不在内联模板中,也不在模板库中
|
||||
- **THEN** 系统抛出错误,错误代码为 `TEMPLATE_NOT_FOUND_IN_LIBRARY`
|
||||
- **AND** 错误消息包含"模板库中找不到指定模板名称"
|
||||
|
||||
### Requirement: 内联和外部模板同名时必须发出警告
|
||||
|
||||
系统 SHALL 检测内联模板和外部模板的同名冲突,返回 WARNING 级别的验证问题,并优先使用内联模板。
|
||||
|
||||
#### Scenario: 同名冲突时发出警告
|
||||
|
||||
- **WHEN** 幻灯片引用的模板名称同时存在于内联模板和模板库中
|
||||
- **THEN** 系统生成 WARNING 级别的验证问题
|
||||
- **AND** 错误代码为 `TEMPLATE_NAME_CONFLICT`
|
||||
- **AND** 错误消息包含"模板名称冲突: '<name>' 同时存在于内联模板和外部模板库"
|
||||
- **AND** 系统优先使用内联模板
|
||||
|
||||
#### Scenario: 同名冲突时优先使用内联模板
|
||||
|
||||
- **WHEN** 内联模板和外部模板都定义了 `title-slide`
|
||||
- **AND** 幻灯片引用 `template: title-slide`
|
||||
- **THEN** 系统使用内联模板的定义
|
||||
- **AND** 忽略模板库中的同名模板
|
||||
|
||||
#### Scenario: 无冲突时正常加载
|
||||
|
||||
- **WHEN** 模板名称仅存在于内联模板或外部模板库中
|
||||
- **THEN** 系统正常加载,不发出警告
|
||||
|
||||
### Requirement: 系统必须支持自定义幻灯片
|
||||
|
||||
系统 SHALL 支持不使用模板的自定义幻灯片,以及同时使用模板和自定义元素的混合模式幻灯片。
|
||||
系统 SHALL 支持不使用模板的自定义幻灯片,以及同时使用模板和自定义元素的混合模式幻灯片。自定义元素的图片相对路径应相对于文档 YAML 所在目录解析。
|
||||
|
||||
#### Scenario: 渲染自定义幻灯片
|
||||
|
||||
- **WHEN** 幻灯片未指定 `template` 字段,直接包含 `elements` 列表
|
||||
- **THEN** 系统跳过模板渲染,直接处理元素列表
|
||||
- **AND** 自定义元素的图片相对路径相对于文档 YAML 所在目录解析
|
||||
|
||||
#### Scenario: 自定义幻灯片中直接指定样式
|
||||
|
||||
- **WHEN** 自定义幻灯片的元素直接指定 `color: "#4a90e2"`
|
||||
- **THEN** 系统正确应用该颜色值
|
||||
|
||||
#### Scenario: 自定义元素的图片路径解析
|
||||
|
||||
- **WHEN** 文档 YAML 位于 `/doc/presentation.yaml`
|
||||
- **AND** 自定义元素包含图片 `src: "./images/chart.png"`
|
||||
- **THEN** 系统将路径解析为 `/doc/images/chart.png`
|
||||
|
||||
#### Scenario: 自定义幻灯片和模板混合使用
|
||||
|
||||
- **WHEN** 演示文稿中部分幻灯片使用模板,部分为自定义
|
||||
- **THEN** 系统正确处理两种类型的幻灯片
|
||||
- **AND** 模板元素的图片相对于模板库目录,自定义元素的图片相对于文档目录
|
||||
|
||||
#### Scenario: 混合模式幻灯片同时使用模板和自定义元素
|
||||
|
||||
- **WHEN** 幻灯片同时指定了 `template` 字段和 `elements` 列表
|
||||
- **THEN** 系统先渲染模板获取模板元素列表,再追加自定义元素列表,生成最终的元素列表
|
||||
- **AND** 模板元素的图片已解析为绝对路径(相对于模板库)
|
||||
- **AND** 自定义元素的图片已解析为绝对路径(相对于文档)
|
||||
|
||||
#### Scenario: 混合模式中模板元素在前
|
||||
|
||||
@@ -185,49 +223,6 @@ Template system 提供可复用的幻灯片布局和样式定义。模板包含
|
||||
- **WHEN** 模板包含复杂的嵌套结构,多层使用变量引用
|
||||
- **THEN** 系统递归解析所有层级的变量,直到无变量引用为止
|
||||
|
||||
### Requirement: 模板名称必须是纯文件名
|
||||
|
||||
系统 SHALL 验证模板名称不包含路径分隔符,确保模板只能从指定目录的一层加载。
|
||||
|
||||
#### Scenario: 拒绝包含正斜杠的模板名称
|
||||
|
||||
- **WHEN** 幻灯片指定 `template: subdir/title-slide`
|
||||
- **THEN** 系统抛出错误,提示"模板名称不能包含路径分隔符: subdir/title-slide"
|
||||
|
||||
#### Scenario: 拒绝包含反斜杠的模板名称
|
||||
|
||||
- **WHEN** 幻灯片指定 `template: subdir\title-slide`
|
||||
- **THEN** 系统抛出错误,提示"模板名称不能包含路径分隔符: subdir\title-slide"
|
||||
|
||||
#### Scenario: 拒绝路径遍历尝试
|
||||
|
||||
- **WHEN** 幻灯片指定 `template: ../other-templates/slide`
|
||||
- **THEN** 系统抛出错误,提示模板名称不能包含路径分隔符
|
||||
|
||||
#### Scenario: 接受纯文件名
|
||||
|
||||
- **WHEN** 幻灯片指定 `template: title-slide`(不包含路径分隔符)
|
||||
- **THEN** 系统正常处理,从指定的 template_dir 加载模板
|
||||
|
||||
#### Scenario: 错误信息提供正确格式示例
|
||||
|
||||
- **WHEN** 系统因模板名称包含路径分隔符而报错
|
||||
- **THEN** 错误信息中包含"模板名称应该是纯文件名,如: 'title-slide'"的提示
|
||||
|
||||
### Requirement: 未指定模板目录时必须报错
|
||||
|
||||
系统 SHALL 在用户未提供 `--template-dir` 参数但 YAML 文件中使用了模板时,立即报错。
|
||||
|
||||
#### Scenario: 使用模板但未指定目录
|
||||
|
||||
- **WHEN** YAML 文件中包含 `template: title-slide`,但 `templates_dir` 参数为 `None`
|
||||
- **THEN** 系统在尝试加载模板时抛出错误,提示"未指定模板目录,无法加载模板"
|
||||
|
||||
#### Scenario: 不使用模板时不检查目录
|
||||
|
||||
- **WHEN** YAML 文件中所有幻灯片都是自定义幻灯片(不包含 `template` 字段)
|
||||
- **THEN** 系统不检查 `templates_dir` 是否为 `None`,正常处理
|
||||
|
||||
### Requirement: 幻灯片定义必须支持 enabled 字段
|
||||
|
||||
幻灯片定义 SHALL 支持可选的 `enabled` 布尔字段,用于控制该幻灯片是否渲染。该字段与模板系统的其他字段(template、vars、elements、background)独立工作。
|
||||
@@ -311,19 +306,27 @@ Template system 提供可复用的幻灯片布局和样式定义。模板包含
|
||||
|
||||
### Requirement: 混合模式必须支持内联模板
|
||||
|
||||
系统 SHALL 在混合模式中支持内联模板与外部模板,功能保持一致。
|
||||
系统 SHALL 在混合模式中支持内联模板与外部模板库,功能保持一致。
|
||||
|
||||
#### Scenario: 内联模板与自定义元素混合使用
|
||||
|
||||
- **WHEN** 幻灯片引用内联模板(在 YAML 文件的 `templates` 字段中定义),同时包含 `elements` 列表
|
||||
- **THEN** 系统正确渲染内联模板元素,并追加自定义元素
|
||||
- **WHEN** 幻灯片引用内联模板(在 YAML 文件的 `templates` 字段中定义)
|
||||
- **AND** 同时包含 `elements` 列表
|
||||
- **THEN** 系统正确渲染内联模板元素
|
||||
- **AND** 追加自定义元素
|
||||
- **AND** 图片路径正确解析(内联模板相对于文档目录)
|
||||
|
||||
#### Scenario: 外部模板与自定义元素混合使用
|
||||
|
||||
- **WHEN** 幻灯片引用外部模板(从 `--template-dir` 目录加载),同时包含 `elements` 列表
|
||||
- **THEN** 系统正确加载外部模板,渲染模板元素,并追加自定义元素
|
||||
- **WHEN** 幻灯片引用外部模板(从 `--template` 指定的模板库加载)
|
||||
- **AND** 同时包含 `elements` 列表
|
||||
- **THEN** 系统正确加载外部模板
|
||||
- **AND** 渲染模板元素
|
||||
- **AND** 追加自定义元素
|
||||
- **AND** 图片路径正确解析(外部模板相对于模板库目录)
|
||||
|
||||
#### Scenario: 内联和外部模板在同一演示文稿中混合使用
|
||||
|
||||
- **WHEN** 演示文稿同时定义了内联模板和使用外部模板,且部分幻灯片使用混合模式
|
||||
- **WHEN** 演示文稿同时定义了内联模板和使用外部模板
|
||||
- **AND** 部分幻灯片使用混合模式
|
||||
- **THEN** 系统正确处理所有组合情况
|
||||
|
||||
Reference in New Issue
Block a user