screenshots

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese
Generate marketing-quality screenshots of your app using Playwright directly. Screenshots are captured at true HiDPI (2x retina) resolution using
deviceScaleFactor: 2
.
直接使用Playwright生成专业营销级别的应用截图。截图将通过
deviceScaleFactor: 2
参数以真实的HiDPI(2倍retina)分辨率捕获。

Prerequisites

前置条件

Playwright must be available. Check for it:
bash
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 playwright
or
npm install -D @playwright/test
必须已安装Playwright。检查是否已安装:
bash
npx playwright --version 2>/dev/null || npm ls playwright 2>/dev/null | grep playwright
如果未找到,请告知用户:
需要安装Playwright。执行以下命令安装:
npm install -D playwright
npm install -D @playwright/test

Step 1: Determine App URL

步骤1:确定应用URL

If
$1
is provided, use it as the app URL.
If no URL is provided:
  1. Check if a dev server is likely running by looking for
    package.json
    scripts
  2. Use
    AskUserQuestion
    to ask the user for the URL or offer to help start the dev server
Common 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)
如果提供了
$1
,则将其作为应用URL。
如果未提供URL:
  1. 通过查看
    package.json
    中的脚本判断是否可能有开发服务器在运行
  2. 使用
    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、通用型)

Step 2: Gather Requirements

步骤2:收集需求

Use
AskUserQuestion
with the following questions:
Question 1: Screenshot count
  • Header: "Count"
  • Question: "How many screenshots do you need?"
  • Options:
    • "3-5" - Quick set of key features
    • "5-10" - Comprehensive feature coverage
    • "10+" - Full marketing suite
Question 2: Purpose
  • Header: "Purpose"
  • Question: "What will these screenshots be used for?"
  • Options:
    • "Product Hunt" - Hero shots and feature highlights
    • "Social media" - Eye-catching feature demos
    • "Landing page" - Marketing sections and benefits
    • "Documentation" - UI reference and tutorials
Question 3: Authentication
  • Header: "Auth"
  • Question: "Does the app require login to access the features you want to screenshot?"
  • Options:
    • "No login needed" - Public pages only
    • "Yes, I'll provide credentials" - Need to log in first
If user selects "Yes, I'll provide credentials", ask follow-up questions:
  • "What is the login page URL?" (e.g.,
    /login
    ,
    /sign-in
    )
  • "What is the email/username?"
  • "What is the password?"
The script will automatically detect login form fields using Playwright's smart locators.
使用
AskUserQuestion
提出以下问题:
问题1:截图数量
  • 标题:"数量"
  • 问题:"你需要多少张截图?"
  • 选项:
    • "3-5张" - 关键功能快速合集
    • "5-10张" - 全面覆盖功能
    • "10张以上" - 完整营销素材包
问题2:使用场景
  • 标题:"用途"
  • 问题:"这些截图将用于什么场景?"
  • 选项:
    • "Product Hunt" - 主视觉图和功能亮点
    • "社交媒体" - 吸睛的功能演示
    • "着陆页" - 营销板块和优势展示
    • "文档" - UI参考和教程
问题3:认证需求
  • 标题:"认证"
  • 问题:"你要截图的功能是否需要登录才能访问?"
  • 选项:
    • "无需登录" - 仅公共页面
    • "需要,我会提供凭据" - 需先登录
