ai-vibe-slides

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

AI Vibe Slides — Beautiful HTML Slide Decks, Ready to Present

AI Vibe Slides — 美观的HTML幻灯片,随时可演示

Goal

目标

Produce a single HTML or React artifact file containing a complete slide deck that can:
  • Present fullscreen directly in the browser
  • Navigate via arrow keys, spacebar, or click
  • Look professional, polished, and stylistically consistent
  • Print or export to PDF when needed
Inspired by banana-slides: a 3-step pipeline of Idea → Outline → Finished Slides, but the output is a self-contained HTML file instead of a fullstack application.

生成单个HTML或React文件,包含完整的幻灯片演示文稿,具备以下功能:
  • 可直接在浏览器中全屏展示
  • 通过箭头键、空格键或点击进行导航
  • 外观专业、精致,风格统一
  • 需要时可打印或导出为PDF
灵感来源于banana-slides:采用想法 → 大纲 → 成品幻灯片的三步流程,但输出为独立HTML文件而非全栈应用。

Slide Creation Pipeline

幻灯片创建流程

Step 1: Understand the Request → Build an Outline

步骤1:理解需求 → 构建大纲

When the user provides a request, first build an outline mentally (no need to display it unless the user asks):
  1. Identify the main topic and target audience (students, business, tech talk...)
  2. Break it into logical sections:
    • Slide 1: Cover (title + subtitle + author)
    • Slides 2-3: Introduction / context
    • Middle slides: Core content (one key idea per slide)
    • Final slide: Conclusion / Call to action / Thank you
  3. Each slide should have: a title, 2-5 bullet points or visual content, and a layout type
If the user only gives a short sentence (e.g., "create slides about AI in healthcare"), automatically expand it into 8-12 slides with a logical structure.
当用户提出需求时,首先在脑海中构建大纲(除非用户要求,否则无需展示):
  1. 确定主题目标受众(学生、商务人士、技术演讲听众等)
  2. 将内容拆分为逻辑章节
    • 第1页:封面(标题+副标题+作者)
    • 第2-3页:介绍/背景
    • 中间页面:核心内容(每页一个关键观点)
    • 最后一页:结论/行动号召/致谢
  3. 每页幻灯片应包含:标题、2-5个要点或视觉内容,以及布局类型
如果用户仅提供短句(例如“创建关于医疗AI的幻灯片”),自动将其扩展为8-12页结构合理的幻灯片。

Step 2: Choose a Design Direction

步骤2:选择设计方向

Based on context, commit to one clear design direction:
ContextSuggested Style
Startup pitch deckBold, dark theme, gradient accents, strong sans-serif
Academic / educationClean, light, diagram-heavy, readable fonts
Tech talk / conferenceModern dark, code-style typography, neon accents
Corporate / reportMinimal, professional, navy/white, serif headings
Creative / marketingColorful, asymmetric layout, bold typography
Kids / early educationPastel, rounded corners, playful icons, large text
General rules:
  • Pick 2-3 primary colors and use them consistently across the entire deck
  • 1 heading font + 1 body font (use Google Fonts)
  • Minimal text, generous whitespace — max 5-6 lines per slide
  • Clear visual hierarchy: large title → medium content → small notes
根据场景确定清晰的设计风格
场景推荐风格
初创企业推介演示文稿大胆深色主题、渐变点缀、醒目无衬线字体
学术/教育场景简洁浅色、重图表、易读字体
技术演讲/会议现代深色、代码风格排版、霓虹色点缀
企业/报告极简专业、蓝白配色、衬线标题
创意/营销色彩丰富、不对称布局、醒目排版
儿童/早教马卡龙色系、圆角、趣味图标、大字体
通用规则:
  • 选择2-3种主色调,并在整个演示文稿中保持一致
  • 1种标题字体 + 1种正文字体(使用Google Fonts)
  • 文字精简、留白充足——每页最多5-6行文字
  • 清晰的视觉层级:大标题 → 中等内容 → 小注释

Step 3: Generate the HTML/React Slide Deck

步骤3:生成HTML/React幻灯片演示文稿

Create a single file (
.html
or
.jsx
) containing everything:

