mcpField
F
frontend-slides

frontend-slides

github.com/gooseworks-ai文档处理1.3k53 次安装4 天前更新
代码仓库

关于 frontend-slides

此技能已弃用,面向非设计人员,用于创建可在浏览器中播放的动画 HTML 演示文稿。它引导用户选择视觉风格,并生成每页适配视口的独立幻灯片。

主要功能

  1. 01单文件 HTML,内含 CSS 和 JavaScript
  2. 02通过视觉预览选择演示风格
  3. 03使用预设设置幻灯片样式
  4. 04幻灯片适配视口且不在页内滚动
  5. 05转换 PowerPoint 并改进 HTML 演示文稿

适用场景

  1. 01制作浏览器播放的产品推介演示
  2. 02将粗略笔记整理成演示文稿
  3. 03将 PowerPoint 文件转换为 HTML 幻灯片
  4. 04改进现有 HTML 演示文稿

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/gooseworks-ai/goose-skills --skill frontend-slides

安装后在 Claude Code 中输入/frontend-slides确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
skill.meta.json
STYLE_PRESETS.md

详细介绍

已弃用: 此技能已由 goose-graphics 取代。请参阅 skills/composites/goose-graphics/(使用 npx goose-skills install goose-graphics 安装)。幻灯片格式是新版技能支持的七种格式之一,并支持 36 种样式预设、图片来源选取和 PNG 导出。此技能将保留一个发布周期,之后移除。

前端幻灯片技能

创建零依赖、富有动画效果的 HTML 演示文稿,完全在浏览器中运行。此技能通过视觉探索(“展示,而非描述”)帮助非设计师找到自己偏好的美学风格,然后生成达到生产质量的幻灯片。

核心理念

  1. 零依赖 — 使用内联 CSS/JS 的单个 HTML 文件。不需要 npm 或构建工具。
  2. 展示,而非描述 — 人们只有亲眼看到,才知道自己想要什么。生成视觉预览,而非抽象选项。
  3. 独特设计 — 避免千篇一律的“AI 垃圾风”审美。每份演示文稿都应显得经过量身打造。
  4. 生产质量 — 代码应有完善注释、易于访问且性能良好。
  5. 适配视口(重要) — 每张幻灯片都必须恰好适配视口。幻灯片内绝不能滚动。这一点不可妥协。

重要:适配视口要求

本节对所有演示文稿均为强制要求。每张幻灯片在任何屏幕尺寸下都必须完整显示,无需滚动。

黄金法则
Each slide = exactly one viewport height (100vh/100dvh)
Content overflows? → Split into multiple slides or reduce content
Never scroll within a slide.
内容密度限制

为确保适配视口,每张幻灯片必须遵守以下限制:

幻灯片类型内容上限
标题幻灯片1 个标题 + 1 个副标题 + 可选的标语
内容幻灯片1 个标题 + 4-6 个项目符号,或 1 个标题 + 2 个段落
功能网格1 个标题 + 最多 6 张卡片(2x3 或 3x2 网格)
代码幻灯片1 个标题 + 最多 8-10 行代码
引言幻灯片1 段引言(最多 3 行)+ 出处
图片幻灯片1 个标题 + 1 张图片(高度最多为 60vh)

如果内容超出这些限制 → 拆分成多张幻灯片

必需的 CSS 架构

每份演示文稿都必须包含以下用于适配视口的基础 CSS:

/* ===========================================
   VIEWPORT FITTING: MANDATORY BASE STYLES
   These styles MUST be included in every presentation.
   They ensure slides fit exactly in the viewport.
   =========================================== */

/* 1. Lock html/body to viewport */
html, body {
    height: 100%;
    overflow-x: hidden;
}

html {
    scroll-snap-type: y mandatory;
    scroll-behavior: smooth;
}

/* 2. Each slide = exact viewport height */
.slide {
    width: 100vw;
    height: 100vh;
    height: 100dvh; /* Dynamic viewport height for mobile browsers */
    overflow: hidden; /* CRITICAL: Prevent ANY overflow */
    scroll-snap-align: start;
    display: flex;
    flex-direction: column;
    position: relative;
}

