关于 screenshots
screenshots 是一项使用 Playwright 为应用生成营销截图的技能,适用于产品展示、社交媒体和文档素材制作。它支持以 2 倍 HiDPI 分辨率捕获页面,并指导从应用路由中发掘截图内容。
主要功能
- 01使用 Playwright 捕获应用页面
- 02以 2 倍 HiDPI 分辨率截图
- 03分析路由以发现页面
- 04支持登录后截图
- 05提供截图故障排查建议
适用场景
- 01制作产品发布展示图
- 02为社交媒体准备功能截图
- 03生成落地页营销素材
- 04制作界面文档参考图
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ steveclarke/ dotfiles --skill screenshots安装后在 Claude Code 中输入/screenshots确认已加载。
详细介绍
直接使用 Playwright 为应用生成营销级截图。截图将以真正的 HiDPI(2 倍视网膜屏)分辨率捕获,使用 deviceScaleFactor: 2。
前置条件
必须能使用 Playwright。检查是否已安装:
npx playwright --version 2>/dev/null || npm ls playwright 2>/dev/null | grep playwright
如果未找到,请告知用户:
必须安装 Playwright。请使用以下命令安装:
npm install -D playwright或npm install -D @playwright/test
第 1 步:确定应用 URL
如果提供了 $1,请将其用作应用 URL。
如果未提供 URL:
- 查看
package.json中的脚本,判断开发服务器是否可能正在运行 - 使用
AskUserQuestion询问用户 URL,或询问是否需要协助启动开发服务器
建议的常见默认 URL:
http://localhost:3000(Next.js、Create React App、Rails)http://localhost:5173(Vite)http://localhost:4000(Phoenix)http://localhost:8080(Vue CLI、通用)
第 2 步:收集需求
使用 AskUserQuestion 提出以下问题:
问题 1:截图数量
- 标题:「数量」
- 问题:「您需要多少张截图?」
- 选项:
- 「3-5」— 快速展示主要功能
- 「5-10」— 全面展示各项功能
- 「10+」— 完整的营销素材套件
问题 2:用途
- 标题:「用途」
- 问题:「这些截图将用于什么场景?」
- 选项:
- 「Product Hunt」— 主视觉截图和功能亮点
- 「社交媒体」— 吸引眼球的功能演示
- 「落地页」— 营销版块和产品优势
- 「文档」— UI 参考和教程
问题 3:身份验证
- 标题:「身份验证」
- 问题:「访问您想截图的功能是否需要登录?」
- 选项:
- 「无需登录」— 仅限公开页面
- 「需要,我会提供凭据」— 需要先登录
如果用户选择「需要,我会提供凭据」,请追问:
- 「登录页面 URL 是什么?」(例如:
/login、/sign-in) - 「电子邮件/用户名是什么?」
- 「密码是什么?」
脚本会使用 Playwright 的智能定位器自动检测登录表单字段。
第 3 步:分析代码库,寻找功能
全面探索代码库,了解应用并找出截图机会。
3.1:先阅读文档
务必先阅读以下文件,了解应用的功能:
-
README.md(以及子目录中的任何 README 文件)— 通读完整 README,了解:
- 应用的用途及其解决的问题
- 主要功能和能力
- 已有文档中记录的截图或功能说明
-
CHANGELOG.md 或 HISTORY.md — 值得重点展示的近期功能
-
docs/ 目录 — 任何其他功能文档
3.2:分析路由,查找页面
阅读路由配置,找出所有可用页面:
| 框架 | 要阅读的文件 | 要查找的内容 |
|---|---|---|
| Next.js App Router | app/ 目录结构 | 每个包含 page.tsx 的文件夹都是一个路由 |
| Next.js Pages Router | pages/ 目录 | 每个文件都是一个路由 |
| Rails | config/routes.rb | 通读整个文件,查找所有路由 |
| React Router | 搜索 createBrowserRouter 或 <Route | 包含路径的路由定义 |
| Vue Router | src/router/index.js 或 router.js | 带有路径定义的路由数组 |
| SvelteKit | src/routes/ 目录 | 每个包含 +page.svelte 的文件夹都是一个路由 |
| Remix | app/routes/ 目录 | 基于文件的路由 |
| Laravel | routes/web.php | 路由定义 |
| Django | urls.py 文件 | URL 模式 |
| Express | 搜索 app.get、router.get | 路由处理程序 |
重要:请实际阅读这些文件,不要只检查它们是否存在。路由定义会告诉您哪些页面可供截图。
3.3:识别关键组件
查找代表可截图功能的组件:
- 仪表板组件
- 具有独特 UI 的功能板块
- 表单和交互式输入
- 数据可视化(图表、曲线图、表格)
- 模态框和对话框
- 导航和侧边栏
- 设置面板
- 用户资料板块
3.4:检查营销素材
查找现有营销内容,了解关键功能:
- 落地页组件(通常位于
components/landing/或components/marketing/) - 功能列表组件
- 定价表格
- 用户评价板块
3.5:构建功能列表
创建一份全面的已发现功能列表,包含:
- 功能名称(来自 README 或组件名称)
- URL 路径(来自路由)
- 要聚焦的 CSS 选择器(来自组件结构)
- 所需 UI 状态(已登录、已填充数据、模态框已打开、已选择特定选项卡)
第 4 步:与用户一起规划截图
向用户展示已发现的功能,并请他们确认或修改列表。
使用 AskUserQuestion:
- 标题:"功能"
- 问题:"我在你的代码库中发现了这些功能。你想截取哪些功能?"
- 选项:列出发现的 3-4 个关键功能,以及“让我选择特定功能”
如果用户想选择特定功能,请继续提问,明确具体要截取的内容。
第 5 步:创建截图目录
mkdir -p screenshots
第 6 步:生成并运行 Playwright 脚本
创建一个使用 Playwright 并设置适当 HiDPI 参数的 Node.js 脚本。脚本应:
- 使用
deviceScaleFactor: 2,获得真正的 Retina 分辨率 - 将视口设为 1440x900(生成 2880x1800 像素的图像)
- 如果提供了凭据,则处理身份验证
- 导航至每个页面并截取屏幕截图
脚本模板
将此脚本写入临时文件(例如 screenshot-script.mjs)并执行:
import { chromium } from 'playwright';
const BASE_URL = '[APP_URL]';
const SCREENSHOTS_DIR = './screenshots';
// Authentication config (if needed)
const AUTH = {
needed: [true|false],
loginUrl: '[LOGIN_URL]',
email: '[EMAIL]',
password: '[PASSWORD]',
};
// Screenshots to capture
const SCREENSHOTS = [
{ name: '01-feature-name', url: '/path', waitFor: '[optional-selector]' },
{ name: '02-another-feature', url: '/another-path' },
// ... add all planned screenshots
];
async function main() {
const browser = await chromium.launch();
// Create context with HiDPI settings
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2, // This is the key for true retina screenshots
});
const page = await context.newPage();
// Handle authentication if needed
if (AUTH.needed) {
console.log('Logging in...');
await page.goto(AUTH.loginUrl);
// Smart login: try multiple common patterns for email/username field
const emailField = page.locator([
'input[type="email"]',
'input[name="email"]',
'input[id="email"]',
'input[placeholder*="email" i]',
'input[name="username"]',
'input[id="username"]',
'input[type="text"]',
].join(', ')).first();
await emailField.fill(AUTH.email);
// Smart login: try multiple common patterns for password field
const passwordField = page.locator([
'input[type="password"]',
'input[name="password"]',
'input[id="password"]',
].join(', ')).first();
await passwordField.fill(AUTH.password);
// Smart login: try multiple common patterns for submit button
const submitButton = page.locator([
'button[type="submit"]',
'input[type="submit"]',
'button:has-text("Sign in")',
'button:has-text("Log in")',
'button:has-text("Login")',
'button:has-text("Submit")',
].join(', ')).first();
await submitButton.click();
await page.waitForLoadState('networkidle');
console.log('Login complete');
}
// Capture each screenshot
for (const shot of SCREENSHOTS) {
console.log(`Capturing: ${shot.name}`);
await page.goto(`${BASE_URL}${shot.url}`);
await page.waitForLoadState('networkidle');
// Optional: wait for specific element
if (shot.waitFor) {
await page.waitForSelector(shot.waitFor);
}
// Optional: perform actions before screenshot
if (shot.actions) {
for (const action of shot.actions) {
if (action.click) await page.click(action.click);
if (action.fill) await page.fill(action.fill.selector, action.fill.value);
if (action.wait) await page.waitForTimeout(action.wait);
}
}
await page.screenshot({
path: `${SCREENSHOTS_DIR}/${shot.name}.png`,
fullPage: shot.fullPage || false,
});
console.log(` Saved: ${shot.name}.png`);
}
await browser.close();
console.log('Done!');
}
main().catch(console.error);
运行脚本
node screenshot-script.mjs
运行后,清理临时脚本:
rm screenshot-script.mjs
第 7 步:高级截图选项
聚焦元素截图
若要截取特定元素,而不是整个视口:
const element = await page.locator('[CSS_SELECTOR]');
await element.screenshot({ path: `${SCREENSHOTS_DIR}/element.png` });
整页截图
对于可滚动内容,截取整个页面:
await page.screenshot({
path: `${SCREENSHOTS_DIR}/full-page.png`,
fullPage: true
});
等待动画结束
如果页面有动画,请等待动画结束:
await page.waitForTimeout(500); // Wait 500ms for animations
截图前点击元素
若要截取模态框、下拉菜单或悬停状态:
await page.click('button.open-modal');
await page.waitForSelector('.modal-content');
await page.screenshot({ path: `${SCREENSHOTS_DIR}/modal.png` });
深色模式截图
如果应用支持深色模式:
// Set dark mode preference
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
colorScheme: 'dark',
});
第 8 步:文件命名规范
使用描述性 kebab-case 文件名,并添加数字前缀以便排序:
| 功能 | 文件名 |
|---|---|
| 仪表板概览 | 01-dashboard-overview.png |
| 链接管理 | 02-link-inbox.png |
| 版本编辑器 | 03-edition-editor.png |
| 分析 | 04-analytics.png |
| 设置 | 05-settings.png |
第 9 步:验证并总结
截取所有屏幕截图后,验证结果:
ls -la screenshots/*.png
sips -g pixelWidth -g pixelHeight screenshots/*.png 2>/dev/null || file screenshots/*.png
向用户提供总结:
- 列出所有生成文件及其路径
- 确认分辨率(在 1440x900 视口下,2x Retina 分辨率应为 2880x1800)
- 说明文件总大小
- 建议后续操作
输出示例:
Generated 5 marketing screenshots:
screenshots/
├── 01-dashboard-overview.png (1.2 MB, 2880x1800 @ 2x)
├── 02-link-inbox.png (456 KB, 2880x1800 @ 2x)
├── 03-edition-editor.png (890 KB, 2880x1800 @ 2x)
├── 04-analytics.png (567 KB, 2880x1800 @ 2x)
└── 05-settings.png (234 KB, 2880x1800 @ 2x)
All screenshots are true retina-quality (2x deviceScaleFactor) and ready for marketing use.
错误处理
- 未找到 Playwright:建议运行
npm install -D playwright - 页面未加载:检查开发服务器是否正在运行,并建议启动它
- 登录失败:智能定位器会尝试常见模式,但可能无法处理不常见的登录表单。如果登录失败,请分析登录页面的 HTML,找到正确的选择器并自定义脚本。
- 未找到元素:验证 CSS 选择器,并提出改为截取整页
- 截图失败:检查磁盘空间,并确认对截图目录具有写入权限
获得最佳效果的提示
- 保持 UI 状态整洁:使用演示数据或种子数据呈现逼真的内容
- 保持尺寸一致:所有截图使用相同的视口
- 等待内容加载:使用
waitForLoadState('networkidle')确保所有内容都已加载 - 隐藏开发工具:确保没有显示浏览器扩展或开发叠加层
- 深色模式变体:如果支持,可考虑同时截取浅色和深色模式的截图