创建单个文件
.html
.jsx
),包含所有内容:

HTML Slide Deck Structure

HTML幻灯片演示文稿结构

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{Presentation Title}</title>
    <link
      href="https://fonts.googleapis.com/css2?family={Font1}&family={Font2}&display=swap"
      rel="stylesheet"
    />
    <style>
      /* === RESET + BASE === */
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      /* === SLIDE CONTAINER === */
      .slide-deck {
        width: 100vw;
        height: 100vh;
        overflow: hidden;
        position: relative;
      }
      .slide {
        width: 100%;
        height: 100%;
        display: none;
        flex-direction: column;
        justify-content: center;
        padding: 60px 80px;
        position: absolute;
        top: 0;
        left: 0;
      }
      .slide.active {
        display: flex;
      }

      /* === TYPOGRAPHY === */
      h1 {
        font-family: "{Font1}", sans-serif;
        font-size: 3.2rem;
        margin-bottom: 1rem;
      }
      h2 {
        font-family: "{Font1}", sans-serif;
        font-size: 2.4rem;
        margin-bottom: 1.5rem;
      }
      p,
      li {
        font-family: "{Font2}", sans-serif;
        font-size: 1.4rem;
        line-height: 1.8;
      }

      /* === THEME COLORS === */
      :root {
        --bg-primary: #0f172a;
        --bg-slide: #1e293b;
        --text-primary: #f8fafc;
        --text-secondary: #94a3b8;
        --accent: #3b82f6;
        --accent-2: #8b5cf6;
      }

      /* === NAVIGATION UI === */
      .nav-hint {
        position: fixed;
        bottom: 20px;
        right: 30px;
        font-size: 0.8rem;
        color: var(--text-secondary);
        opacity: 0.5;
      }
      .slide-counter {
        position: fixed;
        bottom: 20px;
        left: 30px;
        font-size: 0.8rem;
        color: var(--text-secondary);
      }

      /* === TRANSITIONS === */
      .slide {
        animation: fadeIn 0.4s ease;
      }
      @keyframes fadeIn {
        from {
          opacity: 0;
          transform: translateY(10px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      /* === LAYOUT VARIANTS === */
      .slide.cover {
        justify-content: center;
        align-items: center;
        text-align: center;
      }
      .slide.two-column {
        flex-direction: row;
        gap: 60px;
        align-items: center;
      }
      .slide.two-column .col {
        flex: 1;
      }
      .slide.centered {
        align-items: center;
        text-align: center;
      }

      /* === VISUAL ELEMENTS === */
      .card {
        background: rgba(255, 255, 255, 0.05);
        border-radius: 12px;
        padding: 24px;
        margin: 8px 0;
      }
      .badge {
        display: inline-block;
        background: var(--accent);
        color: white;
        padding: 4px 14px;
        border-radius: 20px;
        font-size: 0.85rem;
      }
      .divider {
        width: 60px;
        height: 4px;
        background: var(--accent);
        border-radius: 2px;
        margin: 16px 0;
      }
      .icon-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 20px;
      }

      /* === PRINT / PDF EXPORT === */
      @media print {
        .slide {
          page-break-after: always;
          display: flex !important;
          position: relative;
        }
        .nav-hint,
        .slide-counter {
          display: none;
        }
      }
    </style>
  </head>
  <body>
    <div class="slide-deck" id="deck">
      <!-- SLIDE 1: COVER -->
      <div
        class="slide cover active"
        style="background: linear-gradient(135deg, var(--bg-primary), #1a1a2e);"
      >
        <div class="badge">Topic</div>
        <h1 style="font-size: 3.8rem; margin-top: 20px;">Main Title</h1>
        <p
          style="color: var(--text-secondary); font-size: 1.3rem; margin-top: 12px;"
        >
          Short subtitle description
        </p>
        <div class="divider" style="margin: 20px auto;"></div>
        <p style="color: var(--text-secondary); font-size: 1rem;">
          Author — Date
        </p>
      </div>

      <!-- SLIDE 2+: CONTENT -->
      <div class="slide">
        <h2>Slide Title</h2>
        <div class="divider"></div>
        <ul>
          <li>Key point 1</li>
          <li>Key point 2</li>
          <li>Key point 3</li>
        </ul>
      </div>

      <!-- ... more slides ... -->
    </div>

    <div class="slide-counter">
      <span id="current">1</span> / <span id="total"></span>
    </div>
    <div class="nav-hint">← → or click to navigate</div>

    <script>
      const slides = document.querySelectorAll(".slide");
      let currentSlide = 0;
      document.getElementById("total").textContent = slides.length;

      function showSlide(n) {
        slides[currentSlide].classList.remove("active");
        currentSlide = (n + slides.length) % slides.length;
        slides[currentSlide].classList.add("active");
        document.getElementById("current").textContent = currentSlide + 1;
      }

      document.addEventListener("keydown", (e) => {
        if (e.key === "ArrowRight" || e.key === " ")
          showSlide(currentSlide + 1);
        if (e.key === "ArrowLeft") showSlide(currentSlide - 1);
        if (e.key === "f") document.documentElement.requestFullscreen?.();
        if (e.key === "Escape") document.exitFullscreen?.();
      });

      document.querySelector(".slide-deck").addEventListener("click", (e) => {
        const x = e.clientX / window.innerWidth;
        x > 0.5 ? showSlide(currentSlide + 1) : showSlide(currentSlide - 1);
      });
    </script>
  </body>
</html>

html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{Presentation Title}</title>
    <link
      href="https://fonts.googleapis.com/css2?family={Font1}&family={Font2}&display=swap"
      rel="stylesheet"
    />
    <style>
      /* === RESET + BASE === */
      * {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      /* === SLIDE CONTAINER === */
      .slide-deck {
        width: 100vw;
        height: 100vh;
        overflow: hidden;
        position: relative;
      }
      .slide {
        width: 100%;
        height: 100%;
        display: none;
        flex-direction: column;
        justify-content: center;
        padding: 60px 80px;
        position: absolute;
        top: 0;
        left: 0;
      }
      .slide.active {
        display: flex;
      }

      /* === TYPOGRAPHY === */
      h1 {
        font-family: "{Font1}", sans-serif;
        font-size: 3.2rem;
        margin-bottom: 1rem;
      }
      h2 {
        font-family: "{Font1}", sans-serif;
        font-size: 2.4rem;
        margin-bottom: 1.5rem;
      }
      p,
      li {
        font-family: "{Font2}", sans-serif;
        font-size: 1.4rem;
        line-height: 1.8;
      }

      /* === THEME COLORS === */
      :root {
        --bg-primary: #0f172a;
        --bg-slide: #1e293b;
        --text-primary: #f8fafc;
        --text-secondary: #94a3b8;
        --accent: #3b82f6;
        --accent-2: #8b5cf6;
      }

      /* === NAVIGATION UI === */
      .nav-hint {
        position: fixed;
        bottom: 20px;
        right: 30px;
        font-size: 0.8rem;
        color: var(--text-secondary);
        opacity: 0.5;
      }
      .slide-counter {
        position: fixed;
        bottom: 20px;
        left: 30px;
        font-size: 0.8rem;
        color: var(--text-secondary);
      }

      /* === TRANSITIONS === */
      .slide {
        animation: fadeIn 0.4s ease;
      }
      @keyframes fadeIn {
        from {
          opacity: 0;
          transform: translateY(10px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      /* === LAYOUT VARIANTS === */
      .slide.cover {
        justify-content: center;
        align-items: center;
        text-align: center;
      }
      .slide.two-column {
        flex-direction: row;
        gap: 60px;
        align-items: center;
      }
      .slide.two-column .col {
        flex: 1;
      }
      .slide.centered {
        align-items: center;
        text-align: center;
      }

      /* === VISUAL ELEMENTS === */
      .card {
        background: rgba(255, 255, 255, 0.05);
        border-radius: 12px;
        padding: 24px;
        margin: 8px 0;
      }
      .badge {
        display: inline-block;
        background: var(--accent);
        color: white;
        padding: 4px 14px;
        border-radius: 20px;
        font-size: 0.85rem;
      }
      .divider {
        width: 60px;
        height: 4px;
        background: var(--accent);
        border-radius: 2px;
        margin: 16px 0;
      }
      .icon-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
        gap: 20px;
      }

      /* === PRINT / PDF EXPORT === */
      @media print {
        .slide {
          page-break-after: always;
          display: flex !important;
          position: relative;
        }
        .nav-hint,
        .slide-counter {
          display: none;
        }
      }
    </style>
  </head>
  <body>
    <div class="slide-deck" id="deck">
      <!-- SLIDE 1: COVER -->
      <div
        class="slide cover active"
        style="background: linear-gradient(135deg, var(--bg-primary), #1a1a2e);"
      >
        <div class="badge">Topic</div>
        <h1 style="font-size: 3.8rem; margin-top: 20px;">Main Title</h1>
        <p
          style="color: var(--text-secondary); font-size: 1.3rem; margin-top: 12px;"
        >
          Short subtitle description
        </p>
        <div class="divider" style="margin: 20px auto;"></div>
        <p style="color: var(--text-secondary); font-size: 1rem;">
          Author — Date
        </p>
      </div>

      <!-- SLIDE 2+: CONTENT -->
      <div class="slide">
        <h2>Slide Title</h2>
        <div class="divider"></div>
        <ul>
          <li>Key point 1</li>
          <li>Key point 2</li>
          <li>Key point 3</li>
        </ul>
      </div>

      <!-- ... more slides ... -->
    </div>

    <div class="slide-counter">
      <span id="current">1</span> / <span id="total"></span>
    </div>
    <div class="nav-hint">← → or click to navigate</div>

    <script>
      const slides = document.querySelectorAll(".slide");
      let currentSlide = 0;
      document.getElementById("total").textContent = slides.length;

      function showSlide(n) {
        slides[currentSlide].classList.remove("active");
        currentSlide = (n + slides.length) % slides.length;
        slides[currentSlide].classList.add("active");
        document.getElementById("current").textContent = currentSlide + 1;
      }

      document.addEventListener("keydown", (e) => {
        if (e.key === "ArrowRight" || e.key === " ")
          showSlide(currentSlide + 1);
        if (e.key === "ArrowLeft") showSlide(currentSlide - 1);
        if (e.key === "f") document.documentElement.requestFullscreen?.();
        if (e.key === "Escape") document.exitFullscreen?.();
      });

      document.querySelector(".slide-deck").addEventListener("click", (e) => {
        const x = e.clientX / window.innerWidth;
        x > 0.5 ? showSlide(currentSlide + 1) : showSlide(currentSlide - 1);
      });
    </script>
  </body>
</html>

Slide Layout Types

幻灯片布局类型

Each slide should use the layout that best fits its content:
每页幻灯片应选择最适合其内容的布局:

1. Cover Slide

1. 封面幻灯片

  • Centered, extra-large font, gradient background
  • Topic badge + main title + subtitle + author
  • 居中对齐、超大字体、渐变背景
  • 主题标签+主标题+副标题+作者

2. Section Divider

2. 章节分隔页

  • Only section title + number, accent color background
  • Used to separate major sections of the presentation
  • 仅显示章节标题+编号,点缀色背景
  • 用于分隔演示文稿的主要章节

3. Content + Bullets

3. 内容+要点

  • Left-aligned title + list of key points
  • Use icons/emoji at the start of each bullet instead of plain dots
  • 左对齐标题+关键要点列表
  • 用图标/表情符号替代普通圆点作为要点前缀

4. Two-Column

4. 双栏布局

  • .two-column
    layout: text on left, visuals/list/cards on right
  • Great for comparisons, before-after, text+illustration
  • .two-column
    布局:左侧文字,右侧视觉内容/列表/卡片
  • 非常适合对比、前后变化、文字+插图场景

5. Cards Grid

5. 卡片网格

  • 2-3 column grid, each card containing icon + title + short description
  • Great for features, benefits, team members
  • 2-3列网格,每张卡片包含图标+标题+简短描述
  • 适合展示功能、优势、团队成员

6. Big Number / Statistic

6. 大数字/统计数据

  • Huge number in the center (font-size: 5rem+) + small label below
  • Great for data points, KPIs, impact numbers
  • 屏幕中央显示超大数字(字体大小≥5rem)+下方小标签
  • 适合展示数据点、关键绩效指标(KPI)、影响数值

7. Quote / Highlight

7. 引用/高亮

  • Large text, centered, with decorative quotation marks
  • Different background (light accent color)
  • 大字体、居中对齐,搭配装饰性引号
  • 使用不同背景(浅点缀色)

8. Timeline / Steps

8. 时间线/步骤

  • Horizontal or vertical flexbox, dots connecting each step
  • Great for processes, roadmaps, history
  • 水平或垂直弹性布局,用圆点连接各步骤
  • 适合展示流程、路线图、历史

9. Thank You / CTA

9. 致谢/行动号召

  • Centered, simple, contact info or call to action

  • 居中对齐、简洁明了,包含联系方式或行动号召

MANDATORY Design Rules

强制设计规则

  1. 16:9 aspect ratio: Always use
    width: 100vw; height: 100vh
    — each slide fills the entire screen
  2. Minimal text: Maximum 6 lines per slide. If content is long → split into multiple slides
  3. Large font sizes: Heading ≥ 2.4rem, body ≥ 1.3rem — must be readable on a projector
  4. High contrast: Text must be clearly legible against its background. Verify visually
  5. Consistency: Same fonts, same color palette, same spacing throughout the entire deck
  6. No placeholder images: Never use
    <img src="placeholder">
    . Replace with CSS shapes, gradients, icons (emoji or Lucide for React), or inline SVGs
  7. Subtle animation: Only fadeIn on slide transition. No complex animations that distract
  8. Responsive fullscreen: Must work well at all screen sizes
  9. Print-ready: Include
    @media print
    rules so each slide becomes one printed page

  1. 16:9宽高比:始终使用
    width: 100vw; height: 100vh
    ——每页幻灯片填满整个屏幕
  2. 文字精简:每页最多6行文字。如果内容过长→拆分为多页
  3. 大字体:标题≥2.4rem,正文≥1.3rem——确保在投影仪上清晰可读
  4. 高对比度:文字与背景必须清晰可辨。需视觉验证
  5. 一致性:整个演示文稿使用相同字体、配色方案、间距
  6. 无占位图片:绝不使用
    <img src="placeholder">
    。替换为CSS形状、渐变、图标(表情符号或React的Lucide图标)或内联SVG
  7. 微妙动画:仅在幻灯片切换时使用淡入效果。避免使用分散注意力的复杂动画
  8. 响应式全屏:在所有屏幕尺寸下都能正常显示
  9. 可打印:包含
    @media print
    规则,使每页幻灯片对应一个打印页面

When Using React (.jsx) Instead of HTML

使用React (.jsx)替代HTML的情况

If creating a React artifact, use this pattern:
jsx
import { useState, useEffect, useCallback } from "react";

const slides = [
  { type: "cover", title: "...", subtitle: "..." },
  { type: "content", title: "...", points: ["...", "..."] },
  { type: "twoColumn", title: "...", left: "...", right: "..." },
  // ...
];

export default function SlideDeck() {
  const [current, setCurrent] = useState(0);

  const next = useCallback(
    () => setCurrent((c) => (c + 1) % slides.length),
    [],
  );
  const prev = useCallback(
    () => setCurrent((c) => (c - 1 + slides.length) % slides.length),
    [],
  );

  useEffect(() => {
    const handleKey = (e) => {
      if (e.key === "ArrowRight" || e.key === " ") next();
      if (e.key === "ArrowLeft") prev();
      if (e.key === "f") document.documentElement.requestFullscreen?.();
    };
    window.addEventListener("keydown", handleKey);
    return () => window.removeEventListener("keydown", handleKey);
  }, [next, prev]);

  const renderSlide = (slide) => {
    switch (slide.type) {
      case "cover":
        return /* cover layout */;
      case "content":
        return /* content layout */;
      case "twoColumn":
        return /* two-column layout */;
      // ...
    }
  };

  return (
    <div
      className="w-screen h-screen overflow-hidden relative"
      onClick={(e) => (e.clientX > window.innerWidth / 2 ? next() : prev())}
      style={{ fontFamily: "'Outfit', sans-serif" }}
    >
      {renderSlide(slides[current])}
      <div className="fixed bottom-5 left-8 text-sm opacity-40">
        {current + 1} / {slides.length}
      </div>
    </div>
  );
}
Advantages of React: Tailwind CSS utilities, Lucide icons, more complex logic, recharts for charts.

如果创建React文件,采用以下模式:
jsx
import { useState, useEffect, useCallback } from "react";

const slides = [
  { type: "cover", title: "...", subtitle: "..." },
  { type: "content", title: "...", points: ["...", "..."] },
  { type: "twoColumn", title: "...", left: "...", right: "..." },
  // ...
];

export default function SlideDeck() {
  const [current, setCurrent] = useState(0);

  const next = useCallback(
    () => setCurrent((c) => (c + 1) % slides.length),
    [],
  );
  const prev = useCallback(
    () => setCurrent((c) => (c - 1 + slides.length) % slides.length),
    [],
  );

  useEffect(() => {
    const handleKey = (e) => {
      if (e.key === "ArrowRight" || e.key === " ") next();
      if (e.key === "ArrowLeft") prev();
      if (e.key === "f") document.documentElement.requestFullscreen?.();
    };
    window.addEventListener("keydown", handleKey);
    return () => window.removeEventListener("keydown", handleKey);
  }, [next, prev]);

  const renderSlide = (slide) => {
    switch (slide.type) {
      case "cover":
        return /* cover layout */;
      case "content":
        return /* content layout */;
      case "twoColumn":
        return /* two-column layout */;
      // ...
    }
  };

  return (
    <div
      className="w-screen h-screen overflow-hidden relative"
      onClick={(e) => (e.clientX > window.innerWidth / 2 ? next() : prev())}
      style={{ fontFamily: "'Outfit', sans-serif" }}
    >
      {renderSlide(slides[current])}
      <div className="fixed bottom-5 left-8 text-sm opacity-40">
        {current + 1} / {slides.length}
      </div>
    </div>
  );
}
React的优势:可使用Tailwind CSS工具类、Lucide图标、更复杂的逻辑、recharts图表库。

Pre-Delivery Checklist

交付前检查清单

  • Correct number of slides (cover + content + closing)
  • Arrow keys ← → work, click navigates
  • Counter displays correct "X / N"
  • Press F for fullscreen
  • All text is readable with sufficient contrast
  • No broken images or placeholders
  • Google Fonts load correctly (or good fallback)
  • Print/PDF exports properly (
    @media print
    )
  • Consistent style throughout the entire deck
  • Each slide has exactly one key idea, not overloaded with text

  • 幻灯片数量正确(封面+内容+结尾页)
  • 箭头键← →可正常使用,点击可导航
  • 计数器显示正确的“X / N”
  • 按F键可进入全屏
  • 所有文字对比度充足、清晰可读
  • 无损坏图片或占位符
  • Google Fonts加载正常(或有良好的备选方案)
  • 打印/PDF导出功能正常(
    @media print
    生效)
  • 整个演示文稿风格一致
  • 每页幻灯片仅包含一个关键观点,内容不过载

Natural Language Editing

自然语言编辑

After initial creation, the user can request modifications:
  • "Switch to a light theme" → update CSS variables
  • "Add 2 slides about case studies" → insert slides into the array
  • "Slide 3 has too much text, split it" → refactor content across slides
  • "Change the font to Playfair Display" → update Google Fonts link + CSS
  • "Add a chart to slide 5" → use CSS chart or recharts (React)
On each edit, keep unchanged slides intact and only update what the user requested.
初始创建完成后,用户可请求修改:
  • “切换为浅色主题” → 更新CSS变量
  • “添加2页关于案例研究的幻灯片” → 在数组中插入幻灯片
  • “第3页文字太多,拆分它” → 将内容重构到多页幻灯片
  • “将字体改为Playfair Display” → 更新Google Fonts链接+CSS
  • “在第5页添加图表” → 使用CSS图表或recharts(React)
每次编辑时,保留未修改的幻灯片,仅更新用户要求的内容。