/* 3. Content container with flex for centering */
.slide-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    max-height: 100%;
    overflow: hidden; /* Double-protection against overflow */
    padding: var(--slide-padding);
}

/* 4. ALL typography uses clamp() for responsive scaling */
:root {
    /* Titles scale from mobile to desktop */
    --title-size: clamp(1.5rem, 5vw, 4rem);
    --h2-size: clamp(1.25rem, 3.5vw, 2.5rem);
    --h3-size: clamp(1rem, 2.5vw, 1.75rem);

    /* Body text */
    --body-size: clamp(0.75rem, 1.5vw, 1.125rem);
    --small-size: clamp(0.65rem, 1vw, 0.875rem);

    /* Spacing scales with viewport */
    --slide-padding: clamp(1rem, 4vw, 4rem);
    --content-gap: clamp(0.5rem, 2vw, 2rem);
    --element-gap: clamp(0.25rem, 1vw, 1rem);
}

/* 5. Cards/containers use viewport-relative max sizes */
.card, .container, .content-box {
    max-width: min(90vw, 1000px);
    max-height: min(80vh, 700px);
}

/* 6. Lists auto-scale with viewport */
.feature-list, .bullet-list {
    gap: clamp(0.4rem, 1vh, 1rem);
}

.feature-list li, .bullet-list li {
    font-size: var(--body-size);
    line-height: 1.4;
}

/* 7. Grids adapt to available space */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 250px), 1fr));
    gap: clamp(0.5rem, 1.5vw, 1rem);
}

/* 8. Images constrained to viewport */
img, .image-container {
    max-width: 100%;
    max-height: min(50vh, 400px);
    object-fit: contain;
}

/* ===========================================
   RESPONSIVE BREAKPOINTS
   Aggressive scaling for smaller viewports
   =========================================== */

/* Short viewports (< 700px height) */
@media (max-height: 700px) {
    :root {
        --slide-padding: clamp(0.75rem, 3vw, 2rem);
        --content-gap: clamp(0.4rem, 1.5vw, 1rem);
        --title-size: clamp(1.25rem, 4.5vw, 2.5rem);
        --h2-size: clamp(1rem, 3vw, 1.75rem);
    }
}

/* Very short viewports (< 600px height) */
@media (max-height: 600px) {
    :root {
        --slide-padding: clamp(0.5rem, 2.5vw, 1.5rem);
        --content-gap: clamp(0.3rem, 1vw, 0.75rem);
        --title-size: clamp(1.1rem, 4vw, 2rem);
        --body-size: clamp(0.7rem, 1.2vw, 0.95rem);
    }

    /* Hide non-essential elements */
    .nav-dots, .keyboard-hint, .decorative {
        display: none;
    }
}

/* Extremely short (landscape phones, < 500px height) */
@media (max-height: 500px) {
    :root {
        --slide-padding: clamp(0.4rem, 2vw, 1rem);
        --title-size: clamp(1rem, 3.5vw, 1.5rem);
        --h2-size: clamp(0.9rem, 2.5vw, 1.25rem);
        --body-size: clamp(0.65rem, 1vw, 0.85rem);
    }
}

/* Narrow viewports (< 600px width) */
@media (max-width: 600px) {
    :root {
        --title-size: clamp(1.25rem, 7vw, 2.5rem);
    }

    /* Stack grids vertically */
    .grid {
        grid-template-columns: 1fr;
    }
}

/* ===========================================
   REDUCED MOTION
   Respect user preferences
   =========================================== */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.2s !important;
    }

    html {
        scroll-behavior: auto;
    }
}
防溢出检查清单

生成任何演示文稿之前,请先确认:

  1. ✅ 每个 .slide 都有 height: 100vh; height: 100dvh; overflow: hidden;
  2. ✅ 所有字体大小都使用 clamp(min, preferred, max)
  3. ✅ 所有间距都使用 clamp() 或视口单位
  4. ✅ 内容容器都有 max-height 限制
  5. ✅ 图片都有 max-height: min(50vh, 400px) 或类似限制
  6. ✅ 网格使用带有 minmax() 的 auto-fit 实现自适应列数
  7. ✅ 针对 700px、600px、500px 高度设置断点
  8. ✅ 内容元素不使用固定像素高度
  9. ✅ 每张幻灯片的内容符合密度限制
