---
name: web-screenshot
description: |
  Web-page screenshot skill (still images only, png/jpg), built on Playwright Python — captures any URL to a local image file.
  Supports: full-page / viewport / element / region capture, device emulation, waiting for dynamic content, hiding or masking distracting elements, and static annotations (highlight box / arrow / caption / numbered sequence / redact / code-line highlight).

  Images only (`.png` / `.jpg`). For video (`.webm` / screen recording / storyboard clips) use web-record / `web_record`.

  Use this skill immediately whenever the user asks for any of:
  - Web page screenshot, screen capture, full-page screenshot, long screenshot
  - Capture a specific element / region, partial screenshot, focus on an area
  - Hide or mask elements before capturing (cookie banner, sensitive information)
  - Annotate a still screenshot (highlight box / arrow / label / caption bar / numbering)
  - Highlight lines L5-L20 of a file and screenshot it
  - Wait for an element to appear, then capture
  - Mobile / device-emulated screenshot (iPhone / Pixel, etc.)
  - Capture with cookies / a logged-in session / storageState

  Even when the user does not say "playwright", any request to screenshot a URL and get a local image file should route here.
triggers:
  - Web page screenshot, screen capture, full-page screenshot, long screenshot
  - Capture a specific element / region, partial screenshot, focus on an area
  - Hide or mask elements before capturing, annotate a still screenshot (highlight box / arrow / label)
  - Highlight lines L5-L20 of a file and screenshot it
  - Mobile / device-emulated screenshot, capture with cookies / a logged-in session
---

# Web Screenshot Skill (`web_screenshot`)

Entry script **`scripts/screenshot.py`**, pure Python, outputs **`.png` / `.jpg`**. Full page / viewport / element / region.

> **Still images only.** Screen recording and storyboard video (`.webm`) go through **web-record / `web_record`** — passing `--output xxx.webm` to this skill is rejected outright with a pointer to `web_record`. The recording entry script `scripts/record.py` lives in this same directory (both share the `_media_screenshot/` package below), but it belongs to the web-record tool; see **`skills/web-record/SKILL.md`** for its usage.

Both share the **`scripts/_media_screenshot/`** package underneath, which wraps the Playwright Python API plus every browser-side JS injection (overlay / zoom / scroll, etc.).

> Path convention: read the system-injected `Base directory for this skill: <path>` as `<SkillDir>` and substitute it for `<SkillDir>` in the commands below. Never hardcode an absolute path.

## Prerequisites

- **Python 3.9+**
- **The `playwright` pip package + the chromium engine**: the first run **bootstraps automatically** — when the script detects the package is missing it runs `pip install playwright` + `playwright install chromium`.
  - You can also install it up front:
    ```bash
    pip install playwright
    playwright install chromium
    ```

## Capturing with `screenshot.py`

### Minimal usage

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "example.png"
```

### Full-page screenshot

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "example_full.png" \
  --full-page
```

### Element capture (focus on one part)

Capture **the element itself** by CSS selector (automatically `scrollIntoViewIfNeeded`):

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "card.png" \
  --selector ".card.featured" \
  --wait-for-selector ".card.featured"
```

To suppress the automatic scroll, add `--no-scroll-into-view`.

### Region capture (pixel clip rectangle)

```bash
# Full-page pixel coordinates
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "region.png" \
  --clip "100,200,600,400"     # x, y, w, h

# Clip relative to an element (scrolls the element into view first, then clips from its boundingBox origin plus the offset)
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "region_in_card.png" \
  --selector ".card" \
  --clip "0,0,300,180"
```

### Mobile / device emulation

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://m.example.com" \
  --output "mobile.png" \
  --device "iPhone 15 Pro" \
  --full-page
```

Custom viewport:

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "wide.png" \
  --viewport "1440,900"
```

### Waiting for dynamic content

```bash
# Wait for a selector to appear
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "ready.png" \
  --wait-for-selector "main .loaded" \
  --timeout 30000

# Fixed delay (good for pure front-end animation)
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "delayed.png" \
  --wait-for-timeout 2000
```

### Hiding / masking elements

```bash
# Hide cookie banners, login overlays and other distractions (repeatable)
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://example.com" \
  --output "clean.png" \
  --hide-selector ".cookie-banner" \
  --hide-selector "#login-modal"

# Mask sensitive information (Playwright's native mask, pink by default, color customizable)
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://app.example.com/dashboard" \
  --output "redacted.png" \
  --mask-selector ".user-email" \
  --mask-selector ".api-token" \
  --mask-color "#222"
