# Headless Visual Demo Skill
A reusable capture flow for screenshots and recordings of desktop or web applications. It is designed for remote coding agents to show UI changes when the human isn't watching the screen live.
https://github.com/user-attachments/assets/08d402df-34af-4823-98fb-a1dc96265d83
This skill delivers screenshots or recordings to a human for verification. It is not intended to replace browser automation, CDP, or app-specific inspection tools when the agent needs to reason about UI state programmatically.
## Features
- **Cross-platform**: Linux, macOS, and Windows
- **Multi-app support**: Web apps (Playwright), Tauri, Electron, native desktop apps
- **Proactive**: Suggests captures after UI file changes
- **Flexible**: Screenshots or video recordings
## Supported Configurations
| App Type | Linux | macOS | Windows |
|----------|-------|-------|---------|
| Web apps | Playwright | Playwright | Playwright |
| Tauri | Xvfb + ImageMagick | screencapture | PowerShell |
| Electron | Xvfb + ImageMagick | screencapture | PowerShell |
| Native | Xvfb + ImageMagick | screencapture | PowerShell |
## Quick Start
### 1. Clone this repository
```bash
git clone https://github.com/binhonglee/headless_visual_demo_skill.git /tmp/headless_visual_demo_skill
```
### 2. Copy and customize the capture script
```bash
mkdir -p scripts
cp /tmp/headless_visual_demo_skill/.claude/skills/headless-capture/templates/capture.mjs scripts/
```
Edit `scripts/capture.mjs` to set your app's binary path, window name, or dev server URL.
### 3. Optional: copy the Claude Code skill
If you use Claude Code, copy the skill so future agents know how and when to run captures:
```bash
mkdir -p .claude/skills
cp -R /tmp/headless_visual_demo_skill/.claude/skills/headless-capture .claude/skills/
```
Other agents can still use `scripts/capture.mjs` directly.
### 4. Add npm scripts
```json
{
"scripts": {
"capture": "node scripts/capture.mjs screenshot",
"capture:record": "node scripts/capture.mjs record",
"capture:web": "node scripts/capture.mjs screenshot --web",
"capture:web:record": "node scripts/capture.mjs record --web"
}
}
```
### 5. Install dependencies
**For web apps:**
```bash
npm install -D playwright
npx playwright install chromium
```
**For desktop apps on Linux:**
```bash
apt-get install -y xvfb imagemagick ffmpeg xdotool x11-utils
```
**For desktop apps on macOS:**
```bash
brew install ffmpeg cliclick
```
**For desktop apps on Windows:**
```bash
choco install ffmpeg
```
### 6. Run a capture
```bash
# Web app screenshot
npm run capture:web
# Desktop app screenshot
npm run capture
# Recording (8 seconds)
npm run capture:record -- --duration 8
```
## Usage
```
node scripts/capture.mjs <screenshot|record> [options]
Options:
--out PATH Output file path (default: artifacts/captures/<mode>-<timestamp>.<ext>)
--wait N Seconds to wait for app to appear (default: 6)
--settle N Seconds to wait after app appears (default: 3)
--duration N Recording duration in seconds (default: 8)
--web Force web app mode (Playwright)
--desktop Force desktop app mode
--url URL URL for web capture (default: http://localhost:5173)
--selector SEL CSS selector to wait for (web mode)
--viewport WxH Viewport size (default: 1280x800)
```
## Environment Variables
| Variable | Description | Default |
|----------|-------------|---------|
| `APP_TYPE` | `web`, `tauri`, `electron`, or `native` | Auto-detected |
| `APP_BINARY` | Path to desktop app binary | Project-specific |
| `APP_WINDOW_NAME` | Window title for detection | Project-specific |
| `WEB_URL` | Dev server URL | `http://localhost:5173` |
| `CAPTURE_DISPLAY` | X display (Linux) | `:99` |
| `CAPTURE_SIZE` | Screen/viewport size | `1280x800` |
| `CAPTURE_FRAMERATE` | Recording FPS | `25` |
| `CAPTURE_SKIP_BUILD` | Skip auto-build if `1` | `0` |
## Agent Integrations
The capture script is agent-agnostic. Hook support is agent-specific, so keep the automatic setup for each environment separate.
### Claude Code
Claude Code can use the bundled skill and SessionStart hook.
For automatic toolchain installation in Claude Code on the web sessions:
1. Copy the hook:
```bash
mkdir -p .claude/hooks
cp .claude/skills/headless-capture/templates/session-start.sh .claude/hooks/
chmod +x .claude/hooks/session-start.sh
```
2. Register in `.claude/settings.json`:
```json
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
}
]
}
]
}
}
```
The hook only runs when `CLAUDE_CODE_REMOTE=true` (Claude Code on the web), so local developers aren't affected.
### Cursor
Cursor supports hooks in `.cursor/hooks.json` and `~/.cursor/hooks.json`. Cursor Cloud Agents run command-based project hooks from `.cursor/hooks.json`, but they do not run local user hooks or some client/session lifecycle hooks such as `sessionStart`.
Use the capture script directly in Cursor workspaces:
```bash
node scripts/capture.mjs screenshot --web
node scripts/capture.mjs screenshot --desktop
```
For dependency bootstrapping in Cursor Cloud Agents, prefer a project setup script, devcontainer setup, or documented manual install. Do not copy the Claude hook into `.cursor/hooks.json` as-is; Cursor hooks communicate with JSON over stdin/stdout, while this template is a plain shell installer.
### Codex
Codex supports hooks through `hooks.json` or inline `config.toml` hook tables. If you adapt this flow for Codex, keep the same `scripts/capture.mjs` commands and wrap any startup installer in Codex's expected hook input/output protocol.
Do not copy the Claude hook as-is into Codex hooks; it prints human-readable logs, while Codex hooks expect structured hook behavior and may require hook review/trust before execution.
### Other Remote Agents
For agents without compatible hooks, install the dependencies manually or run an equivalent project setup script before capturing. The only required shared contract is that the agent can run:
```bash
node scripts/capture.mjs screenshot
node scripts/capture.mjs record
```
### Proactive Usage
The skill triggers proactively when:
- UI files are modified (`.tsx`, `.vue`, `.svelte`, `.jsx`, `.html`, `.css`)
- User asks to verify changes visually
- After implementing UI features or fixing visual bugs
## Common Issues
### Blank screenshots
- Increase `--settle` time to let the app finish rendering
- For Tauri, ensure WebKit env hints are set (handled automatically)
### Playwright can't connect
- Start the dev server first (`npm run dev`)
- Check the URL and port are correct
### Permission denied (macOS)
- Grant Screen Recording permission in System Preferences → Security & Privacy
### Display collision (Linux)
- Another Xvfb may be running on `:99`
- Set `CAPTURE_DISPLAY=:98`
## Project Structure
```
scripts/
└── capture.mjs # Unified capture script copied into your project
.claude/skills/headless-capture/
├── SKILL.md # Optional Claude Code skill documentation
└── templates/
├── capture.mjs # Source template for scripts/capture.mjs
└── session-start.sh # Claude Code Linux SessionStart hook template
```
## License
MIT License
Copyright (c) 2024
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.