内容无法适配时

如果内容过多:

应该:

  • 拆分成多张幻灯片
  • 减少项目符号(每张最多 5-6 个)
  • 缩短文本(每个项目尽量控制在 1-2 行)
  • 使用更短的代码片段
  • 创建一张“续篇”幻灯片

不应该:

  • 将字体缩小到可读范围以下
  • 完全移除内边距或间距
  • 允许任何滚动
  • 为了塞进内容而硬挤
测试视口适配

生成后,建议用户在以下尺寸下进行测试:

  • 桌面端:1920×1080、1440×900、1280×720
  • 平板:1024×768、768×1024(竖屏)
  • 移动端:375×667、414×896
  • 横屏手机:667×375、896×414

阶段 0:检测模式

首先,确定用户的需求:

模式 A:新建演示文稿

  • 用户想从头开始创建幻灯片
  • 进入阶段 1(内容探索)

模式 B:PPT 转换

  • 用户有要转换的 PowerPoint 文件(.ppt、.pptx)
  • 进入阶段 4(PPT 提取)

模式 C:增强现有演示文稿

  • 用户有 HTML 演示文稿,想要改进
  • 读取现有文件,了解其结构,然后进行增强

阶段 1:内容探索(新建演示文稿)

设计之前,先了解内容。通过 AskUserQuestion 提问:

步骤 1.1:演示文稿背景

问题 1:用途

  • 标题:“用途”
  • 问题:“这份演示文稿的用途是什么?”
  • 选项:
    • “融资路演稿”——向投资者/客户推介创意、产品或公司
    • “教学/教程”——讲解概念、操作指南或教育内容
    • “会议演讲”——在活动、技术分享或主题演讲中发言
    • “内部演示”——团队动态、战略会议或公司动态

问题 2:页数

  • 标题:“长度”
  • 问题:“大约需要多少张幻灯片?”
  • 选项:
    • “短篇(5-10 张)”——快速推介、闪电演讲
    • “中篇(10-20 张)”——标准演示文稿
    • “长篇(20 张以上)”——深入探讨、全面演讲

问题 3:内容

  • 标题:“内容”
  • 问题:“你已经准备好内容了吗?还是需要帮助梳理结构?”
  • 选项:
    • “内容已全部准备好”——只需要设计演示文稿
    • “有一些初步笔记”——需要帮助整理成幻灯片
    • “只有一个主题”——需要帮助创建完整大纲

如果用户已有内容,请让他们分享(文字、要点、图片等)。


阶段 2:风格探索(视觉体验)

重要:这是一个“展示,而非描述”的阶段。

大多数人无法用语言准确表达设计偏好。因此,我们不问“你想要极简风还是大胆风?”,而是生成迷你预览,让他们直接选择。

用户如何选择预设风格

用户可以通过两种方式选择风格:

选项 A:引导式探索(默认)

  • 用户回答有关氛围的问题
  • 技能根据他们的回答生成 3 个预览文件
  • 用户在浏览器中查看预览并选出最喜欢的
  • 这种方式最适合尚未确定具体风格的用户

选项 B:直接选择

  • 如果用户已经知道自己想要什么,可以按名称指定预设风格
  • 示例:“使用 Bold Signal 风格”或“我想要类似 Dark Botanical 的风格”
  • 直接跳到阶段 3

可用预设:

预设氛围适用场景
Bold Signal自信、有冲击力融资路演稿、主题演讲
Electric Studio简洁、专业代理机构演示文稿
Creative Voltage活力十足、复古现代创意推介
Dark Botanical优雅、精致高端品牌
Notebook Tabs编辑风、有条理报告、评审
Pastel Geometry友好、亲切产品概览
Split Pastel活泼、现代创意代理机构
Vintage Editorial风趣、有个性个人品牌
Neon Cyber未来感、科技感科技初创公司
Terminal Green面向开发者开发工具、API
Swiss Modern极简、精准企业、数据
Paper & Ink文学感、深思熟虑讲故事
步骤 2.0:选择风格

首先,询问用户想如何选择风格:

