screenshots by sickn33/antigravity-awesome-skills
npx skills add https://github.com/sickn33/antigravity-awesome-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:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
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, 通用)使用 AskUserQuestion 询问以下问题:
问题 1:截图数量
问题 2:用途
问题 3:认证
如果用户选择 "Yes, I'll provide credentials",请询问后续问题:
/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';
// 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
要截取特定元素而非整个视口:
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',
});
使用描述性的、带数字前缀的短横线分隔文件名以便排序:
| 功能 | 文件名 |
|---|---|
| 仪表板概览 | 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') 以确保所有内容都已加载每周安装次数
109
代码仓库
GitHub 星标数
27.4K
首次出现
Jan 31, 2026
安全审计
已安装于
opencode105
codex104
kimi-cli103
gemini-cli103
github-copilot103
amp102
Generate marketing-quality screenshots of your app using Playwright directly. Screenshots are captured at true HiDPI (2x retina) resolution using deviceScaleFactor: 2.
Use this skill when:
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
109
Repository
GitHub Stars
27.4K
First Seen
Jan 31, 2026
Security Audits
Gen Agent Trust HubWarnSocketPassSnykFail
Installed on
opencode105
codex104
kimi-cli103
gemini-cli103
github-copilot103
amp102
README 国际化工具:自动化翻译与多语言文档管理 | readme-i18n
44,000 周安装
<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 |