Add core Python script (yaml2pptx.py) for converting YAML to PowerPoint: - Element rendering: text, image, shape, table, chart - Template system with placeholders - PPTX generation with python-pptx OpenSpec workflow setup: - 3 archived changes: browser-preview, template-dir-cli, yaml-to-pptx - 7 main specifications covering all core modules - Config and documentation structure 30 files changed, 4984 insertions(+)
251 lines
7.8 KiB
Markdown
251 lines
7.8 KiB
Markdown
## Context
|
||
|
||
当前 `yaml2pptx.py` 是一个命令行工具,接收 YAML 文件作为输入,生成 PPTX 文件作为输出。脚本使用 uv 的 Inline script metadata 管理依赖(`python-pptx`、`pyyaml`),包含完整的 YAML 解析、模板渲染、元素渲染和 PPTX 生成逻辑。
|
||
|
||
现在需要在同一个脚本中添加预览模式,当用户使用 `--preview` 参数时,启动一个轻量级的 Web 服务器,提供浏览器实时预览功能。预览模式需要复用现有的 YAML 解析和模板渲染逻辑,但将输出目标从 PPTX 改为 HTML/CSS。
|
||
|
||
## Goals / Non-Goals
|
||
|
||
**Goals:**
|
||
- 在 `yaml2pptx.py` 中添加预览模式,通过 `--preview` 参数启用
|
||
- 使用轻量级的 Web 框架(Flask)提供 HTTP 服务
|
||
- 使用 Server-Sent Events (SSE) 实现浏览器自动刷新
|
||
- 使用 watchdog 监听 YAML 文件变化
|
||
- 复用现有的 `Presentation` 类和 YAML 解析逻辑
|
||
- 将 YAML 元素(文本、形状、表格、图片)渲染为 HTML/CSS
|
||
- 保持向后兼容,不影响现有的 PPTX 生成功能
|
||
|
||
**Non-Goals:**
|
||
- 不追求 100% 像素级精确的预览效果(HTML/CSS 与 PPTX 渲染存在差异是可接受的)
|
||
- 不支持交互功能(如点击元素查看属性、拖拽调整位置等)
|
||
- 不支持导出为静态 HTML 文件
|
||
- 不支持多用户同时预览
|
||
- 不支持 PPTX 动画和过渡效果的预览
|
||
|
||
## Decisions
|
||
|
||
### 决策 1: 集成到现有脚本 vs 独立脚本
|
||
|
||
**选择**: 集成到 `yaml2pptx.py` 中,通过 `--preview` 参数启用
|
||
|
||
**理由**:
|
||
- 保持项目简洁,只有一个主脚本
|
||
- 用户体验更统一,无需记忆多个命令
|
||
- 更容易复用现有的解析逻辑和类定义
|
||
- 减少代码重复和维护成本
|
||
|
||
**替代方案**: 创建独立的 `preview.py` 脚本
|
||
- 优点:代码分离更清晰
|
||
- 缺点:需要重复导入或复制代码,增加维护成本
|
||
|
||
### 决策 2: Web 框架选择
|
||
|
||
**选择**: Flask
|
||
|
||
**理由**:
|
||
- 轻量级,适合本地开发工具
|
||
- API 简单,易于实现
|
||
- 社区成熟,文档丰富
|
||
- 支持 Server-Sent Events
|
||
|
||
**替代方案**:
|
||
- FastAPI: 更现代,但对于简单的预览服务来说过于复杂
|
||
- Python 内置 http.server: 太简单,不支持动态路由和 SSE
|
||
|
||
### 决策 3: 实时刷新方案
|
||
|
||
**选择**: Server-Sent Events (SSE)
|
||
|
||
**理由**:
|
||
- 单向推送,符合需求(服务器通知浏览器刷新)
|
||
- 浏览器原生支持,无需额外的 JavaScript 库
|
||
- 实现简单,约 10 行代码
|
||
- 无需 WebSocket 的复杂握手和双向通信
|
||
|
||
**替代方案**:
|
||
- WebSocket: 过于复杂,需要 Flask-SocketIO 依赖
|
||
- 轮询: 延迟高,资源浪费
|
||
- Meta refresh: 固定时间刷新,无法响应文件变化
|
||
|
||
### 决策 4: 文件监听方案
|
||
|
||
**选择**: watchdog
|
||
|
||
**理由**:
|
||
- 跨平台,支持 macOS、Linux、Windows
|
||
- 成熟稳定,广泛使用
|
||
- API 简单,易于集成
|
||
- 可以监听整个目录(包括模板文件变化)
|
||
|
||
**替代方案**:
|
||
- 轮询文件修改时间: 延迟高,不够优雅
|
||
- 操作系统原生 API: 不跨平台,实现复杂
|
||
|
||
### 决策 5: 单位转换策略
|
||
|
||
**选择**: 固定 DPI (96 DPI),幻灯片固定尺寸 960x540 像素
|
||
|
||
**理由**:
|
||
- 实现简单,无需复杂的缩放计算
|
||
- 960x540 是合理的预览尺寸(16:9 比例)
|
||
- 用户可以使用浏览器缩放功能(Cmd +/-)调整大小
|
||
- 与 PPTX 的英寸单位转换清晰:1 英寸 = 96 像素
|
||
|
||
**替代方案**:
|
||
- 自适应缩放: 更灵活,但实现复杂,需要处理百分比定位
|
||
- CSS 英寸单位: 不同浏览器可能有差异
|
||
|
||
### 决策 6: HTML 渲染策略
|
||
|
||
**选择**: 使用 `<div>` + 内联样式渲染元素
|
||
|
||
**理由**:
|
||
- 简单直接,易于实现
|
||
- 使用绝对定位模拟 PPTX 的坐标系统
|
||
- 内联样式避免 CSS 类管理的复杂性
|
||
- 文本使用 `pt` 单位保持与 PPTX 一致
|
||
|
||
**元素映射**:
|
||
- 文本元素: `<div>` + `font-size: Xpt`
|
||
- 形状元素: `<div>` + `background-color` + `border-radius`
|
||
- 表格元素: `<table>` 标签
|
||
- 图片元素: `<img>` 标签
|
||
|
||
**替代方案**:
|
||
- SVG 渲染: 更精确,但实现复杂
|
||
- Canvas 渲染: 性能好,但无法选择文本,调试困难
|
||
|
||
### 决策 7: 代码组织
|
||
|
||
**选择**: 在 `yaml2pptx.py` 末尾添加预览相关代码,使用条件判断分离两种模式
|
||
|
||
**结构**:
|
||
```python
|
||
# 现有代码(PPTX 生成)
|
||
class Presentation: ...
|
||
class PptxGenerator: ...
|
||
|
||
# 新增代码(预览模式)
|
||
# HTML 渲染函数
|
||
def render_element_to_html(elem): ...
|
||
|
||
# Flask 应用
|
||
app = Flask(__name__)
|
||
@app.route('/'): ...
|
||
@app.route('/events'): ...
|
||
|
||
# 主函数修改
|
||
def main():
|
||
if args.preview:
|
||
# 启动预览服务器
|
||
start_preview_server()
|
||
else:
|
||
# 生成 PPTX(现有逻辑)
|
||
generate_pptx()
|
||
```
|
||
|
||
**理由**:
|
||
- 清晰分离两种模式的逻辑
|
||
- 不影响现有代码的可读性
|
||
- 易于维护和测试
|
||
|
||
## Risks / Trade-offs
|
||
|
||
### 风险 1: HTML/CSS 渲染与 PPTX 渲染存在差异
|
||
|
||
**风险**: 预览效果与最终 PPTX 可能不完全一致(字体渲染、间距、换行等)
|
||
|
||
**缓解措施**:
|
||
- 在文档中明确说明预览仅供参考
|
||
- 使用相同的单位系统(pt、英寸)尽量保持一致
|
||
- 提供快速生成 PPTX 的方式进行最终确认
|
||
|
||
### 风险 2: 依赖增加
|
||
|
||
**风险**: 新增 `flask` 和 `watchdog` 依赖,增加脚本启动时间和依赖管理复杂度
|
||
|
||
**缓解措施**:
|
||
- 使用 uv 的 Inline script metadata 自动管理依赖
|
||
- 仅在预览模式下导入 Flask 和 watchdog(延迟导入)
|
||
- 依赖都是轻量级库,影响较小
|
||
|
||
### 风险 3: 端口冲突
|
||
|
||
**风险**: 默认端口 5000 可能被其他服务占用
|
||
|
||
**缓解措施**:
|
||
- 提供 `--port` 参数允许用户指定端口
|
||
- 捕获端口占用错误,提示用户更换端口
|
||
|
||
### 风险 4: 文件监听性能
|
||
|
||
**风险**: 监听大型目录可能影响性能
|
||
|
||
**缓解措施**:
|
||
- 仅监听 YAML 文件所在目录,不递归监听
|
||
- 使用 watchdog 的高效事件机制
|
||
- 添加防抖逻辑,避免短时间内多次刷新
|
||
|
||
### 权衡 1: 简单性 vs 功能完整性
|
||
|
||
**权衡**: 选择简单的实现方案,牺牲一些高级功能(如元素交互、网格线、标尺等)
|
||
|
||
**理由**: 预览功能的核心价值是快速反馈,简单实现可以更快交付,后续可根据需求迭代
|
||
|
||
### 权衡 2: 精确性 vs 速度
|
||
|
||
**权衡**: 使用 HTML/CSS 渲染而不是生成 PPTX 再转图片,牺牲精确性换取速度
|
||
|
||
**理由**: 实时预览的核心是速度,用户可以随时生成 PPTX 进行精确确认
|
||
|
||
## Migration Plan
|
||
|
||
### 部署步骤
|
||
|
||
1. 在 `yaml2pptx.py` 的 Inline script metadata 中添加依赖:
|
||
```python
|
||
# dependencies = [
|
||
# "python-pptx",
|
||
# "pyyaml",
|
||
# "flask", # 新增
|
||
# "watchdog", # 新增
|
||
# ]
|
||
```
|
||
|
||
2. 添加预览相关代码(约 200 行)
|
||
|
||
3. 修改 `parse_args()` 函数,添加 `--preview` 和 `--port` 参数
|
||
|
||
4. 修改 `main()` 函数,根据参数选择模式
|
||
|
||
5. 更新 README.md,添加预览功能的使用说明
|
||
|
||
### 回滚策略
|
||
|
||
- 如果预览功能有问题,用户仍可使用原有的 PPTX 生成功能(完全向后兼容)
|
||
- 可以通过 git revert 回滚到之前的版本
|
||
- 预览功能是可选的,不影响核心功能
|
||
|
||
### 测试计划
|
||
|
||
1. 单元测试:测试 HTML 渲染函数的正确性
|
||
2. 集成测试:测试预览服务器的启动和文件监听
|
||
3. 手动测试:在不同浏览器中测试预览效果
|
||
4. 兼容性测试:确保不影响现有的 PPTX 生成功能
|
||
|
||
## Open Questions
|
||
|
||
1. 是否需要支持多幻灯片的缩略图导航?
|
||
- 当前方案:垂直排列显示所有幻灯片
|
||
- 可选方案:添加左侧缩略图导航栏
|
||
|
||
2. 是否需要支持深色模式?
|
||
- 当前方案:仅支持浅色背景
|
||
- 可选方案:添加深色模式切换
|
||
|
||
3. 是否需要显示元素边框和尺寸标注?
|
||
- 当前方案:不显示辅助信息
|
||
- 可选方案:添加调试模式,显示元素边框和尺寸
|
||
|
||
这些问题可以在实现后根据用户反馈决定是否添加。
|