问题:风格选择方式

  • 标题:「风格」
  • 问题:「您想如何选择演示文稿的风格?」
  • 选项:
    • 「展示选项」— 根据我的需求生成 3 个预览(推荐大多数用户选择)
    • 「我知道自己想要什么」— 让我直接从预设列表中选择
    • 「使用客户品牌形象」— 应用客户的品牌颜色、字体和美学风格

如果选择「展示选项」 → 继续步骤 2.1(氛围选择)

如果选择「我知道自己想要什么」 → 显示预设选择器:

问题:选择预设

  • 标题:「预设」
  • 问题:「您想使用哪种风格?」
  • 选项:
    • 「Bold Signal」— 深色背景上的鲜艳卡片,自信且醒目
    • 「Dark Botanical」— 优雅暗色风格,搭配柔和的抽象图形
    • 「Notebook Tabs」— 编辑风格的纸张质感,搭配彩色分区标签
    • 「Pastel Geometry」— 友好的柔和色调,搭配装饰性胶囊图形

(如果用户选择其中一个,则跳至阶段 3。如果他们想查看更多选项,则展示其他预设或继续引导式探索。)

如果选择「使用客户品牌形象」 → 加载客户视觉识别:

  1. 询问这份演示文稿面向哪位客户(或根据上下文推断)
  2. 检查是否存在 clients/<client-name>/brand/visual-identity.md
  3. 如果文件存在: 阅读「Slide Preset」部分。将其中的 CSS :root 自定义属性用作演示文稿的主题变量,将其字体排版用作字体组合,将其字体加载 <link> 标签用于加载字体,并将其标志性元素作为装饰性 CSS 的设计指导。
  4. 如果文件不存在: 告诉用户:「尚未为 [client] 提取视觉识别信息。您想现在提取吗?请提供客户的网站 URL。」然后运行 visual-brand-extractor 技能以生成文件,并继续操作。
  5. 加载客户预设后,跳至阶段 3。
步骤 2.1:氛围选择(引导式探索)

问题 1:感受

  • 标题:「氛围」
  • 问题:「您希望观众观看幻灯片时有什么感受?」
  • 选项:
    • 「印象深刻/充满信心」— 专业、值得信赖,让人觉得这个团队很有实力
    • 「兴奋/充满活力」— 创新、大胆,展现未来感
    • 「平静/专注」— 清晰、周到、易于理解
    • 「受到启发/感动」— 富有情感、善于讲故事、令人难忘
  • 多选:true(最多可选 2 项)
步骤 2.2:生成风格预览

根据用户选择的氛围,在临时目录中生成 3 个不同的风格预览,保存为迷你 HTML 文件。每个预览都应是一张标题幻灯片,并展示:

  • 字体排版(字体选择、标题/正文层级)
  • 调色板(背景色、强调色、文字颜色)
  • 动画风格(元素的进入方式)
  • 整体美学感受

可考虑的预览风格(根据氛围选择 3 种):

氛围风格选项
印象深刻/充满信心"Bold Signal"、"Electric Studio"、"Dark Botanical"
兴奋/充满活力"Creative Voltage"、"Neon Cyber"、"Split Pastel"
平静/专注"Notebook Tabs"、"Paper & Ink"、"Swiss Modern"
受到启发/感动"Dark Botanical"、"Vintage Editorial"、"Pastel Geometry"

重要:切勿使用以下通用设计模式:

  • 白色背景上的紫色渐变
  • Inter、Roboto 或系统字体
  • 标准蓝色作为主色
  • 可预测的主视觉布局

应改用有特色的设计选择:

  • 独特的字体组合(Clash Display、Satoshi、Cormorant Garamond、DM Sans 等)
  • 风格鲜明且协调统一的配色主题
  • 富有氛围感的背景(渐变、细腻的图案、层次感)
  • 标志性的动画时刻
第 2.3 步:展示预览

在此目录中创建预览:.claude-design/slide-previews/

.claude-design/slide-previews/
├── style-a.html   # First style option
├── style-b.html   # Second style option
├── style-c.html   # Third style option
└── assets/        # Any shared assets

每个预览文件都应:

  • 独立完整(内嵌 CSS/JS)
  • 只包含一张展示视觉风格的“标题幻灯片”
  • 带有动画,以展示动效风格
  • 约 50–100 行,不要制作完整演示文稿