如果用户选择"需要,我会提供凭据",继续询问以下问题:
  • "登录页面的URL是什么?"(例如:
    /login
    /sign-in
  • "邮箱/用户名是什么?"
  • "密码是什么?"
脚本将通过Playwright的智能定位器自动检测登录表单字段。

Step 3: Analyze Codebase for Features

步骤3:分析代码库以挖掘功能

Thoroughly explore the codebase to understand the app and identify screenshot opportunities.
全面探索代码库,了解应用并确定可截图的内容。

3.1: Read Documentation First

3.1:优先阅读文档

Always start by reading these files to understand what the app does:
  1. README.md (and any README files in subdirectories) - Read the full README to understand:
    • What the app is and what problem it solves
    • Key features and capabilities
    • Screenshots or feature descriptions already documented
  2. CHANGELOG.md or HISTORY.md - Recent features worth highlighting
  3. docs/ directory - Any additional documentation about features
始终先阅读以下文件以了解应用功能:
  1. README.md(及子目录中的所有README文件)- 完整阅读README以了解:
    • 应用定位及解决的问题
    • 核心功能与能力
    • 已有的截图或功能描述
  2. CHANGELOG.mdHISTORY.md - 值得重点展示的近期功能
  3. docs/ 目录 - 任何关于功能的额外文档

3.2: Analyze Routes to Find Pages

3.2:分析路由以查找页面

Read the routing configuration to discover all available pages:
FrameworkFile to ReadWhat 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 RouterSearch for
createBrowserRouter
or
<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
ExpressSearch for
app.get
,
router.get
Route handlers
Important: Actually read these files, don't just check if they exist. The route definitions tell you what pages are available for screenshots.
阅读路由配置以发现所有可用页面:
框架需读取的文件查找内容
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: Identify Key Components

3.3:识别核心组件

Look for components that represent screenshottable features:
  • Dashboard components
  • Feature sections with distinct UI
  • Forms and interactive inputs
  • Data visualizations (charts, graphs, tables)
  • Modals and dialogs
  • Navigation and sidebars
  • Settings panels
  • User profile sections
寻找代表可截图功能的组件:
  • 仪表板组件
  • 具有独特UI的功能板块
  • 表单与交互式输入组件
  • 数据可视化(图表、图形、表格)
  • 模态框与对话框
  • 导航栏与侧边栏
  • 设置面板
  • 用户资料板块

3.4: Check for Marketing Assets

3.4:检查营销资产

Look for existing marketing content that hints at key features:
  • Landing page components (often in
    components/landing/
    or
    components/marketing/
    )
  • Feature list components
  • Pricing tables
  • Testimonial sections
寻找暗示核心功能的现有营销内容:
  • 着陆页组件(通常位于
    components/landing/
    components/marketing/
  • 功能列表组件
  • 定价表
  • 推荐语板块

3.5: Build Feature List

3.5:构建功能列表

Create a comprehensive list of discovered features with:
  • Feature name (from README or component name)
  • URL path (from routes)
  • CSS selector to focus on (from component structure)
  • Required UI state (logged in, data populated, modal open, specific tab selected)
创建已发现功能的完整列表,包含:
  • 功能名称(来自README或组件名称)
  • URL路径(来自路由)
  • 需聚焦的CSS选择器(来自组件结构)
  • 所需UI状态(已登录、数据已填充、模态框已打开、特定标签页已选中)

Step 4: Plan Screenshots with User

步骤4:与用户确认截图计划

Present the discovered features to the user and ask them to confirm or modify the list.
Use
AskUserQuestion
:
  • Header: "Features"
  • Question: "I found these features in your codebase. Which would you like to screenshot?"
  • Options: List 3-4 key features discovered, plus "Let me pick specific ones"
If user wants specific ones, ask follow-up questions to clarify exactly what to capture.
向用户展示已发现的功能,请求确认或修改列表。
使用
AskUserQuestion
  • 标题:"功能"
  • 问题:"我在你的代码库中发现了这些功能,你想对哪些进行截图?"
  • 选项:列出3-4个已发现的核心功能,加上"让我选择特定功能"
如果用户想要选择特定功能,进一步询问以明确需要捕获的内容。

Step 5: Create Screenshots Directory

步骤5:创建截图目录

bash
mkdir -p screenshots
bash
mkdir -p screenshots

Step 6: Generate and Run Playwright Script

步骤6:生成并运行Playwright脚本

Create a Node.js script that uses Playwright with proper HiDPI settings. The script should:
  1. Use
    deviceScaleFactor: 2
    for true retina resolution
  2. Set viewport to 1440x900 (produces 2880x1800 pixel images)
  3. Handle authentication if credentials were provided
  4. Navigate to each page and capture screenshots
创建一个Node.js脚本,使用Playwright并配置正确的HiDPI设置。该脚本应:
  1. **使用
    deviceScaleFactor: 2
    **以实现真实的retina分辨率
  2. 将视口设置为1440x900(生成2880x1800像素的图片)
  3. 处理认证(如果提供了凭据)
  4. 导航到每个页面并捕获截图

Script Template

脚本模板

Write this script to a temporary file (e.g.,
screenshot-script.mjs
) and execute it:
javascript
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);
将以下脚本写入临时文件(例如:
screenshot-script.mjs
)并执行:
javascript
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);

Running the Script

运行脚本

bash
node screenshot-script.mjs
After running, clean up the temporary script:
bash
rm screenshot-script.mjs
bash
node screenshot-script.mjs
运行完成后,清理临时脚本:
bash
rm screenshot-script.mjs

Step 7: Advanced Screenshot Options

步骤7:高级截图选项

Element-Focused Screenshots

聚焦元素的截图

To screenshot a specific element instead of the full viewport:
javascript
const element = await page.locator('[CSS_SELECTOR]');
await element.screenshot({ path: `${SCREENSHOTS_DIR}/element.png` });
如需仅截图特定元素而非整个视口:
javascript
const element = await page.locator('[CSS_SELECTOR]');
await element.screenshot({ path: `${SCREENSHOTS_DIR}/element.png` });