```

### Annotating a still screenshot (highlight box / arrow / caption / numbering / redact)

`--annotate` takes a JSON document (file path or inline string) describing the annotations to draw, using the storyboard scene types. Every scene is drawn once, before the capture.

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://github.com/user/repo" \
  --output "annotated.png" \
  --annotate annotate.json
```

Example `annotate.json`:

```json
{
  "annotations": [
    { "type": "highlight", "selector": "strong[itemprop=\"name\"] a",
      "color": "#ff3b30", "label": "Look here" },
    { "type": "arrow",
      "from": { "selector": "#repo-stars-counter-star" },
      "to":   { "selector": "strong[itemprop=\"name\"] a", "side": "right" },
      "color": "#ff9500", "label": "⭐" },
    { "type": "caption", "text": "Project home page", "position": "bottom" },
    { "type": "redact", "selectors": [".user-email"], "mode": "blur" }
  ],
  "settleMs": 900
}
```

> The top level may also be a bare array: `[{"type": "highlight", ...}]`. `settleMs` is how long to let animations settle after drawing before capturing; it defaults to 900ms.

### Capturing with a logged-in session

Two options, pick one:

**A. Pass storageState directly (recommended)**

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://app.example.com/dashboard" \
  --output "dashboard.png" \
  --storage-state "./auth.json"
```

> `auth.json` can be exported after logging in via `npx playwright codegen --save-storage=auth.json <url>`.

**B. Pass a cookies JSON only**

```bash
python3 <SkillDir>/scripts/screenshot.py \
  --url "https://app.example.com/dashboard" \
  --output "dashboard.png" \
  --cookies "./cookies.json"
```

The cookies top level must be an array, each entry following the Playwright cookie format:

```json
[
  {"name": "session", "value": "abc", "domain": ".example.com", "path": "/", "httpOnly": true, "secure": true}
]
```

### `screenshot.py` flags

| Flag | Description | Default |
|------|-------------|---------|
| `-u` / `--url` | Target URL (required) | — |
| `-o` / `--output` | Local output path | `screenshot.png` |
| `-b` / `--browser` | `chromium` / `firefox` / `webkit` | `chromium` |
| `--device` | Device-emulation name | none |
| `--viewport` | `"width,height"` | none |
| `--full-page` | Capture the whole scrollable page | off |
| `--selector` | Capture the element itself | none |
| `--clip` | `"x,y,w,h"` region capture; combined with `--selector` it offsets from the element's boundingBox | none |
| `--no-scroll-into-view` | In selector mode, disable the automatic scroll | off |
| `--wait-for-selector` | Wait for this selector before acting | none |
| `--wait-for-timeout` | Fixed wait before acting (ms) | none |
| `--color-scheme` | `light` / `dark` / `no-preference` | none |
| `--user-agent` | Override the User-Agent | none |
| `--timeout` | Playwright global timeout (ms) | none |
| `--ignore-https-errors` | Ignore certificate errors | off |
| `--storage-state` | storageState JSON file path | none |
| `--cookies` | cookies JSON string or file | none |
| `--hide-selector` | `display:none` this selector before capturing (repeatable) | none |
| `--mask-selector` | Mask this selector with Playwright's native mask (repeatable) | none |
| `--mask-color` | Mask fill color | Playwright pink |
| `--annotate` | Annotation JSON file or string (array or `{annotations, settleMs}`) | none |

## Error handling

- **The first run is slow**: the script auto-runs `pip install playwright` + `playwright install chromium`. Wait it out once.
- **`Cannot find module 'playwright'` / `ModuleNotFoundError: playwright`**: the bootstrap did not complete; run `pip install playwright && playwright install chromium` manually.
- **`Executable doesn't exist`**: the browser engine is missing; run `playwright install chromium` (the script usually triggers this itself).
- **`Timeout ... exceeded`**: raise `--timeout` / `--wait-for-timeout`, or switch to a more reliable `--wait-for-selector`.
- **Blank capture / page not finished rendering**: add `--wait-for-selector` on a key element; for pure animation use `--wait-for-timeout`.
- **`selector "X" has no bounding box`**: the element exists but is invisible or zero-sized; wait for visibility with `--wait-for-selector` first.
- **Session expired**: re-export `storageState` or update the cookies JSON — `domain` / `path` must match.
- **HTTPS certificate errors**: in test environments add `--ignore-https-errors`.
- **"The screenshot entry only emits still images"**: `--output` used a video extension such as `.webm` / `.mp4`. Switch to the `web_record` tool, or change the extension to `.png` / `.jpg`.