向用户展示:

I've created 3 style previews for you to compare:

**Style A: [Name]** — [1 sentence description]
**Style B: [Name]** — [1 sentence description]
**Style C: [Name]** — [1 sentence description]

Open each file to see them in action:
- .claude-design/slide-previews/style-a.html
- .claude-design/slide-previews/style-b.html
- .claude-design/slide-previews/style-c.html

Take a look and tell me:
1. Which style resonates most?
2. What do you like about it?
3. Anything you'd change?

然后使用 AskUserQuestion:

问题:选择你的风格

  • 标题:“风格”
  • 问题:“你更喜欢哪种风格预览?”
  • 选项:
    • “风格 A:[名称]” — [简短描述]
    • “风格 B:[名称]” — [简短描述]
    • “风格 C:[名称]” — [简短描述]
    • “混合元素” — 组合不同风格的元素

如果选择“混合元素”,请询问具体要求。


第 3 阶段:生成演示文稿

现在,根据以下内容生成完整的演示文稿:

  • 第 1 阶段的内容
  • 第 2 阶段的风格
文件结构

对于单个演示文稿:

presentation.html    # Self-contained presentation
assets/              # Images, if any

对于包含多个演示文稿的项目:

[presentation-name].html
[presentation-name]-assets/
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 rel="stylesheet" href="https://api.fontshare.com/v2/css?f[]=...">

    <style>
        /* ===========================================
           CSS CUSTOM PROPERTIES (THEME)
           Easy to modify: change these to change the whole look
           =========================================== */
        :root {
            /* Colors */
            --bg-primary: #0a0f1c;
            --bg-secondary: #111827;
            --text-primary: #ffffff;
            --text-secondary: #9ca3af;
            --accent: #00ffcc;
            --accent-glow: rgba(0, 255, 204, 0.3);

            /* Typography - MUST use clamp() for responsive scaling */
            --font-display: 'Clash Display', sans-serif;
            --font-body: 'Satoshi', sans-serif;
            --title-size: clamp(2rem, 6vw, 5rem);
            --subtitle-size: clamp(0.875rem, 2vw, 1.25rem);
            --body-size: clamp(0.75rem, 1.2vw, 1rem);

            /* Spacing - MUST use clamp() for responsive scaling */
            --slide-padding: clamp(1.5rem, 4vw, 4rem);
            --content-gap: clamp(1rem, 2vw, 2rem);

            /* Animation */
            --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
            --duration-normal: 0.6s;
        }

        /* ===========================================
           BASE STYLES
           =========================================== */
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        html {
            scroll-behavior: smooth;
            scroll-snap-type: y mandatory;
            height: 100%;
        }

        body {
            font-family: var(--font-body);
            background: var(--bg-primary);
            color: var(--text-primary);
            overflow-x: hidden;
            height: 100%;
        }

        /* ===========================================
           SLIDE CONTAINER
           CRITICAL: Each slide MUST fit exactly in viewport
           - Use height: 100vh (NOT min-height)
           - Use overflow: hidden to prevent scroll
           - Content must scale with clamp() values
           =========================================== */
        .slide {
            width: 100vw;
            height: 100vh; /* EXACT viewport height - no scrolling */
            height: 100dvh; /* Dynamic viewport height for mobile */
            padding: var(--slide-padding);
            scroll-snap-align: start;
            display: flex;
            flex-direction: column;
            justify-content: center;
            position: relative;
            overflow: hidden; /* Prevent any content overflow */
        }

        /* Content wrapper that prevents overflow */
        .slide-content {
            flex: 1;
            display: flex;
            flex-direction: column;
            justify-content: center;
            max-height: 100%;
            overflow: hidden;
        }

        /* ===========================================
           RESPONSIVE BREAKPOINTS
           Adjust content for different screen sizes
           =========================================== */
        @media (max-height: 600px) {
            :root {
                --slide-padding: clamp(1rem, 3vw, 2rem);
                --content-gap: clamp(0.5rem, 1.5vw, 1rem);
            }
        }

        @media (max-width: 768px) {
            :root {
                --title-size: clamp(1.5rem, 8vw, 3rem);
            }
        }

        @media (max-height: 500px) and (orientation: landscape) {
            /* Extra compact for landscape phones */
            :root {
                --title-size: clamp(1.25rem, 5vw, 2rem);
                --slide-padding: clamp(0.75rem, 2vw, 1.5rem);
            }
        }

        /* ===========================================
           ANIMATIONS
           Trigger via .visible class (added by JS on scroll)
           =========================================== */
        .reveal {
            opacity: 0;
            transform: translateY(30px);
            transition: opacity var(--duration-normal) var(--ease-out-expo),
                        transform var(--duration-normal) var(--ease-out-expo);
        }

        .slide.visible .reveal {
            opacity: 1;
            transform: translateY(0);
        }

        /* Stagger children */
        .reveal:nth-child(1) { transition-delay: 0.1s; }
        .reveal:nth-child(2) { transition-delay: 0.2s; }
        .reveal:nth-child(3) { transition-delay: 0.3s; }
        .reveal:nth-child(4) { transition-delay: 0.4s; }

        /* ... more styles ... */
    </style>
