screenshots by shpigford/skills
npx skills add https://github.com/shpigford/skills --skill screenshots直接使用 Playwright 生成具有营销品质的应用截图。截图使用 deviceScaleFactor: 2 以真正的 HiDPI(2x 视网膜)分辨率捕获。
必须已安装 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。
如果未提供 URL:
package.json 中的脚本,检查开发服务器是否可能正在运行AskUserQuestion 询问用户 URL 或提供帮助以启动开发服务器广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
建议的常见默认 URL:
http://localhost:3000 (Next.js, Create React App, Rails)http://localhost:5173 (Vite)http://localhost:4000 (Phoenix)http://localhost:8080 (Vue CLI, 通用)使用 AskUserQuestion 询问以下问题:
问题 1:截图数量
问题 2:用途
问题 3:身份验证
如果用户选择"是的,我将提供凭据",则询问后续问题:
/login, /sign-in)脚本将使用 Playwright 的智能定位器自动检测登录表单字段。
彻底探索代码库以了解应用并识别截图机会。
始终从阅读这些文件开始 以了解应用的功能:
阅读路由配置以发现所有可用页面:
| 框架 | 要读取的文件 | 查找内容 |
|---|---|---|
| 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 | 路由处理程序 |
重要提示:实际读取这些文件,而不仅仅是检查它们是否存在。路由定义告诉您哪些页面可用于截图。
寻找代表可截图功能的组件:
寻找暗示关键功能的现有营销内容:
components/landing/ 或 components/marketing/ 中)创建一个包含以下内容的全面已发现功能列表:
向用户展示已发现的功能,并要求他们确认或修改列表。
使用 AskUserQuestion:
如果用户想要特定的功能,请询问后续问题以明确要捕获的确切内容。
mkdir -p screenshots
创建一个使用 Playwright 并具有正确 HiDPI 设置的 Node.js 脚本。该脚本应:
deviceScaleFactor: 2 以获得真正的视网膜分辨率将此脚本写入临时文件 (例如 screenshot-script.mjs) 并执行:
import { chromium } from 'playwright';
const BASE_URL = '[APP_URL]';
const SCREENSHOTS_DIR = './screenshots';
// 身份验证配置(如果需要)
const AUTH = {
needed: [true|false],
loginUrl: '[LOGIN_URL]',
email: '[EMAIL]',
password: '[PASSWORD]',
};
// 要捕获的截图
const SCREENSHOTS = [
{ name: '01-feature-name', url: '/path', waitFor: '[optional-selector]' },
{ name: '02-another-feature', url: '/another-path' },
// ... 添加所有计划的截图
];
async function main() {
const browser = await chromium.launch();
// 使用 HiDPI 设置创建上下文
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2, // 这是实现真正视网膜截图的关键
});
const page = await context.newPage();
// 如果需要,处理身份验证
if (AUTH.needed) {
console.log('Logging in...');
await page.goto(AUTH.loginUrl);
// 智能登录:尝试多种常见的邮箱/用户名字段模式
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);
// 智能登录:尝试多种常见的密码字段模式
const passwordField = page.locator([
'input[type="password"]',
'input[name="password"]',
'input[id="password"]',
].join(', ')).first();
await passwordField.fill(AUTH.password);
// 智能登录:尝试多种常见的提交按钮模式
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');
}
// 捕获每张截图
for (const shot of SCREENSHOTS) {
console.log(`Capturing: ${shot.name}`);
await page.goto(`${BASE_URL}${shot.url}`);
await page.waitForLoadState('networkidle');
// 可选:等待特定元素
if (shot.waitFor) {
await page.waitForSelector(shot.waitFor);
}
// 可选:在截图前执行操作
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
要截取特定元素而非整个视口:
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); // 等待 500ms 让动画完成
要捕获模态框、下拉菜单或悬停状态:
await page.click('button.open-modal');
await page.waitForSelector('.modal-content');
await page.screenshot({ path: `${SCREENSHOTS_DIR}/modal.png` });
如果应用支持深色模式:
// 设置深色模式偏好
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
colorScheme: 'dark',
});
使用描述性的、带数字前缀的短横线分隔文件名以便排序:
| 功能 | 文件名 |
|---|---|
| 仪表板概览 | 01-dashboard-overview.png |
| 链接管理 | 02-link-inbox.png |
| 版本编辑器 | 03-edition-editor.png |
| 分析 | 04-analytics.png |
| 设置 | 05-settings.png |
捕获所有截图后,验证结果:
ls -la screenshots/*.png
sips -g pixelWidth -g pixelHeight screenshots/*.png 2>/dev/null || file screenshots/*.png
向用户提供总结:
示例输出:
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.
npm install -D playwrightwaitForLoadState('networkidle') 确保所有内容都已加载每周安装次数
95
仓库
GitHub 星标数
161
首次出现
2026 年 1 月 24 日
安全审计
安装于
opencode83
claude-code80
cursor80
codex78
gemini-cli78
github-copilot69
Generate marketing-quality screenshots of your app using Playwright directly. Screenshots are captured at true HiDPI (2x retina) resolution using deviceScaleFactor: 2.
Playwright must be available. Check for it:
npx playwright --version 2>/dev/null || npm ls playwright 2>/dev/null | grep playwright
If not found, inform the user:
Playwright is required. Install it with:
npm install -D playwrightornpm install -D @playwright/test
If $1 is provided, use it as the app URL.
If no URL is provided:
package.json scriptsAskUserQuestion to ask the user for the URL or offer to help start the dev serverCommon default URLs to suggest:
http://localhost:3000 (Next.js, Create React App, Rails)http://localhost:5173 (Vite)http://localhost:4000 (Phoenix)http://localhost:8080 (Vue CLI, generic)Use AskUserQuestion with the following questions:
Question 1: Screenshot count
Question 2: Purpose
Question 3: Authentication
If user selects "Yes, I'll provide credentials", ask follow-up questions:
/login, /sign-in)The script will automatically detect login form fields using Playwright's smart locators.
Thoroughly explore the codebase to understand the app and identify screenshot opportunities.
Always start by reading these files to understand what the app does:
README.md (and any README files in subdirectories) - Read the full README to understand:
CHANGELOG.md or HISTORY.md - Recent features worth highlighting
docs/ directory - Any additional documentation about features
Read the routing configuration to discover all available pages:
| Framework | File to Read | What to Look For |
|---|---|---|
| Next.js App Router | app/ directory structure | Each folder with page.tsx is a route |
| Next.js Pages Router | pages/ directory | Each file is a route |
| Rails | config/routes.rb | Read the entire file for all routes |
| React Router | Search for createBrowserRouter or |
Important : Actually read these files, don't just check if they exist. The route definitions tell you what pages are available for screenshots.
Look for components that represent screenshottable features:
Look for existing marketing content that hints at key features:
components/landing/ or components/marketing/)Create a comprehensive list of discovered features with:
Present the discovered features to the user and ask them to confirm or modify the list.
Use AskUserQuestion:
If user wants specific ones, ask follow-up questions to clarify exactly what to capture.
mkdir -p screenshots
Create a Node.js script that uses Playwright with proper HiDPI settings. The script should:
deviceScaleFactor: 2 for true retina resolutionWrite this script to a temporary file (e.g., screenshot-script.mjs) and execute it:
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
After running, clean up the temporary script:
rm screenshot-script.mjs
To screenshot a specific element instead of the full viewport:
const element = await page.locator('[CSS_SELECTOR]');
await element.screenshot({ path: `${SCREENSHOTS_DIR}/element.png` });
For scrollable content, capture the entire page:
await page.screenshot({
path: `${SCREENSHOTS_DIR}/full-page.png`,
fullPage: true
});
If the page has animations, wait for them to complete:
await page.waitForTimeout(500); // Wait 500ms for animations
To capture a modal, dropdown, or hover state:
await page.click('button.open-modal');
await page.waitForSelector('.modal-content');
await page.screenshot({ path: `${SCREENSHOTS_DIR}/modal.png` });
If the app supports dark mode:
// Set dark mode preference
const context = await browser.newContext({
viewport: { width: 1440, height: 900 },
deviceScaleFactor: 2,
colorScheme: 'dark',
});
Use descriptive, kebab-case filenames with numeric prefixes for ordering:
| Feature | Filename |
|---|---|
| Dashboard overview | 01-dashboard-overview.png |
| Link management | 02-link-inbox.png |
| Edition editor | 03-edition-editor.png |
| Analytics | 04-analytics.png |
| Settings | 05-settings.png |
After capturing all screenshots, verify the results:
ls -la screenshots/*.png
sips -g pixelWidth -g pixelHeight screenshots/*.png 2>/dev/null || file screenshots/*.png
Provide a summary to the user:
Example output:
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.
npm install -D playwrightwaitForLoadState('networkidle') to ensure all content loadsWeekly Installs
95
Repository
GitHub Stars
161
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykFail
Installed on
opencode83
claude-code80
cursor80
codex78
gemini-cli78
github-copilot69
GitHub Actions 官方文档查询助手 - 精准解答 CI/CD 工作流问题
40,500 周安装
<Route| Route definitions with paths |
| Vue Router | src/router/index.js or router.js | Routes array with path definitions |
| SvelteKit | src/routes/ directory | Each folder with +page.svelte is a route |
| Remix | app/routes/ directory | File-based routing |
| Laravel | routes/web.php | Route definitions |
| Django | urls.py files | URL patterns |
| Express | Search for app.get, router.get | Route handlers |