1
0

refactor: 重构文档结构,采用渐进式信息披露模式

将 README.md 拆分为多个专题文档,减少认知负荷:
- 用户文档迁移到 docs/ (用户指南、元素、模板、参考等)
- 开发文档迁移到 docs/development/ (架构、模块、规范)
- README.md 精简至 ~290 行,仅保留概览和导航
- 删除 README_DEV.md,内容已迁移
- 归档 OpenSpec 变更 refactor-docs-progressive-disclosure
This commit is contained in:
2026-03-06 15:11:36 +08:00
parent 98098dc911
commit 124ef0e5ce
41 changed files with 5238 additions and 2228 deletions

24
docs/templates/_index.md vendored Normal file
View File

@@ -0,0 +1,24 @@
# 模板系统文档
本目录包含模板系统的详细说明。
## 模板类型
- [内联模板](inline.md) - 在源文件中定义模板
- [外部模板库](external-library.md) - 独立的模板库文件
- [混合模式](mixing-mode.md) - 模板与自定义元素组合
- [条件渲染](condition-rendering.md) - 元素和页面的条件显示
## 使用指南
- **简单场景**:使用内联模板
- **跨文档复用**:使用外部模板库
- **灵活布局**:使用混合模式
- **动态内容**:使用条件渲染
## 相关文档
- [字体主题系统](../fonts.md) - 字体配置
- [用户指南](../user-guide.md) - 完整使用说明
[返回文档索引](../README.md)

180
docs/templates/condition-rendering.md vendored Normal file
View File