Full Page Screenshots

整页截图

For scrollable content, capture the entire page:
javascript
await page.screenshot({
  path: `${SCREENSHOTS_DIR}/full-page.png`,
  fullPage: true
});
对于可滚动内容,捕获整个页面:
javascript
await page.screenshot({
  path: `${SCREENSHOTS_DIR}/full-page.png`,
  fullPage: true
});

Waiting for Animations

等待动画完成

If the page has animations, wait for them to complete:
javascript
await page.waitForTimeout(500); // Wait 500ms for animations
如果页面包含动画,等待其完成后再截图:
javascript
await page.waitForTimeout(500); // 等待500毫秒以完成动画

Clicking Elements Before Screenshot

截图前操作元素

To capture a modal, dropdown, or hover state:
javascript
await page.click('button.open-modal');
await page.waitForSelector('.modal-content');
await page.screenshot({ path: `${SCREENSHOTS_DIR}/modal.png` });
如需捕获模态框、下拉菜单或悬停状态:
javascript
await page.click('button.open-modal');
await page.waitForSelector('.modal-content');
await page.screenshot({ path: `${SCREENSHOTS_DIR}/modal.png` });

Dark Mode Screenshots

深色模式截图

If the app supports dark mode:
javascript
// Set dark mode preference
const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 2,
  colorScheme: 'dark',
});
如果应用支持深色模式:
javascript
// 设置深色模式偏好
const context = await browser.newContext({
  viewport: { width: 1440, height: 900 },
  deviceScaleFactor: 2,
  colorScheme: 'dark',
});

Step 8: File Naming Convention

步骤8:文件命名规范

Use descriptive, kebab-case filenames with numeric prefixes for ordering:
FeatureFilename
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
使用描述性的短横线分隔式(kebab-case)文件名,并添加数字前缀以排序:
功能文件名
仪表板概览
01-dashboard-overview.png
链接管理
02-link-inbox.png
版本编辑器
03-edition-editor.png
数据分析
04-analytics.png
设置页面
05-settings.png

Step 9: Verify and Summarize

步骤9:验证与总结

After capturing all screenshots, verify the results:
bash
ls -la screenshots/*.png
sips -g pixelWidth -g pixelHeight screenshots/*.png 2>/dev/null || file screenshots/*.png
Provide a summary to the user:
  1. List all generated files with their paths
  2. Confirm the resolution (should be 2880x1800 for 2x retina at 1440x900 viewport)
  3. Mention total file sizes
  4. Suggest any follow-up actions
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.
捕获所有截图后,验证结果:
bash
ls -la screenshots/*.png
sips -g pixelWidth -g pixelHeight screenshots/*.png 2>/dev/null || file screenshots/*.png
向用户提供总结:
  1. 列出所有生成的文件及其路径
  2. 确认分辨率(1440x900视口下的2倍retina截图应为2880x1800)
  3. 提及文件总大小
  4. 建议后续操作
示例输出:
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.

Error Handling

错误处理

  • Playwright not found: Suggest
    npm install -D playwright
  • Page not loading: Check if the dev server is running, suggest starting it
  • Login failed: The smart locators try common patterns but may fail on unusual login forms. If login fails, analyze the login page HTML to find the correct selectors and customize the script.
  • Element not found: Verify the CSS selector, offer to take a full page screenshot instead
  • Screenshot failed: Check disk space, verify write permissions to screenshots directory
  • 未找到Playwright:建议执行
    npm install -D playwright
  • 页面无法加载:检查开发服务器是否运行,建议启动服务器
  • 登录失败:智能定位器会尝试常见模式,但可能在非标准登录表单上失败。如果登录失败,分析登录页面HTML以找到正确的选择器并自定义脚本。
  • 元素未找到:验证CSS选择器,建议改为捕获整页截图
  • 截图失败:检查磁盘空间,验证对screenshots目录的写入权限

Tips for Best Results

最佳实践提示

  1. Clean UI state: Use demo/seed data for realistic content
  2. Consistent sizing: Use the same viewport for all screenshots
  3. Wait for content: Use
    waitForLoadState('networkidle')
    to ensure all content loads
  4. Hide dev tools: Ensure no browser extensions or dev overlays are visible
  5. Dark mode variants: Consider capturing both light and dark mode if supported
  1. 干净的UI状态:使用演示/种子数据以呈现真实内容
  2. 一致的尺寸:所有截图使用相同的视口
  3. 等待内容加载:使用
    waitForLoadState('networkidle')
    确保所有内容加载完成
  4. 隐藏开发工具:确保没有浏览器扩展或开发覆盖层可见
  5. 深色模式变体:如果应用支持,考虑同时捕获浅色和深色模式截图