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

View File

@@ -0,0 +1,18 @@
# 模块详解
本目录包含各代码模块的详细说明。
## 核心模块
- [Elements](elements.md) - 元素抽象层
- [Template](template.md) - 模板系统
- [Validators](validators.md) - 验证器
- [Renderers](renderers.md) - 渲染器
- [Loaders](loaders.md) - 加载器
## 相关文档
- [架构设计](../architecture.md) - 整体代码结构
- [开发规范](../development-guide.md) - 编码规范
[返回开发文档索引](../README.md)

View File

@@ -0,0 +1,137 @@
# Elements 模块
`core/elements.py` 定义了所有元素类型的数据类和工厂函数。
## 职责
- 定义元素数据类(使用 `@dataclass`
- 在创建时进行元素级验证
- 提供元素工厂函数
## 包含的内容
### FontConfig 类
字体配置数据类,支持所有字体属性:
```python
@dataclass
class FontConfig:
parent: Optional[str] = None
family: Optional[str] = None
size: Optional[int] = None
bold: Optional[bool] = None
italic: Optional[bool] = None
underline: Optional[bool] = None
strikethrough: Optional[bool] = None
color: Optional[str] = None
align: Optional[str] = None
line_spacing: Optional[float] = None
space_before: Optional[float] = None
space_after: Optional[float] = None
baseline: Optional[str] = None
caps: Optional[str] = None
```
`__post_init__` 中验证 baseline 和 caps 枚举值。
### 元素类
#### TextElement
```python
@dataclass
class TextElement:
type: str = 'text'
content: str = ''
box: list = field(default_factory=lambda: [1, 1, 8, 1])
font: FontConfig | str | dict = field(default_factory=dict)
```
#### ImageElement
```python
@dataclass
class ImageElement:
type: str = 'image'
src: str = ''
box: list = field(default_factory=lambda: [1, 1, 4, 3])
```
#### ShapeElement
```python
@dataclass
class ShapeElement:
type: str = 'shape'
box: list = field(default_factory=lambda: [1, 1, 4, 2])
shape: str = 'rectangle'
fill: str = '#000000'
line: dict = field(default_factory=dict)
```
#### TableElement
```python
@dataclass
class TableElement:
type: str = 'table'
position: list = field(default_factory=lambda: [0, 0])
col_widths: list = field(default_factory=list)
data: list = field(default_factory=list)
font: FontConfig | str | dict = field(default_factory=dict)
header_font: FontConfig | str | dict = field(default_factory=dict)
style: dict = field(default_factory=dict)
```
### create_element() 工厂函数
```python
def create_element(elem_dict: dict):
elem_type = elem_dict.get('type')
if elem_type == 'text':
return TextElement(**elem_dict)
elif elem_type == 'image':
return ImageElement(**elem_dict)
elif elem_type == 'shape':
return ShapeElement(**elem_dict)
elif elem_type == 'table':
return TableElement(**elem_dict)
else:
raise ValueError(f"Unknown element type: {elem_type}")
```
自动将字典形式的 font 转换为 FontConfig 对象。
## 创建时验证
每个元素类在 `__post_init__` 中进行验证:
```python
def __post_init__(self):
# 检查必需字段
if len(self.box) != 4:
raise ValueError("box 必须包含 4 个数字")
# 验证颜色格式
if self.fill and not _is_valid_color(self.fill):
raise ValueError(f"无效的颜色格式: {self.fill}")
```
## 元素级验证职责
- 必需字段检查
- 数据类型检查
- 值的有效性检查:
- 颜色格式验证
- 字体大小合理性
- 枚举值检查(如形状类型)
- 表格数据一致性
## 相关文档
- [Template 模块](template.md) - 模板系统
- [Validators 模块](validators.md) - 验证器详解
[返回开发文档索引](../README.md)

View File

@@ -0,0 +1,135 @@
# Loaders 模块
`loaders/yaml_loader.py` 负责 YAML 文件加载和验证。
## 职责
- YAML 文件加载
- 演示文稿结构验证
- 内联模板验证
- 外部模板验证
## 包含的内容
### YAMLError
自定义异常类:
```python
class YAMLError(Exception):
"""YAML 相关错误"""
pass
```
### load_yaml_file()
加载 YAML 文件:
```python
def load_yaml_file(file_path):
"""加载 YAML 文件并返回解析后的数据"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
data = yaml.safe_load(f)
return data
except yaml.YAMLError as e:
raise YAMLError(f"YAML 语法错误: {e}")
except FileNotFoundError:
raise YAMLError(f"文件不存在: {file_path}")
```
### validate_presentation_yaml()
验证演示文稿结构:
```python
def validate_presentation_yaml(data, file_path):
"""验证演示文稿 YAML 结构"""
# 检查必需字段
if 'metadata' not in data:
raise YAMLError(f"缺少 metadata 字段: {file_path}")
if 'slides' not in data:
raise YAMLError(f"缺少 slides 字段: {file_path}")
# 验证 metadata
metadata = data['metadata']
if 'size' not in metadata:
raise YAMLError(f"metadata 缺少 size 字段: {file_path}")
if metadata['size'] not in ['16:9', '4:3']:
raise YAMLError(f"无效的 size 值: {metadata['size']}")
# 验证内联模板(如果有)
if 'templates' in data:
validate_templates_yaml(data['templates'])
# 验证幻灯片
for slide in data['slides']:
# 验证 enabled 字段
if 'enabled' in slide:
if not isinstance(slide['enabled'], bool):
raise YAMLError(f"enabled 必须是布尔值: {file_path}")
```
### validate_template_yaml()
验证外部模板结构:
```python
def validate_template_yaml(template_data, template_name):
"""验证外部模板结构"""
if 'elements' not in template_data:
raise YAMLError(f"模板缺少 elements 字段: {template_name}")
if 'vars' in template_data:
for var in template_data['vars']:
if 'name' not in var:
raise YAMLError(f"变量缺少 name 字段: {template_name}")
```
### validate_templates_yaml()
验证内联模板结构:
```python
def validate_templates_yaml(templates):
"""验证内联模板结构"""
if not isinstance(templates, dict):
raise YAMLError("templates 必须是字典类型")
for name, template_data in templates.items():
validate_template_yaml(template_data, f"templates.{name}")
# 检测默认值中引用不存在的变量
vars_def = {v['name']: v for v in template_data.get('vars', [])}
for var in template_data.get('vars', []):
if 'default' in var:
# 检查默认值是否引用了不存在的变量
# 实现省略...
```
## 内联模板验证
内联模板验证包括:
- 结构验证(是否为字典)
- 元素验证(是否有 elements 字段)
- 变量定义验证(是否有 name 字段)
- 默认值验证(检测循环引用)
## enabled 字段验证
验证 `enabled` 字段必须是布尔值:
```python
if 'enabled' in slide:
if not isinstance(slide['enabled'], bool):
raise YAMLError(f"enabled 必须是布尔值,不能是字符串或条件表达式")
```
## 相关文档
- [架构设计](../architecture.md) - 代码结构
- [验证器模块](validators.md) - 验证详解
[返回开发文档索引](../README.md)

View File

@@ -0,0 +1,153 @@
# Renderers 模块
`renderers/` 目录包含 PPTX 和 HTML 渲染器。
## 模块组成
### pptx_renderer.py
PPTX 文件生成器:
```python
class PptxGenerator:
def __init__(self, size="16:9"):
self.presentation = Presentation()
self.slide_width = 10 # 英寸
self.slide_height = 5.625 if size == "16:9" else 7.5
def add_slide(self):
"""添加新幻灯片"""
slide = self.presentation.slides.add_slide(self.blank_layout)
return slide
def _render_element(self, slide, elem, base_path):
"""分发元素到对应的渲染方法"""
if isinstance(elem, TextElement):
self._render_text(slide, elem)
elif isinstance(elem, ImageElement):
self._render_image(slide, elem, base_path)
# ...
def _render_text(self, slide, elem):
"""渲染文本元素"""
# 实现...
def _render_image(self, slide, elem, base_path):
"""渲染图片元素"""
# 实现...
def _render_shape(self, slide, elem):
"""渲染形状元素"""
# 实现...
def _render_table(self, slide, elem):
"""渲染表格元素"""
# 实现...
def save(self, output_path):
"""保存 PPTX 文件"""
self.presentation.save(output_path)
```
### html_renderer.py
HTML 预览渲染器:
```python
class HtmlRenderer:
def __init__(self, size="16:9"):
self.slide_width = 960 # 96 DPI * 10"
self.slide_height = 540 if size == "16:9" else 720
def render_slide(self, slide_data, index, base_path=None):
"""渲染幻灯片为 HTML"""
elements_html = ""
for elem in slide_data:
if isinstance(elem, TextElement):
elements_html += self.render_text(elem)
elif isinstance(elem, ImageElement):
elements_html += self.render_image(elem, base_path)
# ...
return self.SLIDE_TEMPLATE.format(
width=self.slide_width,
height=self.slide_height,
content=elements_html
)
def render_text(self, elem):
"""渲染文本为 HTML"""
# 实现...
def render_image(self, elem, base_path):
"""渲染图片为 HTML"""
# 实现...
def render_shape(self, elem):
"""渲染形状为 HTML"""
# 实现...
def render_table(self, elem):
"""渲染表格为 HTML"""
# 实现...
```
## 设计特点
### 渲染器内置在生成器中
- **封装性**:渲染逻辑与生成器紧密相关
- **简单性**:不需要额外的渲染器接口
- **性能**:避免额外的方法调用开销
### 使用 isinstance() 检查类型
```python
if isinstance(elem, TextElement):
self._render_text(slide, elem)
```
### 通过元素对象属性访问数据
```python
def _render_text(self, slide, elem):
text_box = elem.box
content = elem.content
font_config = elem.font
```
## 共享元素抽象层
两个渲染器共享相同的元素数据类:
- `TextElement`
- `ImageElement`
- `ShapeElement`
- `TableElement`
## 单位转换
### HTML 渲染器
使用固定 DPI (96) 进行单位转换:
```python
DPI = 96
pixels = inches * DPI
```
### PPTX 渲染器
python-pptx 使用 Inches 单位:
```python
from pptx.util import Inches
left = Inches(box[0])
```
## 相关文档
- [Elements 模块](elements.md) - 元素数据类
- [预览功能](../../user-guide.md) - 用户指南
[返回开发文档索引](../README.md)

View File

@@ -0,0 +1,148 @@
# Template 模块
`core/template.py` 实现了模板系统,支持变量解析、条件渲染和模板渲染。
## 职责
- 模板加载和变量解析
- 条件表达式评估
- 模板渲染
## 包含的内容
### Template 类
#### from_data() 类方法
从字典创建模板实例(用于内联模板):
```python
@classmethod
def from_data(cls, template_data, template_name, base_dir=None):
"""从字典创建模板(内联模板或外部模板)"""
obj = cls.__new__(cls)
obj.data = template_data
obj.base_dir = base_dir
obj.vars_def = {var['name']: var for var in template_data.get('vars', [])}
obj.elements = template_data.get('elements', [])
return obj
```
#### 变量解析
**resolve_value()** - 解析变量值:
```python
def resolve_value(self, value, vars_values):
"""解析变量值,支持 {varname} 语法"""
if isinstance(value, str) and '{' in value:
# 替换变量
result = value
for var_name, var_value in vars_values.items():
result = result.replace(f'{{{var_name}}}', str(var_value))
return result
return value
```
**resolve_element()** - 解析元素中的变量:
```python
def resolve_element(self, element, vars_values):
"""递归解析元素中的所有变量"""
resolved = {}
for key, value in element.items():
if isinstance(value, dict):
resolved[key] = self.resolve_element(value, vars_values)
elif isinstance(value, str):
resolved[key] = self.resolve_value(value, vars_values)
else:
resolved[key] = value
return resolved
```
#### 条件渲染
**evaluate_condition()** - 委托给 ConditionEvaluator
```python
def evaluate_condition(self, condition, vars_values):
"""评估条件表达式"""
from core.condition_evaluator import ConditionEvaluator
evaluator = ConditionEvaluator()
return evaluator.evaluate(condition, vars_values)
```
#### 模板渲染
**render()** - 渲染模板:
```python
def render(self, vars_values):
"""渲染模板,返回解析后的元素列表"""
elements = []
for elem in self.elements:
# 检查条件渲染
if 'visible' in elem:
if not self.evaluate_condition(elem['visible'], vars_values):
continue
# 解析变量
resolved_elem = self.resolve_element(elem, vars_values)
elements.append(resolved_elem)
return elements
```
## 特点
### 支持两种模板方式
- **外部模板**:从文件加载
- **内联模板**:从字典创建(通过 `from_data()`
### 变量替换
支持 `{varname}` 语法的变量替换:
```yaml
templates:
title-slide:
vars:
- name: title
elements:
- type: text
content: "{title}" # 变量替换
```
### 条件渲染
支持 `visible` 属性的条件表达式:
```yaml
elements:
- type: text
content: "有数据"
visible: "{count > 0}"
```
## 内联模板支持
`from_data()` 类方法使模板可以从字典创建:
```python
# 从内联模板定义创建
template = Template.from_data(
template_data=inline_template_dict,
template_name="title-slide",
base_dir=document_base_dir
)
```
## 相关文档
- [条件评估器](../condition-rendering.md) - 条件表达式语法
- [Elements 模块](elements.md) - 元素数据类
- [内联模板](../../templates/inline.md) - 用户指南
[返回开发文档索引](../README.md)

View File

@@ -0,0 +1,112 @@
# Validators 模块
`validators/` 目录包含所有验证相关的代码。
## 模块组成
### validators/result.py
验证结果数据结构:
```python
@dataclass
class ValidationIssue:
level: str # ERROR/WARNING/INFO
message: str
location: str = ""
code: str = ""
@dataclass
class ValidationResult:
errors: List[ValidationIssue]
warnings: List[ValidationIssue]
infos: List[ValidationIssue]
```
### validators/geometry.py
几何验证器,检查元素边界:
```python
class GeometryValidator:
def __init__(self, slide_width, slide_height):
self.slide_width = slide_width
self.slide_height = slide_height
self.tolerance = 0.1 # 英寸
def validate_element(self, element, slide_index, elem_index):
"""检查元素是否在页面范围内"""
# 检查边界
# 支持容忍度
```
### validators/resource.py
资源验证器,检查文件存在性:
```python
class ResourceValidator:
def __init__(self, base_dir):
self.base_dir = Path(base_dir)
def validate_image(self, src, slide_index, elem_index):
"""检查图片文件是否存在"""
# 检查文件路径
# 验证文件存在
```
### validators/validator.py
主验证器,协调所有子验证器:
```python
class Validator:
def __init__(self, slide_width, slide_height, base_dir):
self.geometry_validator = GeometryValidator(slide_width, slide_height)
self.resource_validator = ResourceValidator(base_dir)
def validate_presentation(self, presentation):
"""验证整个演示文稿"""
# 遍历所有幻灯片和元素
# 调用子验证器
```
## 验证职责分层
### 元素级验证
在元素类中完成:
- 必需字段检查
- 数据类型检查
- 值的有效性检查
### 系统级验证
在验证器中完成:
- 几何验证(需要页面尺寸)
- 资源验证(需要文件路径)
- 跨元素验证
## 验证容忍度
几何验证时,允许 0.1 英寸的容忍度:
```python
TOLERANCE = 0.1 # 英寸
if right > slide_width + TOLERANCE:
# 报告 WARNING
```
## 分级错误报告
- **ERROR**:阻止转换的严重问题
- **WARNING**:影响视觉效果的问题
- **INFO**:优化建议
## 相关文档
- [Elements 模块](elements.md) - 元素级验证
- [开发规范](../development-guide.md) - 验证职责
[返回开发文档索引](../README.md)