mcpField
U
ui-screenshots

ui-screenshots

github.com/github设计与 UI40.5k210 次安装今天更新
代码仓库

关于 ui-screenshots

ui-screenshots 是用于开发阶段捕获网页和图形界面的技能,可记录视觉变化。它涵盖整页及交互状态截图,并通过裁剪生成页面局部图像。

主要功能

  1. 01网页整页截图
  2. 02记录修改前后的界面
  3. 03捕获交互状态
  4. 04使用 PIL 裁剪图像
  5. 05网页、Electron 和桌面截图指南

适用场景

  1. 01记录代码修改前后的界面
  2. 02捕获工具提示或选中状态
  3. 03从整页图像裁剪页面区域

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/awesome-copilot --skill ui-screenshots

安装后在 Claude Code 中输入/ui-screenshots确认已加载。

详细介绍

UI 截图

在开发过程中捕获 Web 应用和图形界面的截图,以记录视觉变更。

何时使用此技能

当你需要执行以下操作时,请使用此技能:

  • 捕获运行中的 Web 应用的当前状态
  • 记录代码变更前后的 UI
  • 截取交互状态(工具提示、悬停、选中元素)
  • 捕获页面的特定部分而无需重新截图

前提条件

pip install playwright Pillow -q
playwright install chromium

核心工作流

1. 截取原始整页截图
from playwright.async_api import async_playwright

async def capture(url="http://localhost:3000", out="screenshot-raw.png", width=1400, height=5000):
    async with async_playwright() as p:
        browser = await p.chromium.launch()
        page = await browser.new_page(viewport={"width": width, "height": height})
        await page.goto(url, wait_until="networkidle")
        await page.wait_for_timeout(4000)  # let charts/animations render
        await page.screenshot(path=out, full_page=True)
        await browser.close()
  • 使用高视口(height=5000),让页面无需滚动即可渲染全部内容
  • wait_until="networkidle" + wait_for_timeout(4000) 确保异步图表加载完成
  • full_page=True 捕获整个可滚动内容
2. 查看原始图像,然后用 PIL 裁剪

不要试图通过 Playwright 的 clip 参数获得完美的裁剪结果。 它在整页截图时并不可靠。

from PIL import Image

img = Image.open("screenshot-raw.png")
cropped = img.crop((left, top, right, bottom))  # adjust based on what you see
cropped.save("screenshot-final.png")
  1. 截取原始截图
  2. 查看它以了解实际的像素位置
  3. 根据你看到的内容用 PIL 裁剪
  4. 查看结果——如果不正确,重新裁剪(即时完成,无需重新截图)
3. 在裁剪上迭代,而不是在捕获上迭代
  • 重新截图很慢(浏览器启动 + 页面加载 + 渲染等待)
  • 重新裁剪是即时的(只需 PIL)
  • 获取一张高质量的原始截图,然后按需以多种方式切片
4. 交互状态
element = page.locator("selector").first
await element.hover()
await page.wait_for_timeout(1000)  # let tooltip appear
await page.screenshot(path="screenshot-hover.png", full_page=True)

对于没有悬停效果的"选中"状态,在点击后将鼠标移开:

await element.click()
await page.mouse.move(300, 300)  # move away so hover doesn't show
await page.wait_for_timeout(500)
await page.screenshot(path="screenshot-selected.png", full_page=True)
5. 特定区域截图

从单张整页截图中裁剪不同区域:

img.crop((0, 200, 920, 900)).save("screenshot-header.png")
img.crop((0, 900, 920, 1600)).save("screenshot-main.png")

指南

  1. 务必在进行任何更改之前捕获变更前状态——如果忘记,你必须回退代码才能获得变更前的截图
  2. 变更前/变更后配对必须使用相同的视口宽度和裁剪——否则对比毫无意义
  3. 在你已经更改代码后获取"变更前"状态:使用 git checkout HEAD~1 -- <files> 回退,截图,然后使用 git checkout HEAD -- <files> 恢复
  4. 对于交互状态:为每个状态捕获变更前和变更后——不要假设"正常"状态的变更前截图涵盖了所有情况
  5. 在 Playwright 中使用 device_scale_factor=1 强制 1x 像素,使截图与用户在 100% 缩放时看到的一致
  6. 图表需要额外等待时间——Plotly、D3 等异步渲染;在 networkidle 后至少等待 4 秒
  7. 窄视口会暴露渲染缺陷——某些边框/对齐问题只在特定宽度下出现