</head>
<body>
    
    <div class="progress-bar"></div>

    
    <nav class="nav-dots">
        
    </nav>

    
    <section class="slide title-slide">
        <h1 class="reveal">Presentation Title</h1>
        <p class="reveal">Subtitle or author</p>
    </section>

    <section class="slide">
        <h2 class="reveal">Slide Title</h2>
        <p class="reveal">Content...</p>
    </section>

    

    <script>
        /* ===========================================
           SLIDE PRESENTATION CONTROLLER
           Handles navigation, animations, and interactions
           =========================================== */

        class SlidePresentation {
            constructor() {
                // ... initialization
            }

            // ... methods
        }

        // Initialize
        new SlidePresentation();
    </script>
</body>
</html>
必需的 JavaScript 功能

每个演示文稿都应包含:

  1. SlidePresentation 类 — 主控制器

    • 键盘导航(方向键、空格键)
    • 触控/滑动支持
    • 鼠标滚轮导航
    • 更新进度条
    • 导航圆点
  2. Intersection Observer — 用于滚动触发的动画

    • 幻灯片进入视口时添加 .visible 类
    • 高效触发 CSS 动画
  3. 可选增强功能(根据风格选择):

    • 带轨迹效果的自定义光标
    • 粒子系统背景(canvas)
    • 视差效果
    • 悬停时的 3D 倾斜效果
    • 磁性按钮
    • 计数器动画
代码质量要求

注释: 每个部分都应有清晰的注释,说明:

  • 功能是什么
  • 为何需要此部分
  • 如何修改
/* ===========================================
   CUSTOM CURSOR
   Creates a stylized cursor that follows mouse with a trail effect.
   - Uses lerp (linear interpolation) for smooth movement
   - Grows larger when hovering over interactive elements
   =========================================== */
class CustomCursor {
    constructor() {
        // ...
    }
}

无障碍性:

  • 语义化 HTML(<section>、<nav>、<main>)
  • 支持键盘导航
  • 在需要时添加 ARIA 标签
  • 支持减少动态效果设置
@media (prefers-reduced-motion: reduce) {
    .reveal {
        transition: opacity 0.3s ease;
        transform: none;
    }
}

响应式设计与视口适配(至关重要):

完整的 CSS 和指南请参阅上方的“至关重要:视口适配要求”部分。

快速参考:

  • 每个 .slide 都必须设置 height: 100vh; height: 100dvh; overflow: hidden;
  • 所有排版和间距都必须使用 clamp()
  • 遵守内容密度限制(最多 4–6 个项目符号、最多 6 张卡片等)
  • 为以下高度添加断点:700px、600px、500px
  • 内容无法容纳时 → 拆分为多张幻灯片,绝不滚动

第 4 阶段:转换 PPT

转换 PowerPoint 文件时:

第 4.1 步:提取内容

使用 Python 和 python-pptx 提取:

from pptx import Presentation
from pptx.util import Inches, Pt
import json
import os
import base64

