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(+)
7.8 KiB
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 末尾添加预览相关代码,使用条件判断分离两种模式
结构:
# 现有代码(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
部署步骤
-
在
yaml2pptx.py的 Inline script metadata 中添加依赖:# dependencies = [ # "python-pptx", # "pyyaml", # "flask", # 新增 # "watchdog", # 新增 # ] -
添加预览相关代码(约 200 行)
-
修改
parse_args()函数,添加--preview和--port参数 -
修改
main()函数,根据参数选择模式 -
更新 README.md,添加预览功能的使用说明
回滚策略
- 如果预览功能有问题,用户仍可使用原有的 PPTX 生成功能(完全向后兼容)
- 可以通过 git revert 回滚到之前的版本
- 预览功能是可选的,不影响核心功能
测试计划
- 单元测试:测试 HTML 渲染函数的正确性
- 集成测试:测试预览服务器的启动和文件监听
- 手动测试:在不同浏览器中测试预览效果
- 兼容性测试:确保不影响现有的 PPTX 生成功能
Open Questions
-
是否需要支持多幻灯片的缩略图导航?
- 当前方案:垂直排列显示所有幻灯片
- 可选方案:添加左侧缩略图导航栏
-
是否需要支持深色模式?
- 当前方案:仅支持浅色背景
- 可选方案:添加深色模式切换
-
是否需要显示元素边框和尺寸标注?
- 当前方案:不显示辅助信息
- 可选方案:添加调试模式,显示元素边框和尺寸
这些问题可以在实现后根据用户反馈决定是否添加。