非 Web 应用截图

适用于 Playwright 无法触及的桌面应用(VS、WPF、WinForms、控制台应用、终端)。

mss + ctypes(推荐用于桌面窗口)

通过 Win32 API 按标题查找窗口,用 mss 捕获其区域。经测试每次捕获约 33ms。

import ctypes
from ctypes import c_int, Structure, byref, windll
import mss
from PIL import Image

user32 = windll.user32

def find_window(title_contains):
    """Find visible windows matching a title substring."""
    results = []
    WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_void_p)
    def cb(hwnd, _):
        if user32.IsWindowVisible(hwnd):
            buf = ctypes.create_unicode_buffer(256)
            user32.GetWindowTextW(hwnd, buf, 256)
            if title_contains.lower() in buf.value.lower():
                results.append((hwnd, buf.value))
        return True
    user32.EnumWindows(WNDENUMPROC(cb), 0)
    return results

def capture_window(title_contains, output_path):
    """Capture a window by title substring."""
    windows = find_window(title_contains)
    if not windows:
        raise ValueError(f"No window matching '{title_contains}'")
    hwnd = windows[0][0]

    class RECT(Structure):
        _fields_ = [('left', c_int), ('top', c_int), ('right', c_int), ('bottom', c_int)]
    rect = RECT()
    user32.GetWindowRect(hwnd, byref(rect))
    w, h = rect.right - rect.left, rect.bottom - rect.top

    with mss.mss() as sct:
        shot = sct.grab({'left': rect.left, 'top': rect.top, 'width': w, 'height': h})
        img = Image.frombytes('RGB', shot.size, shot.rgb)
        img.save(output_path)
        return img

# Usage:
capture_window('Visual Studio Code', 'vscode-capture.png')

前提条件: pip install mss pillow 限制: 窗口必须可见(不能在其他窗口后面或最小化)。

Electron 应用(VS Code 等)

仅限 Node.js Playwright——Python Playwright 没有 electron API。通过 CDP(Chrome DevTools Protocol)捕获,而非从屏幕捕获——即使最小化也能工作。

const { _electron: electron } = require('playwright');
const app = await electron.launch({
    executablePath: 'C:\\Program Files\\Microsoft VS Code\\Code.exe',
    args: ['--new-window', '--disable-extensions', '--user-data-dir=' + tmpDir]
});
const window = await app.firstWindow();
await window.waitForLoadState('domcontentloaded');

// Minimize immediately — captures still work via CDP
await app.evaluate(({ BrowserWindow }) => {
    BrowserWindow.getAllWindows()[0].minimize();
});

await window.screenshot({ path: 'capture.png' }); // works while minimized!
await app.close();

关键:必须使用 --user-data-dir=<temp>,否则 VS Code 会交给现有实例处理,启动的进程会立即退出。

决策树
场景工具备注
Web 应用(localhost)Playwright经过验证,完整的 DOM 访问
Electron 应用(VS Code)Playwright Electron(Node.js)通过 CDP 可在最小化时工作
桌面应用,可见窗口mss + ctypes(按标题查找)每次捕获约 33ms
桌面应用,在其他窗口后面Windows Graphics Capture API设置复杂,需 Win10 1903+
快速全屏mss约 68ms

限制

  • Web 捕获需要本地运行的应用或可访问的 URL
  • 桌面捕获(mss)要求窗口可见且未被遮挡
  • Electron 捕获需要 Node.js Playwright(不是 Python)
  • 某些具有大量客户端渲染的 SPA 可能需要超出 networkidle 的自定义等待逻辑

评论

支持 Markdown