def extract_pptx(file_path, output_dir):
    """
    Extract all content from a PowerPoint file.
    Returns a JSON structure with slides, text, and images.
    """
    prs = Presentation(file_path)
    slides_data = []

    # Create assets directory
    assets_dir = os.path.join(output_dir, 'assets')
    os.makedirs(assets_dir, exist_ok=True)

    for slide_num, slide in enumerate(prs.slides):
        slide_data = {
            'number': slide_num + 1,
            'title': '',
            'content': [],
            'images': [],
            'notes': ''
        }

        for shape in slide.shapes:
            # Extract title
            if shape.has_text_frame:
                if shape == slide.shapes.title:
                    slide_data['title'] = shape.text
                else:
                    slide_data['content'].append({
                        'type': 'text',
                        'content': shape.text
                    })

            # Extract images
            if shape.shape_type == 13:  # Picture
                image = shape.image
                image_bytes = image.blob
                image_ext = image.ext
                image_name = f"slide{slide_num + 1}_img{len(slide_data['images']) + 1}.{image_ext}"
                image_path = os.path.join(assets_dir, image_name)

                with open(image_path, 'wb') as f:
                    f.write(image_bytes)

                slide_data['images'].append({
                    'path': f"assets/{image_name}",
                    'width': shape.width,
                    'height': shape.height
                })

        # Extract notes
        if slide.has_notes_slide:
            notes_frame = slide.notes_slide.notes_text_frame
            slide_data['notes'] = notes_frame.text

        slides_data.append(slide_data)

    return slides_data
第 4.2 步:确认内容结构

向用户展示提取的内容:

I've extracted the following from your PowerPoint:

**Slide 1: [Title]**
- [Content summary]
- Images: [count]

**Slide 2: [Title]**
- [Content summary]
- Images: [count]

...

All images have been saved to the assets folder.

Does this look correct? Should I proceed with style selection?
第 4.3 步:选择风格

结合提取的内容,继续第 2 阶段(风格探索)。

第 4.4 步:生成 HTML

将提取的内容转换为所选风格,同时保留:

  • 所有文本内容
  • 所有图片(从 assets 文件夹引用)
  • 幻灯片顺序
  • 任何演讲者备注(作为 HTML 注释或单独的文件)

第 5 阶段:交付

最终输出

演示文稿完成后:

  1. 清理临时文件

    • 如果 .claude-design/slide-previews/ 存在,则将其删除
  2. 打开演示文稿

    • 使用 open [filename].html 在浏览器中打开
  3. 提供摘要

Your presentation is ready!

📁 File: [filename].html
🎨 Style: [Style Name]
📊 Slides: [count]

**Navigation:**
- Arrow keys (← →) or Space to navigate
- Scroll/swipe also works
- Click the dots on the right to jump to a slide

**To customize:**
- Colors: Look for `:root` CSS variables at the top
- Fonts: Change the Fontshare/Google Fonts link
- Animations: Modify `.reveal` class timings

Would you like me to make any adjustments?

风格参考:效果 → 感受对应关系

使用此指南,让动画效果符合预期感受:

戏剧感 / 电影感
  • 缓慢淡入(1–1.5 秒)
  • 大幅缩放过渡(0.9 → 1)
  • 深色背景与聚光灯效果
  • 视差滚动
  • 全幅图像
科技感 / 未来感
  • 霓虹发光效果(使用强调色的 box-shadow)
  • 粒子系统(canvas 背景)
  • 网格图案
  • 使用等宽字体作为点缀
  • 故障或文字扰动效果
  • 青色、洋红色、电光蓝配色
活泼 / 友好
  • 弹跳缓动(弹簧物理效果)
  • 圆角(大圆角)
  • 柔和或鲜艳的色彩
  • 漂浮/上下摆动动画
  • 手绘或插画元素
专业 / 企业风
  • 微妙、快速的动画(200-300ms)
  • 简洁的无衬线字体
  • 海军蓝、石板灰或炭灰色背景
  • 精准的间距和对齐
  • 极少的装饰元素
  • 聚焦数据可视化
平和 / 极简
  • 非常缓慢、微妙的动态效果
  • 大量留白
  • 柔和的配色
  • 衬线字体
  • 宽裕的内边距
  • 以内容为中心,不受干扰
编辑 / 杂志风
  • 鲜明的字体层级
  • 引言和提示框
  • 图文交互
  • 打破网格的布局
  • 衬线标题、无衬线正文字体
  • 黑白配色,搭配一种强调色