@@ -0,0 +1,180 @@
# 条件渲染
条件渲染允许你根据变量值控制元素和幻灯片的显示。
## 元素级条件渲染
使用 `visible` 属性控制元素显示,支持强大的条件表达式:
### 基本示例
```yaml
# 简单比较
- type: text
content: "有数据"
visible: "{count > 0}"
# 字符串比较
- type: text
content: "草稿状态"
visible: "{status == 'draft'}"
# 非空检查(向后兼容)
- type: text
content: "{subtitle}"
visible: "{subtitle != ''}"
```
### 支持的表达式类型
#### 1. 比较运算
`==`, `!=`, `>`, `<`, `>=`, `<=`
```yaml
visible: "{score >= 60}"
visible: "{price <= 100}"
```
#### 2. 逻辑运算
`and`, `or`, `not`
```yaml
visible: "{count > 0 and status == 'active'}"
visible: "{is_draft or is_preview}"
visible: "{not (count == 0)}"
```
#### 3. 成员测试
`in`, `not in`
```yaml
visible: "{status in ['draft', 'review', 'published']}"
visible: "{level in (1, 2, 3)}"
visible: "{'test' in version}" # 字符串包含
```
#### 4. 数学运算
`+`, `-`, `*`, `/`, `%`, `**`
```yaml
visible: "{(price * discount) > 50}"
visible: "{(total / count) >= 10}"
```
#### 5. 内置函数
`int()`, `float()`, `str()`, `len()`, `bool()`, `abs()`, `min()`, `max()`
```yaml
visible: "{len(items) > 0}"
visible: "{int(value) > 100}"
```
### 复杂条件示例
```yaml
# 范围检查
- type: text
content: "评分: {score}"
visible: "{score >= 60 and score <= 100}"
# 多条件组合
- type: text
content: "管理员或高分用户"
visible: "{is_admin or (score >= 90)}"
# 嵌套条件
- type: text
content: "符合条件"
visible: "{((count > 0) and (status == 'active')) or (is_admin and (level >= 3))}"
```
## 页面级启用控制
使用 `enabled` 参数控制整个幻灯片是否渲染:
```yaml
slides:
# 正常渲染的幻灯片
- template: title-slide
vars:
title: "主标题"
# 临时禁用的幻灯片(开发调试)
- enabled: false
template: work-in-progress
vars:
title: "未完成的内容"
# 继续渲染后续幻灯片
- template: content-slide
vars:
title: "内容页"
```
### enabled 参数说明
- **类型**:布尔值(`true``false`
- **默认值**`true`(未指定时默认启用)
- **用途**:临时禁用幻灯片,无需删除或注释 YAML 内容
- **场景**开发调试、版本控制、A/B 测试
### enabled vs visible 的区别
| 特性 | `enabled`(页面级) | `visible`(元素级) |
|------|-------------------|-------------------|
| 作用范围 | 整个幻灯片 | 单个元素 |
| 类型 | 布尔值 | 条件表达式 |
| 判断时机 | 加载时(静态) | 渲染时(动态) |
| 使用场景 | 临时禁用页面 | 条件显示元素 |
### 示例
```yaml
slides:
# 页面启用,但副标题元素可能隐藏
- enabled: true
template: title-slide
vars:
title: "标题"
subtitle: "" # 空字符串,元素级 visible 会隐藏副标题
# 整页禁用,不渲染
- enabled: false
elements:
- type: text
content: "这一页不会出现在最终 PPTX 中"
box: [1, 1, 8, 1]
font: {size: 44}
```
## 安全策略
条件表达式评估使用 simpleeval 库,具有以下安全限制:
- 表达式最大长度限制500 字符
- 禁止属性访问obj.attr
- 禁止函数定义lambda, def
- 禁止模块导入import
- 白名单函数限制
## 错误处理
常见错误信息:
- `条件表达式中的变量未定义` - 使用了未在 vars 中定义的变量
- `条件表达式中使用了不支持的函数` - 使用了白名单之外的函数
- `条件表达式使用了不支持的语法特性` - 使用了禁止的语法
- `不支持属性访问` - 尝试访问对象属性
- `条件表达式语法错误` - 表达式语法有误
## 相关文档
- [内联模板](inline.md) - 在源文件中定义模板
- [混合模式](mixing-mode.md) - 模板与自定义元素组合
[返回文档索引](../README.md)

192
docs/templates/external-library.md vendored Normal file
View File

@@ -0,0 +1,192 @@
# 外部模板库
外部模板库是一个包含多个模板的 YAML 文件,适合跨文档复用和团队共享。
## 创建模板库文件
创建模板库文件 `templates.yaml`
```yaml
# 模板库元数据(必需)
metadata:
size: "16:9" # 必需:模板库尺寸,必须与使用它的文档一致
description: "公司标准模板库" # 可选:描述信息
fonts: # 可选:模板库字体主题
template-title:
family: "cjk-sans"
size: 44
bold: true
color: "#2c3e50"
template-body:
family: "sans"
size: 20
color: "#34495e"
fonts_default: "@template-body" # 可选:模板库默认字体
# 模板定义(必需)
templates:
title-slide:
description: "标题页模板"
vars:
- name: title
required: true
- name: subtitle
required: false
default: ""
elements:
- type: text
box: [1, 2, 8, 1]
content: "{title}"
font: "@template-title" # 引用模板库字体
- type: text
box: [1, 3.5, 8, 0.5]
content: "{subtitle}"
visible: "{subtitle != ''}"
font:
parent: "@template-title"
size: 24
align: center
content-slide:
description: "内容页模板"
vars:
- name: title
required: true
- name: content
required: true
elements:
- type: text
box: [1, 1, 8, 0.8]
content: "{title}"
font:
parent: "@template-title"
size: 32
- type: text
box: [1, 2, 8, 3]
content: "{content}"
# 未指定 font 时使用 fonts_default
```
## 重要说明
### 1. metadata 字段是必需的
- `metadata.size` 必须指定("16:9" 或 "4:3"
- 模板库的 size 必须与使用它的文档 size 一致,否则会报错
### 2. 字体主题系统
- 模板库可以定义自己的字体主题(`metadata.fonts`
- 文档可以引用模板库的字体(跨域引用)
- 模板库不能引用文档的字体(单向引用)
- 模板库的 `fonts_default` 只能引用模板库内部字体
### 3. 字体级联规则
- 模板元素未指定字体时,使用模板库的 `fonts_default`
- 如果模板库没有 `fonts_default`,使用文档的 `fonts_default`
- 如果都没有,使用系统默认字体
## 使用外部模板库
在命令行中指定模板库文件:
```bash
uv run yaml2pptx.py convert presentation.yaml output.pptx --template ./templates.yaml
```
在 YAML 文件中引用模板:
```yaml
slides:
- template: title-slide
vars:
title: "我的演示文稿"
subtitle: "使用外部模板库"
- template: content-slide
vars:
title: "第一章"
content: "这是内容"
```
## 模板库特性
- 单个文件包含多个模板
- 支持模板库元数据description、version、author
- 每个模板可以有独立的 description
- 便于版本控制和分发
- 支持相对路径的图片资源(相对于模板库文件所在目录)
## 模板库文件结构
```yaml
# 顶层元数据(可选)
description: "模板库描述"
version: "版本号"
author: "作者"
# 模板定义(必需)
templates:
模板名称1:
description: "模板描述(可选)"
vars: [...]
elements: [...]
模板名称2:
description: "模板描述(可选)"
vars: [...]
elements: [...]
```
## 模板 description 字段
模板可以包含可选的 `description` 字段,用于描述模板的用途和设计意图:
```yaml
templates:
title-slide:
description: "用于章节标题页的模板,包含主标题和副标题"
vars:
- name: title
required: true
elements:
- type: text
box: [1, 2, 8, 1]
content: "{title}"
font:
size: 44
bold: true
```
## 资源路径解析
模板库中的图片资源使用相对于模板库文件所在目录的路径:
```yaml
# templates.yaml 所在目录:/path/to/templates/
templates:
logo-slide:
elements:
- type: image
src: "images/logo.png" # 相对于 templates.yaml 所在目录
box: [1, 1, 2, 2]
```
## 与内联模板的对比
| 特性 | 内联模板 | 外部模板库 |
|------|---------|-----------|
| 定义位置 | 源文件中 | 独立文件 |
| 适用场景 | 单文档使用 | 跨文档复用 |
| 命令行参数 | 无需 | 需要 `--template` |
| 模板间引用 | 不支持 | 支持 |
| 字体主题 | 使用文档字体 | 可定义独立字体 |
## 相关文档
- [内联模板](inline.md) - 在源文件中定义模板
- [混合模式](mixing-mode.md) - 模板与自定义元素组合
- [字体主题系统](../fonts.md) - 字体配置和跨域引用
[返回文档索引](../README.md)

190
docs/templates/inline.md vendored Normal file
View File

@@ -0,0 +1,190 @@
# 内联模板
内联模板允许你在 YAML 源文件中直接定义模板,无需创建单独的模板文件。
## 定义内联模板
在 YAML 文件顶层添加 `templates` 字段:
```yaml
metadata:
size: "16:9"
templates:
title-slide:
vars:
- name: title
required: true
- name: subtitle
required: false
default: ""
elements:
- type: text
box: [1, 2, 8, 1]
content: "{title}"
font:
size: 44
bold: true
align: center
- type: text
box: [1, 3.5, 8, 0.5]
content: "{subtitle}"
visible: "{subtitle != ''}"
font:
size: 24
align: center
slides:
- template: title-slide
vars:
title: "我的演示文稿"
subtitle: "使用内联模板"
```
## 变量定义
### required 变量
```yaml
vars:
- name: title
required: true
```
### 可选变量与默认值
```yaml
vars:
- name: subtitle
required: false
default: ""
```
## 内联模板特性
- 支持变量替换和条件渲染
- 可以与外部模板混合使用
- 无需指定 `--template` 参数
- 内联模板不能相互引用
- 内联和外部模板同名时会发出警告,优先使用内联模板
## 何时使用内联模板
**适合使用内联模板**
- 模板仅在单个文档中使用
- 快速原型开发
- 简单的模板定义1-3 个元素)
- 文档自包含,无需外部依赖
**适合使用外部模板**
- 需要跨多个文档复用
- 复杂的模板定义(>5 个元素)
- 团队共享的模板库
- 需要版本控制和独立维护
## 最佳实践
### 1. 命名规范
- 内联模板使用描述性名称(如 `title-slide`, `content-slide`
- 避免与外部模板同名,否则会发出警告
- 使用一致的命名风格kebab-case 推荐)
### 2. 模板大小
- 内联模板建议不超过 50 行
- 超过 50 行考虑拆分或使用外部模板
- 保持 YAML 文件可读性
### 3. 混合使用
- 可以在同一文档中混合使用内联和外部模板
- 通用模板使用外部模板(如标题页、结束页)
- 文档特定模板使用内联模板
### 4. 迁移策略
- 原型阶段使用内联模板快速迭代
- 模板稳定后,如需复用则迁移到外部模板
- 使用 `--template` 参数指定外部模板库文件
## 内联模板限制
- 内联模板不能相互引用(会报错)
- 内联和外部模板同名时会发出警告,优先使用内联模板
- 内联模板不支持继承或组合
## 示例
### 简单标题页
```yaml
metadata:
size: "16:9"
templates:
simple-title:
vars:
- name: title
required: true
elements:
- type: text
box: [1, 2.5, 8, 1]
content: "{title}"
font:
size: 44
bold: true
align: center
slides:
- template: simple-title
vars:
title: "欢迎使用 yaml2pptx"
```
### 带条件渲染的模板
```yaml
templates:
optional-subtitle:
vars:
- name: title
required: true
- name: subtitle
required: false
default: ""
elements:
- type: text
box: [1, 2, 8, 1]
content: "{title}"
font:
size: 44
bold: true
align: center
- type: text
box: [1, 3.2, 8, 0.6]
content: "{subtitle}"
visible: "{subtitle != ''}"
font:
size: 24
align: center
slides:
- template: optional-subtitle
vars:
title: "标题"
subtitle: "有副标题"
- template: optional-subtitle
vars:
title: "只有标题"
# subtitle 省略,使用默认值 ""
```
## 相关文档
- [外部模板库](external-library.md) - 独立的模板库文件
- [混合模式](mixing-mode.md) - 模板与自定义元素组合
- [条件渲染](condition-rendering.md) - 元素和页面的条件显示
[返回文档索引](../README.md)

228
docs/templates/mixing-mode.md vendored Normal file
View File

@@ -0,0 +1,228 @@
# 混合模式
混合模式允许你在使用模板的同时添加自定义元素,实现更灵活的布局组合。
## 基本用法
在使用模板的幻灯片中,同时指定 `template``elements` 字段:
```yaml
slides:
# 混合模式:模板 + 自定义元素
- template: standard-header
vars:
title: "混合模式示例"
theme_color: "#3949ab"
elements:
# 自定义内容区域
- type: text
box: [1, 1.5, 8, 1]
content: "这是自定义内容"
font:
size: 24
# 自定义形状
- type: shape
shape: rectangle
box: [1, 3, 8, 2]
fill: "#f5f5f5"
```
## 变量共享
自定义元素可以访问模板中定义的变量:
```yaml
templates:
branded-header:
vars:
- name: title
- name: theme_color
default: "#3949ab"
elements:
- type: shape
box: [0, 0, 10, 0.8]
fill: "{theme_color}"
- type: text
box: [0.5, 0.2, 9, 0.5]
content: "{title}"
slides:
- template: branded-header
vars:
title: "我的页面"
theme_color: "#4caf50"
elements:
# 自定义元素使用模板变量
- type: shape
box: [1, 2, 8, 3]
fill: "{theme_color}" # 使用模板的 theme_color
```
## 元素渲染顺序
混合模式中元素按以下顺序渲染z 轴顺序):
1. **模板元素**(先渲染,在底层)
2. **自定义元素**(后渲染,在上层)
这意味着自定义元素会覆盖在模板元素之上。
```yaml
slides:
- template: background-template # 提供背景和头部
vars:
title: "标题"
elements:
# 这些元素会显示在模板元素之上
- type: shape
box: [2, 2, 6, 3]
fill: "#ffffff" # 白色框会覆盖背景
```
## 使用场景
**适合使用混合模式**
- 复用统一的头部/底部,自定义中间内容
- 使用模板提供的背景和品牌元素,添加页面特定内容
- 需要在标准布局基础上添加特殊元素
## 示例
### 统一头部 + 自定义内容
```yaml
templates:
standard-header:
vars:
- name: title
elements:
# 统一的头部样式
- type: shape
box: [0, 0, 10, 0.8]
fill: "#3949ab"
- type: text
box: [0.5, 0.2, 9, 0.5]
content: "{title}"
font:
color: "#ffffff"
slides:
# 页面 1头部 + 文本内容
- template: standard-header
vars:
title: "文本页面"
elements:
- type: text
box: [1, 1.5, 8, 3]
content: "页面内容..."
# 页面 2头部 + 表格
- template: standard-header
vars:
title: "数据页面"
elements:
- type: table
position: [1, 1.5]
col_widths: [3, 3]
data:
- ["列1", "列2"]
- ["数据1", "数据2"]
# 页面 3头部 + 图片
- template: standard-header
vars:
title: "图片页面"
elements:
- type: image
box: [2, 1.5, 6, 3.5]
src: "chart.png"
```
### 背景模板 + 覆盖层
```yaml
templates:
gradient-bg:
vars:
- name: accent_color
default: "#3498db"
elements:
- type: shape
box: [0, 0, 10, 5.625]
fill: "{accent_color}"
slides:
- template: gradient-bg
vars:
accent_color: "#9b59b6"
elements:
# 白色内容框覆盖在渐变背景上
- type: shape
box: [1, 1, 8, 3.625]
fill: "#ffffff"
- type: text
box: [1.5, 2, 7, 2]
content: "内容区域"
font:
size: 32
```
## 向后兼容性
混合模式完全向后兼容:
- **纯模板模式**:只指定 `template`,行为不变
- **纯自定义模式**:只指定 `elements`,行为不变
- **混合模式**:同时指定 `template``elements`,新功能
```yaml
slides:
# 纯模板模式(原有行为)
- template: title-slide
vars:
title: "标题"
# 纯自定义模式(原有行为)
- elements:
- type: text
content: "自定义内容"
# 混合模式(新功能)
- template: title-slide
vars:
title: "标题"
elements:
- type: text
content: "额外内容"
```
## 幻灯片 description 字段
幻灯片可以包含可选的 `description` 字段,用于描述该幻灯片的作用和内容。**`description` 内容会自动写入 PPT 备注页**,方便在演示时查看演讲说明:
```yaml
slides:
- description: "介绍项目背景和目标"
template: title-slide
vars:
title: "项目背景"
- description: "展示核心功能特性"
elements:
- type: text
content: "功能特性"
```
**注意事项**
- 仅幻灯片级别的 `description` 会写入备注
- 模板的 `description` 不会继承到幻灯片备注
- `metadata.description` 用于描述整个演示文稿,不写入单个幻灯片备注
## 相关文档
- [内联模板](inline.md) - 在源文件中定义模板
- [外部模板库](external-library.md) - 独立的模板库文件
- [条件渲染](condition-rendering.md) - 元素和页面的条件显示
[返回文档索引](../README.md)