1
0
Files
PPTX/openspec/changes/archive/2026-03-02-add-browser-preview/design.md
lanyuanxiaoyao cd7988cbd5 feat: initial implementation of html2pptx with OpenSpec documentation
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(+)
2026-03-02 14:28:25 +08:00

7.8 KiB
Raw Blame History

Context

当前 yaml2pptx.py 是一个命令行工具,接收 YAML 文件作为输入,生成 PPTX 文件作为输出。脚本使用 uv 的 Inline script metadata 管理依赖(python-pptxpyyaml),包含完整的 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: 依赖增加

风险: 新增 flaskwatchdog 依赖,增加脚本启动时间和依赖管理复杂度

缓解措施:

  • 使用 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 中添加依赖:

    # 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. 是否需要显示元素边框和尺寸标注?

    • 当前方案:不显示辅助信息
    • 可选方案:添加调试模式,显示元素边框和尺寸

这些问题可以在实现后根据用户反馈决定是否添加。