动画模式参考

入场动画
/* Fade + Slide Up (most common) */
.reveal {
    opacity: 0;
    transform: translateY(30px);
    transition: opacity 0.6s var(--ease-out-expo),
                transform 0.6s var(--ease-out-expo);
}

.visible .reveal {
    opacity: 1;
    transform: translateY(0);
}

/* Scale In */
.reveal-scale {
    opacity: 0;
    transform: scale(0.9);
    transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}

/* Slide from Left */
.reveal-left {
    opacity: 0;
    transform: translateX(-50px);
    transition: opacity 0.6s, transform 0.6s var(--ease-out-expo);
}

/* Blur In */
.reveal-blur {
    opacity: 0;
    filter: blur(10px);
    transition: opacity 0.8s, filter 0.8s var(--ease-out-expo);
}
背景效果
/* Gradient Mesh */
.gradient-bg {
    background:
        radial-gradient(ellipse at 20% 80%, rgba(120, 0, 255, 0.3) 0%, transparent 50%),
        radial-gradient(ellipse at 80% 20%, rgba(0, 255, 200, 0.2) 0%, transparent 50%),
        var(--bg-primary);
}

/* Noise Texture */
.noise-bg {
    background-image: url("data:image/svg+xml,..."); /* Inline SVG noise */
}

/* Grid Pattern */
.grid-bg {
    background-image:
        linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
        linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
    background-size: 50px 50px;
}
交互效果
/* 3D Tilt on Hover */
class TiltEffect {
    constructor(element) {
        this.element = element;
        this.element.style.transformStyle = 'preserve-3d';
        this.element.style.perspective = '1000px';
        this.bindEvents();
    }

    bindEvents() {
        this.element.addEventListener('mousemove', (e) => {
            const rect = this.element.getBoundingClientRect();
            const x = (e.clientX - rect.left) / rect.width - 0.5;
            const y = (e.clientY - rect.top) / rect.height - 0.5;

            this.element.style.transform = `
                rotateY(${x * 10}deg)
                rotateX(${-y * 10}deg)
            `;
        });

        this.element.addEventListener('mouseleave', () => {
            this.element.style.transform = 'rotateY(0) rotateX(0)';
        });
    }
}

故障排除

常见问题

字体未加载:

  • 检查 Fontshare/Google Fonts URL
  • 确保 CSS 中的字体名称匹配

动画未触发:

  • 确认 Intersection Observer 正在运行
  • 检查是否已添加 .visible 类

滚动捕捉不起作用:

  • 确保在 html/body 上设置了 scroll-snap-type
  • 每张幻灯片都需要 scroll-snap-align: start

移动设备问题:

  • 在 768px 断点处禁用开销较大的效果
  • 测试触摸事件
  • 减少粒子数量或禁用画布

性能问题:

  • 谨慎使用 will-change
  • 优先使用 transform 和 opacity 动画
  • 对滚动 / 鼠标移动处理程序进行节流

相关技能

  • learn — 为演示文稿生成 FORZARA.md 文档
  • frontend-design — 用于制作超出幻灯片范围的复杂交互页面
  • design-and-refine:design-lab — 用于迭代组件设计

示例会话流程

  1. 用户:「我想为我的 AI 初创公司制作一份融资演示文稿」
  2. 技能询问用途、长度和内容
  3. 用户分享要点和关键信息
  4. 技能询问期望的感受(印象深刻 + 兴奋)
  5. 技能生成 3 个风格预览
  6. 用户选择风格 B(霓虹赛博风),并要求使用更暗的背景
  7. 技能生成包含所有幻灯片的完整演示文稿
  8. 技能在浏览器中打开演示文稿
  9. 用户要求调整特定幻灯片
  10. 交付最终演示文稿

转换会话流程

  1. 用户:「将我的 slides.pptx 转换为网页演示文稿」
  2. 技能从 PPT 中提取内容和图像
  3. 技能与用户确认提取的内容
  4. 技能询问期望的感受 / 风格
  5. 技能生成风格预览
  6. 用户选择一种风格
  7. 技能生成保留原有资源的 HTML 演示文稿
  8. 交付最终演示文稿

评论

支持 Markdown