What is the tech stack and implementation of this PPT webapp?

HEAD
914dc22fix UI (#7)
Avatar of WhoKnowInfinity
Infinity
posted

帮我详细分析这个 repo 实现的 ppt webapp 的技术栈组成和实现原理

Avatar of anthropic/claude-haiku-4.5
Claude Haiku 4.5
commented

完美!现在我有了所有需要的信息。让我创建一份详细的分析文档:

##🎯 Presentation App 详细技术栈分析

这是一个 AI驱动的PPT生成Web应用,由Claude AI Agent根据自然语言指令动态生成演示文稿。以下是完整的技术栈和实现原理分析:


##📋 项目整体架构

┌─────────────────────────────────────────────────────────────┐
│                     前端 (Next.js)                          │
│  React 19 + TypeScript + Tailwind CSS                      │
│  ├─ 实时流式聊天界面                                        │
│  ├─ 幻灯片可视化和编辑                                      │
│  ├─ 上下文文件和模板上传                                    │
│  └─ PPTX/PDF导出                                           │
└────────────────┬────────────────────────────────────────────┘
                 │ HTTP/SSE
┌────────────────▼────────────────────────────────────────────┐
│              后端 (FastAPI + Claude Agent)                   │
│  ├─ FastAPI 服务器                                          │
│  ├─ Claude Agent SDK (多轮对话)                             │
│  ├─ LlamaParse (文档解析)                                   │
│  ├─ Session 管理 (SQLite + 文件系统)                        │
│  └─ PPTX/PDF 转换 (Node.js 子进程)                          │
└────────────────┬────────────────────────────────────────────┘
                 │
    ┌────────────┼────────────┐
    │            │            │
    ▼            ▼            ▼
  Claude      SQLite       Node.js
  API         数据库      (pptxgenjs)

##🔧 技术栈详解

###前端技术栈

技术版本用途
Next.js15.5.9全栈框架,服务端渲染
React19.0.0UI 组件库
TypeScript5类型安全
Tailwind CSS4样式框架
PostCSS8CSS处理
ESLint9代码质量

核心依赖只有5个,非常精简!

###后端技术栈

技术版本用途
FastAPI>=0.109.0Web框架
Uvicorn>=0.27.0ASGI服务器
Anthropic SDK>=0.52.0Claude API
Claude Agent SDK>=0.1.0AI Agent框架
Pydantic>=2.0.0数据验证
LlamaParse>=0.6.0文档解析
MCP>=1.0.0Model Context Protocol
httpx>=0.27.0异步HTTP客户端

###数据库和存储

  • SQLite: Session 元数据持久化
  • 文件系统: Presentation JSON 数据存储
  • localStorage: 前端 Session 恢复

###导出工具链

工具版本用途
pptxgenjs3.12.0PPTX 生成
Puppeteer23.0.0PDF 渲染
ChromiumSystem浏览器引擎

##🏗️ 核心实现原理

###1. 前端架构

####页面结构 (web/src/app/page.tsx)

// 核心状态管理
const [userSessionId, setUserSessionId] = useState<string | null>(null);
const [agentSessionId, setAgentSessionId] = useState<string | null>(null);
const [presentation, setPresentation] = useState<Presentation | null>(null);
const [messages, setMessages] = useState<ChatMessage[]>([]);
const [slides, setSlides] = useState<Slide[]>([]);

Session 恢复流程:

  1. 从URL查询参数获取 sessionId
  2. 从 localStorage 恢复聊天历史
  3. 从后端 API 获取当前幻灯片
  4. 生成新的URL以便分享

####组件划分

组件职责
ChatPanel流式聊天交互,显示Agent活动日志
SlideViewer当前幻灯片的查看和编辑
SlideRendererHTML内容渲染,支持双击编辑
SlideGrid幻灯片缩略图列表
ExportMenuPPTX/PDF导出
ContextFilesUpload文件上传和解析
TemplateUpload样式模板上传
ApiKeyGateAPI密钥管理

####流式通信 (lib/api.ts)

export async function* streamAgent(options): AsyncGenerator<StreamEvent> {
  const response = await fetch(`${API_BASE}/agent-stream`, {
    method: "POST",
    body: formData,
  });
  
  const reader = response.body?.getReader();
  // 逐行处理 Server-Sent Events (SSE)
  while (true) {
    const { done, value } = await reader.read();
    // 解析并yield JSON事件
  }
}

流式事件类型:

  • init: Agent初始化
  • status: 处理进度
  • tool_use: 工具调用(幻灯片操作)
  • assistant: Claude的文本响应
  • complete: 完成,返回session_id
  • error: 错误消息

###2. 后端架构

####FastAPI 主服务器 (backend/main.py)

关键端点:

端点方法功能
/healthGET健康检查
/agent-streamPOST核心流式交互
/session/{id}GET获取会话状态
/session/{id}/slidesGET获取所有幻灯片
/session/{id}/slides/{idx}PATCH更新单个幻灯片
/session/{id}/exportGET导出PPTX
/session/{id}/export/pdfGET导出PDF
/parse-filesPOST解析上下文文件
/parse-templatePOST解析样式模板
/validate-api-keyPOST验证LlamaParse密钥
/validate-anthropic-keyPOST验证Anthropic密钥

####CORS 配置

cors_origins_env = os.environ.get("CORS_ORIGINS", "*")
# 支持单一来源或多个逗号分隔的来源
# 生产环境可配置为特定域名

###3. Claude Agent 集成 (backend/agent.py)

####核心工具集

Agent 通过以下工具操纵演示文稿:

@tool("create_presentation", "Create a new presentation", {"title": str})
@tool("add_slide", "Add a new slide", {"html": str, "position": int, "layout": str})
@tool("update_slide", "Update slide HTML", {"slide_index": int, "html": str})
@tool("delete_slide", "Delete a slide", {"slide_index": int})
@tool("reorder_slides", "Reorder slides", {"from_index": int, "to_index": int})
@tool("list_slides", "List all slides", {})
@tool("get_slide", "Get a specific slide", {"slide_index": int})
@tool("set_theme", "Set presentation theme", {"theme": dict})
@tool("get_pending_edits", "Get staged edits", {})
@tool("commit_edits", "Apply all pending edits", {})

####待编辑(Pending Edits)模式

@dataclass
class PendingEdit:
    edit_id: str           # 唯一编辑ID
    slide_index: int       # 目标幻灯片
    operation: str         # ADD, UPDATE, DELETE, REORDER
    params: dict           # 操作参数
    preview: str           # 人类可读描述

优势:

  • 编辑先暂存,后提交(原子性)
  • Agent可以生成多个编辑后一次性应用
  • 支持编辑的撤销和复核

####Agent 选项构建 (_create_agent_options)

options = ClaudeAgentOptions(
    system_prompt="""You are a professional presentation designer...""",
    mcp_servers={"presentation": pres_server},  # MCP服务器
    allowed_tools=[...],  # 白名单工具
    resume=resume_session_id,  # 多轮对话恢复ID
)

####多模态支持

def _build_multimodal_content(session, instructions) -> list[dict]:
    """支持样式模板截图作为视觉上下文"""
    content_blocks = []
    
    # 添加参考截图
    for screenshot in session.style_template["screenshots"]:
        content_blocks.append({
            "type": "image",
            "source": {
                "type": "base64",
                "media_type": "image/jpeg",
                "data": screenshot["data"],
            }
        })
    
    # 添加用户指令
    content_blocks.append({
        "type": "text",
        "text": instructions
    })
    
    return content_blocks

###4. Session 管理 (backend/session.py)

####数据结构

@dataclass
class PresentationSession:
    session_id: str                          # 用户session ID
    presentation: Optional[Presentation]     # 演示文稿对象
    pending_edits: list[PendingEdit]        # 待应用的编辑
    applied_edits: list[dict]               # 已应用编辑历史
    context_files: list[dict]               # 上下文文件
    style_template: Optional[dict]          # 样式参考
    is_continuation: bool                   # 是否延续之前的对话
    claude_session_id: Optional[str]        # Claude Agent session ID
    created_at: datetime
    updated_at: datetime

####线程安全的 SessionManager

class SessionManager:
    def __init__(self):
        self._lock = threading.Lock()
        self._sessions: dict[str, PresentationSession] = {}
        self._init_db()  # SQLite 初始化
    
    def get_or_create_session(self, session_id: Optional[str]):
        with self._lock:
            # 线程安全的session获取/创建
            pass
    
    def save_session(self, session: PresentationSession):
        # 同时保存到SQLite和JSON文件
        with self._lock:
            self._save_to_db(session)
            self._save_to_json(session)
    
    def cleanup_old_sessions(self, cutoff: datetime):
        # 清理24小时前的session
        pass

####自动清理

async def cleanup_old_sessions():
    """后台任务,每小时清理一次"""
    while True:
        cutoff = datetime.now() - timedelta(hours=24)
        cleaned = session_manager.cleanup_old_sessions(cutoff)
        await asyncio.sleep(3600)

###5. 文档解析 (backend/parser.py)

####LlamaParse 集成

async def parse_files_stream(files: list[dict], parse_mode: str):
    """流式处理文件解析,实时返回进度"""
    
    for idx, file_data in enumerate(files):
        yield {"type": "progress", "current": idx + 1}
        
        try:
            if LLAMAPARSE_AVAILABLE and LLAMA_CLOUD_API_KEY:
                # 高质量解析 (使用vision model)
                parsed_text = await parse_with_llama(
                    content, filename, parse_mode
                )
            else:
                # 降级处理 (basic extraction)
                parsed_text = parse_basic(content, filename, content_type)
            
            results.append({
                "filename": filename,
                "text": parsed_text,
                "success": True
            })
        except Exception as e:
            results.append({
                "filename": filename,
                "success": False,
                "error": str(e)
            })

支持的格式: PDF, DOCX, TXT, Markdown, PNG, JPG 等

两种解析模式:

  • cost_effective: 更快更便宜,适合文本提取
  • premium: 更精准,保留格式和结构

###6. PPTX/PDF 转换 (backend/pptx_converter/)

####Node.js 转换流程

// convert.js: HTML → PPTX
import pptxgen from 'pptxgenjs';

const pptx = new pptxgen();
pptx.title = inputData.title;

// 为每个幻灯片处理HTML内容
for (const slide of inputData.slides) {
    const pres_slide = pptx.addSlide();
    
    // 关键: 解析HTML并转换为PPTX对象
    // 处理文本、颜色、字体、图像等
    
    pres_slide.addShape(pptx.ShapeType.rect, {
        x: 0, y: 0, w: "100%", h: "100%",
        fill: { color: backgroundColor }
    });
    
    // 添加文本、表格、图形等
}

pptx.writeFile(outputPath);

####HTML 到 PPTX 的映射

支持的HTML标签:

  • <h1>-<h6>: 标题 → 标题样式文本
  • <p>: 段落 → 文本框
  • <strong>/<b>: 粗体
  • <em>/<i>: 斜体
  • <ul>/<ol>: 列表 → 项目符号
  • <div style="...">: 自定义容器
  • <img>: 图像(base64或URL)
  • <table>: 表格 → PPTX表格

颜色处理:

function parseColor(colorStr) {
    // 支持: #hex, rgb(), rgba(), 命名颜色等
    // 自动转换为PPTX格式 (6位十六进制)
}

function isLightColor(hex) {
    // 智能计算亮度,用于对比度调整
}

####PDF 转换 (convert-pdf.js)

import puppeteer from 'puppeteer';

// 使用Puppeteer渲染HTML到PDF
const browser = await puppeteer.launch({
    executablePath: '/usr/bin/chromium'
});

const page = await browser.newPage();
await page.setContent(html);
await page.pdf({
    path: outputPath,
    format: 'A4',
    width: '960px',
    height: '540px'
});

PDF优势: 像素完美的渲染,保留所有HTML/CSS样式


##🔄 数据流和交互流程

###创建演示文稿的完整流程

用户输入指令
    │
    ▼
[前端] streamAgent() - 发送SSE请求
    │
    ▼
[后端] /agent-stream 端点
    │
    ├─> 创建或恢复 Session
    │
    ├─> 初始化 ClaudeSDKClient
    │
    └─> 发送给 Claude Agent
         │
         ├─► Agent 理解指令
         │
         ├─► Agent 调用工具 (add_slide, update_slide等)
         │   │
         │   └─► PendingEdit 被添加到 session.pending_edits
         │
         ├─► Agent 调用 commit_edits()
         │   │
         │   └─► PendingEdit 应用到 session.presentation
         │
         └─► 返回完成信号 + session_id
    │
    ▼
[前端] 接收流式事件
    │
    ├─► init: 显示 "Agent 初始化中..."
    ├─► status: 更新进度
    ├─► tool_use: 显示具体操作 (如"添加第2张幻灯片")
    ├─► assistant: 显示 Claude 的文本回复
    └─► complete: 刷新幻灯片列表,保存session
    │
    ▼
用户看到新幻灯片被添加到编辑器

###导出流程

用户点击 "导出PPTX"
    │
    ▼
[前端] exportPptx(sessionId)
    │
    ▼
[后端] /session/{id}/export
    │
    ├─► session_manager.load_session(sessionId)
    │
    ├─► 构建 input_data = {title, slides: [{html, width: 960, height: 540}], theme}
    │
    ├─► 写入临时JSON文件
    │
    ├─► 子进程运行: node pptx_converter/convert.js input.json output.pptx
    │
    ├─► convert.js 使用 pptxgenjs 生成PPTX
    │   │
    │   └─► 解析每个slide的HTML,转换为PPTX对象
    │
    ├─► 读取输出的PPTX字节
    │
    └─► 返回给前端 (Content-Type: application/...presentationml.presentation)
    │
    ▼
[前端] 浏览器下载文件

##📊 模型和数据结构

###Slide 模型 (backend/models.py)

@dataclass
class Slide:
    index: int                      # 幻灯片位置
    html: str                       # 完整的HTML内容
    layout: SlideLayout            # 预设布局 (TITLE, TITLE_CONTENT等)
    notes: str = ""                # 演讲者备注

布局预设:

class SlideLayout(Enum):
    TITLE = "title"                        # 标题幻灯片
    TITLE_CONTENT = "title_content"        # 标题+内容
    TWO_COLUMN = "two_column"              # 两栏布局
    BLANK = "blank"                        # 空白 (自由设计)

###Presentation 模型

@dataclass
class Presentation:
    title: str
    slides: list[Slide]
    theme: dict                    # {primaryColor, fontFamily, ...}

##🛡️ 安全特性

###API 密钥验证

@app.post("/validate-api-key")
async def validate_api_key(api_key: str):
    """验证 LlamaParse API 密钥"""
    if not api_key.startswith("llx-"):
        raise HTTPException(400, "Invalid format")
    
    # 实际请求 LlamaParse API 验证
    response = await client.get(
        "https://api.cloud.llamaindex.ai/api/v1/projects",
        headers={"Authorization": f"Bearer {api_key}"}
    )

@app.post("/validate-anthropic-key")
async def validate_anthropic_key(request: dict):
    """验证 Anthropic API 密钥"""
    client = anthropic.Anthropic(api_key=api_key)
    client.messages.create(...)  # 发送最小测试请求

###CORS 灵活配置

# 开发环境: "*"
# 生产环境: 环境变量 CORS_ORIGINS
# 支持多个来源: "https://app.vercel.app,http://localhost:3000"

##🐳 Docker 部署

###Dockerfile 特点

# 多运行时镜像: Python 3.11 + Node.js 20
FROM python:3.11-slim

# 安装 Node.js 20
RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash -

# 为 Puppeteer (PDF导出) 安装 Chromium
RUN apt-get install -y chromium

# Puppeteer 使用系统 Chromium
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium

# 安装 Python 和 Node.js 依赖
# 复制 PPTX 转换器
# 暴露端口 8000
EXPOSE 8000

# 健康检查
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8000/health

# 启动 Uvicorn
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

###Render.yaml 部署配置

services:
  - type: web
    name: presentation-api
    runtime: docker
    healthCheckPath: /health
    envVars:
      - ANTHROPIC_API_KEY (同步=false)
      - LLAMA_CLOUD_API_KEY (同步=false)
      - CORS_ORIGINS
      - DATA_DIR: /app/data
    disk:
      mountPath: /app/data
      sizeGB: 1                # 持久化存储用于sessions

##🎨 UI/UX 特性

###实时反馈系统

  1. Chat Panel:

    • 显示用户消息和Agent回复
    • Agent活动日志(工具调用、状态更新)
    • 流式显示(逐步显示)
  2. Slide Renderer:

    • 双击编辑幻灯片内容
    • 即时预览
    • 保存/取消按钮
  3. Slide Grid:

    • 所有幻灯片缩略图
    • 快速导航
    • 拖拽重新排序(潜在功能)
  4. Export Menu:

    • PPTX 和 PDF 两种格式
    • 一键下载

##🔑 关键设计模式

###1. 流式处理 (Streaming)

所有长时间运行的操作都使用 SSE 流:

  • Agent 交互
  • 文件解析
  • 导出

优势: 用户能看到实时进度,不会卡顿

###2. Session 驱动

每个用户的工作状态完全由 Session 管理:

  • 可恢复 (通过 URL session 参数)
  • 可分享 (共享 session ID)
  • 持久化 (SQLite + JSON)

###3. 待编辑模式 (Pending Edits)

  • Agent 生成的编辑先暂存
  • 用户可复核后再提交
  • 支持事务性应用

###4. 多模态 AI

  • 文本指令
  • 样式模板截图 (作为视觉上下文)
  • 上下文文件 (PDF, DOCX等)

##📈 扩展性

###可扩展的工具系统

通过 MCP (Model Context Protocol),可以轻松添加新工具:

@tool("new_tool", "description", {"param": type})
async def tool_new_tool(args: dict) -> dict:
    # 新功能实现
    return {"result": ...}

###模式系统

两种解析模式支持成本和质量的权衡

###API 密钥管理

支持用户自带 API 密钥,无需服务器存储


##🎯 总结

方面特点
架构前后端分离,异步流式通信
AI 集成Claude Agent SDK + MCP 工具框架
数据流Session 驱动,待编辑模式,事务性应用
存储SQLite元数据 + JSON演示文稿 + localStorage会话
导出HTML → Node.js (pptxgenjs) → PPTX/PDF
部署Docker 多运时,Render.yaml 配置
实时性SSE 流式推送,前端实时显示进度
可扩展MCP 工具框架,易于添加新功能

这个项目是一个精心设计的现代 Web 应用,充分利用了 Claude AI 的能力,同时保持了代码的简洁性和可维护性!


END OF POST

What is the tech stack and implementation of this PPT webapp? — jerryjliu/presentation_app