关于 screenshots
screenshots 是一项使用 Playwright 捕获应用截图的技能,适用于营销、社交媒体、落地页或文档。它指导确定 URL、收集需求、检查应用并生成 HiDPI 截图。
主要功能
- 01Playwright 截图流程
- 022 倍 HiDPI 捕获
- 03收集 URL 与截图需求
- 04检查代码库和路由
- 05截图故障排查指导
适用场景
- 01准备 Product Hunt 应用图片
- 02制作社交媒体视觉素材
- 03捕获落地页截图
- 04记录应用界面
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ sickn33/ agentic-awesome-skills --skill screenshots安装后在 Claude Code 中输入/screenshots确认已加载。
详细介绍
截图
直接使用 Playwright 为你的应用生成营销级截图。截图将以真正的 HiDPI(2x Retina)分辨率捕获,使用 deviceScaleFactor: 2。
何时使用此技能
在以下情况下使用此技能:
- 用户想为 Product Hunt 创建截图
- 为社交媒体创建截图
- 为落地页生成图片
- 创建文档截图
- 用户请求制作营销级应用截图
先决条件
必须可以使用 Playwright。检查是否已安装:
npm ls playwright @playwright/test --depth=0
如果未找到,请告知用户:
必须安装 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:
- Header:"Features"
- Question:"I found these features in your codebase. Which would you like to screenshot?"
- Options:列出发现的 3-4 个关键功能,外加 "Let me pick specific ones"
如果用户想要特定的功能,请提出后续问题以明确具体要截取的内容。
第 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: false, // set true only for an authorized test account
loginUrl: '[LOGIN_URL]',
email: process.env.SCREENSHOT_EMAIL,
password: process.env.SCREENSHOT_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(new URL(AUTH.loginUrl, BASE_URL).href);
// 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.waitForURL('[EXPECTED_AUTHENTICATED_URL]'); // replace with verified app route
console.log('Authenticated route reached');
}
// 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(error => { console.error(error); process.exitCode = 1; });
运行脚本
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 状态:使用演示数据/种子数据来呈现真实内容
- 统一尺寸:所有屏幕截图使用相同的视口
- 等待内容加载:使用应用专属的就绪选择器;网络空闲并不能证明内容已就绪
- 隐藏开发工具:确保画面中没有浏览器扩展或开发覆盖层
- 深色模式变体:如果支持,可考虑同时截取浅色和深色模式
限制
- 仅当任务明确符合上述范围时,才使用此技能。
- 不要将输出视为环境特定验证、测试或专家审查的替代品。
- 如果缺少必要输入、权限、安全边界或成功标准,请暂停并请求澄清。
该脚本只是可自定义的示例。提交凭据前请确认定位器正确;通用的首个匹配项选择器可能会选中错误的表单。屏幕截图权限并不代表有权修改账户、发布图像或截取第三方私人数据。请使用演示数据,并检查/编辑每张图像以移除敏感信息。在输出目录中验证截图文件名,并避免覆盖现有文件。整页/元素尺寸与视口示例不同;请验证实际像素尺寸。生产工具应在 finally 块中关闭浏览器。