{
  "schema_version": 1,
  "type": "skill",
  "slug": "adom-screenshot",
  "title": "adom screenshot",
  "brief": "Capture screenshots from inside the Adom container — Hydrogen panel/tab, AV scene, desktop, KiCad/Fusion, anywhere. Standardized output paths so the AI and the human are always looking at the same ima",
  "version": "1.0.0",
  "tags": [],
  "license": "MIT",
  "sample_prompts": [
    {
      "label": "Screenshot panel",
      "prompt": "Screenshot the Hydrogen panel named 'aci viewer'"
    },
    {
      "label": "AV capture",
      "prompt": "Capture the current AV scene to /tmp/scene.png"
    },
    {
      "label": "Desktop",
      "prompt": "Take a desktop screenshot"
    },
    {
      "label": "KiCad",
      "prompt": "Screenshot KiCad's 3D viewer"
    },
    {
      "label": "Tab",
      "prompt": "Screenshot the active Hydrogen tab"
    }
  ],
  "source_path": "SKILL.md",
  "readme": "---\nname: adom-screenshot\ndescription: How to capture screenshots -- Hydrogen panels/workspace/screen (primary), Adom Desktop windows (native apps), and legacy AV panel tools. Enables AI visual feedback loops. **EVERY screenshot MUST be logged to shotlog immediately after capture (see \"Always inject to shotlog\" below). This is non-negotiable for ralph loops, walkthroughs, debug sessions, and one-off verifications alike.** Trigger words -- screenshot, screencap, capture panel, hydrogen screenshot, browser screenshot, ralph loop, visual verification, debug loop, walkthrough proof, visual feedback.\n---\n\n# Screenshot & Visual Feedback\n\n## ⚡ Always inject to shotlog — non-negotiable\n\n**After EVERY screenshot, before moving on, run `shotlog inject -c <channel> -d \"<specific description>\" -s <source> <file>`.** No exceptions — not for \"quick tests,\" not for \"one-off verifications,\" not for ralph-loops that are mostly for chat preview. The shotlog is the durable artifact; ephemeral chat scrollback is not.\n\n**Why:**\n- The shotlog webview is how humans review visual history across a session. Screenshots not in shotlog are effectively lost the moment the chat scrolls.\n- Slugified descriptions in filenames become the searchable index for \"what did we see at step 3 of the walkthrough glow proof?\"\n- This rule is what distinguishes a repeatable visual record from a one-off preview.\n\n**How to apply:**\n- Every single `adom-cli hydrogen screenshot panel|workspace|screen` call is immediately followed by a `shotlog inject` call.\n- Every `browser_screenshot` (pup) call is immediately followed by a `shotlog inject` call.\n- Every `av_capture` / `av_tab_capture` call (legacy; avoid) is immediately followed by a `shotlog inject` call.\n- Pick a channel name tied to the task (`bme680-walkthrough-glows`, `rp2040-usbc-rotate`). Reuse across related shots in the same session.\n- Description is SPECIFIC (`step6-testpoints-halo-bloom`, not `shot6`). It becomes the slugified filename; future-you reads these to reconstruct what happened.\n- `-s <source>` = origin tag (`adom-cli`, `pup_screenshot`, `av_capture`, `manual`).\n- In a ralph loop, inject **every** frame of the loop, not just the final one.\n\n**Failure mode to avoid:** Running a 6-step ralph loop with 6 `hydrogen screenshot` calls and 0 `shotlog inject` calls. This has happened multiple times; each time the user had to call it out. Don't repeat.\n\n## DEFAULT: Use Hydrogen Screenshots\n\n**Your primary screenshot tool is `adom-cli hydrogen screenshot`.** It uses the browser's element-capture API for pixel-perfect screenshots of any hydrogen panel, the full workspace, or the entire screen. It captures everything: WebGL, canvas, CSS filters, nested iframes, video elements.\n\n**Priority order:**\n1. `hydrogen screenshot panel` -- single panel (fastest, most efficient)\n2. `hydrogen screenshot workspace` -- all panels side by side\n3. `hydrogen screenshot screen` -- entire display\n4. Adom Desktop `desktop_screenshot_window` -- native apps, background windows\n5. Adom Desktop `desktop_screenshot_screen` -- full desktop with all apps\n\n**`av_tab_capture` is DEPRECATED — do not use it.** It requires manual setup (camera button + sharing dialog) and is unreliable. Use `hydrogen screenshot` instead for all full-tab captures.\n\n`av_capture` is still valid for AV viewer panel canvas content specifically (3D renders, SVG symbols). It is not the default.\n\n## Quick Reference\n\n| Need to see... | Tool | Setup |\n|---|---|---|\n| A single hydrogen panel | `hydrogen screenshot panel --panel-id <id>` | Screen sharing (monitor icon) |\n| All hydrogen panels | `hydrogen screenshot workspace` | Screen sharing |\n| Full display | `hydrogen screenshot screen` | Screen sharing (\"Share entire screen\") |\n| KiCad, Fusion 360, native apps | `desktop_screenshot_window` via adom-desktop CLI | Adom Desktop app |\n| Background window | `desktop_screenshot_window` (captures by HWND) | Adom Desktop app |\n| Full desktop with all apps | `desktop_screenshot_screen` via adom-desktop CLI | Adom Desktop app |\n| AV viewer panel content | `av_capture` (legacy) | AV connected |\n\n---\n\n## Hydrogen Editor Screenshots\n\n### Screen Sharing Setup (one-time per session)\n\n**Required for hydrogen screenshots to work.**\n\n1. Click the **monitor icon** in the hydrogen nav bar (top right)\n2. A browser dialog appears:\n   - **\"Share this tab\"** -- enables `panel` + `workspace` scopes (recommended)\n   - **\"Share entire screen\"** -- enables all scopes including `screen`\n3. Persists for the session -- only needs to be done once\n\n**If 504 timeout:** Browser is connected but the sharing approval dialog wasn't addressed within ~90s (user missed it, dismissed it, or is AFK). Retry with a clearer `--reason` so the user can identify this session, or tell them to click the monitor icon in the nav bar to pre-approve.\n\n**If 409 \"No browser session is connected\":** Editor tab is closed or SSE is severed. Ask the user to surface the Hydrogen tab — E3 auto-reconnect re-registers within seconds on `visibilitychange`. Verify with `adom-cli hydrogen probe`, then retry.\n\n### Commands\n\n```bash\n# Get panel IDs\nadom-cli hydrogen workspace get\n\n# Single panel (fastest -- use this by default)\nadom-cli hydrogen screenshot panel --panel-id <leaf-id>\n\n# Specific tab (auto-discovers pane, switches to it)\nadom-cli hydrogen screenshot panel --tab-id <tab-id>\n\n# All panels side by side\nadom-cli hydrogen screenshot workspace\n\n# Entire display\nadom-cli hydrogen screenshot screen\n\n# Check sharing status\nadom-cli hydrogen screenshot status\n\n# Request sharing approval\nadom-cli hydrogen sharing request --share screen --audio --reason \"Need to capture demo\"\n```\n\nFiles saved automatically to `~/project/screenshots/`. Prints the file path on success. Each `leaf.id` from `workspace get` is a panelId. Each `leaf.tabs[i].id` is a tabId.\n\n---\n\n## Adom Desktop Screenshots (native apps, background windows)\n\nFor native desktop applications and windows that hydrogen can't see. Requires the Adom Desktop app running on the user's machine.\n\n```bash\n# Check desktop is connected\nadom-desktop ping\n\n# List all windows\nadom-desktop desktop_list_windows\n\n# Screenshot a specific window (even background windows)\nadom-desktop desktop_screenshot_window \\\n  '{\"hwnd\":<hwnd>,\"savePath\":\"project-content/screenshots/desktop-debug.png\"}'\n\n# Screenshot entire desktop\nadom-desktop desktop_screenshot_screen \\\n  '{\"savePath\":\"project-content/screenshots/desktop-full.png\"}'\n\n# Bring a window to foreground first\nadom-desktop browser_focus_window '{\"sessionId\":\"debug\"}'\n```\n\n### Key properties\n- `desktop_screenshot_window` captures by HWND -- works even if the window is behind other apps\n- `desktop_screenshot_screen` captures whatever is visible on screen\n- 15-second timeout per capture\n- Cross-application: KiCad, Fusion 360, Chrome, terminal, anything on the desktop\n\n### Typical workflow: verify KiCad delivery\n\n```text\n1. Send file to KiCad: kicad_open_board(...)\n2. List windows: desktop_list_windows → find KiCad's HWND\n3. Capture: desktop_screenshot_window({ hwnd: 12345, savePath: \"...\" })\n4. Analyze → verify the board loaded correctly\n```\n\n---\n\n## Visual Feedback Loop\n\nThis is the pattern that makes AI autonomous for visual work. **Steps 4 and 5 are a pair — never do 4 without 5.** A screenshot that isn't in shotlog isn't really a captured artifact; it's a chat-message flash that scrolls out of reach in minutes.\n\n```text\n1. Edit code\n2. Refresh the debug surface (webview refresh, pup reload, server restart)\n3. Interact -- send commands to exercise the change (rotate camera, click buttons, toggle states)\n4. Screenshot (choose the right method from the table above)\n5. shotlog inject with a SPECIFIC description   ← always, no exceptions\n6. Analyze the screenshot -- does it look right?\n7. If not right --> identify what's wrong --> fix --> go to step 1\n```\n\n**Always capture after visual changes, always inject into shotlog.** Don't commit UI changes without verifying them visually first. Don't run a ralph-loop that takes N screenshots and injects zero of them — that's a loop that leaves no trace.\n\n### Finding exact pixel positions of UI elements\n\nWhen you need to annotate screenshots or overlay mockups on real UI, DON'T guess pixel positions. Two techniques:\n\n**1. Query the DOM for exact coordinates:**\n```js\n// Use hydrogen sandbox eval or browser_eval to get element position\ndocument.querySelector('.screen-share-button').getBoundingClientRect()\n// -> {x: 1342, y: 58, width: 32, height: 32}\n```\nExact pixel coordinates, no guessing. Works for any HTML element.\n\n**2. Inject highlight borders then screenshot:**\n```bash\n# Screenshot 1: base (no highlights)\nadom-cli hydrogen screenshot screen -o /tmp/base.png\n\n# Inject CSS borders on target elements\nadom-desktop browser_eval '{\"sessionId\":\"...\",\"expr\":\"document.querySelector(\\\".target\\\").style.border=\\\"3px solid red\\\"\"}'\n\n# Screenshot 2: with highlights\nadom-cli hydrogen screenshot screen -o /tmp/highlighted.png\n\n# Compare the two to see exactly where elements are\n```\n\n**Never guess pixel positions and iterate manually.** Query the DOM first. If you can't query (e.g., browser chrome elements), use the inject+compare technique.\n\n### When to use which method\n\n- Content in a webview panel --> `hydrogen screenshot panel`\n- Multiple panels, layout verification --> `hydrogen screenshot workspace`\n- Full workspace with nav bar --> `hydrogen screenshot screen`\n- Pup browser session --> `browser_screenshot` via adom-desktop CLI\n- KiCad, Fusion 360, native apps --> `desktop_screenshot_window` via adom-desktop CLI\n- Background window --> `desktop_screenshot_window` (captures by HWND)\n- Full desktop --> `desktop_screenshot_screen` via adom-desktop CLI\n\n---\n\n## AV Panel: avShot (`av_capture`)\n\n**Scope**: AV viewer panel canvas content only (the rendered output inside iframes). Use only for content displayed in the Adom Viewer panel specifically.\n\n```text\nav_capture()  -->  PNG of whatever the AV panel is showing\n```\n\n- **MCP tool**: `av_capture` -- no parameters\n- **Saves to**: `project-content/screenshots/av/mgmt-screenshot-{timestamp}.png`\n- **Setup**: None -- works automatically when a viewer is connected\n- **Timeout**: 10 seconds\n\n### How it works\n\n```text\nMCP tool --> mgmt relay (port 8772) --> WebSocket broadcast to browser\n--> browser runs captureOVScreenshot() with fallback strategies:\n  1.  postMessage to 3D/canvas iframe --> iframe renders canvas --> PNG\n  2.  Direct SVG serialization (for SVG content)\n  3.  Direct <img> capture (for image elements)\n--> sends PNG back via WebSocket --> saved to disk --> returned to MCP\n```\n\nFor 3D/canvas iframes, **cooperative iframe capture** is used: the parent asks the iframe to export its canvas via `postMessage`, and the iframe serializes its own content.\n\n### What it CAN capture\n- 3D model renders (Babylon.js canvas via `canvas.toDataURL()`)\n- Symbol/footprint SVGs (serialized to XML, re-rendered at 8x scale)\n- Image content displayed in the viewer\n- Parent-rendered DOM content\n\n### What it CANNOT capture\n- HTML overlays (toolbar buttons, tooltips) -- use hydrogen screenshot instead\n- Nested iframes (LibView 3-pane layout) -- use hydrogen screenshot instead\n- `<canvas>` inside widgets without a `mgmt_capture_request` handler\n\n---\n\n## AV Panel: tabShot (`av_tab_capture`)\n\n**Scope**: Full browser tab -- everything visible in the tab including AV panel, overlays, nested iframes.\n\n```text\nav_tab_capture()  -->  PNG of the entire browser tab\n```\n\n- **MCP tool**: `av_tab_capture` -- no parameters\n- **Saves to**: `project-content/screenshots/av/tab-capture-{timestamp}.png`\n- **Timeout**: 10 seconds\n\n### Setup (one-time per session)\n\n1. Click the **camera button** in AV (or press Alt+S)\n2. Click **\"Share This Tab\"** in the popup\n3. Select the IDE tab in the browser's sharing dialog\n\n### Key properties\n- Captures everything in the tab -- toolbar icons, button states, multi-pane layouts, nested iframes\n- NOT affected by foreground window changes\n- Near-zero CPU cost when idle\n- Survives AV restarts\n\n---\n\n## Adding Capture Support to AV Widgets\n\n### Canvas-based iframe viewers (3D, WebGL, charts)\n\nIf your AV viewer renders to `<canvas>`, implement the `mgmt_capture_request` protocol:\n\n```javascript\nwindow.addEventListener('message', (e) => {\n  const msg = typeof e.data === 'string' ? JSON.parse(e.data) : e.data;\n  if (msg.type === 'mgmt_capture_request') {\n    scene.render(); // force render if needed\n    const dataUrl = canvas.toDataURL('image/png');\n    window.parent.postMessage({\n      type: 'mgmt_canvas_capture',\n      _reqId: msg._reqId,\n      data: dataUrl\n    }, '*');\n  }\n});\n```\n\nFor SVG content, serialize to XML:\n```javascript\nconst xml = new XMLSerializer().serializeToString(svgClone);\nwindow.parent.postMessage({\n  type: 'mgmt_canvas_capture',\n  _reqId: msg._reqId,\n  svgXml: xml, svgWidth: width, svgHeight: height, bgColor: '#0d1117'\n}, '*');\n```\n\n**Note:** Do not use html2canvas for capture wiring. It is deprecated -- it doesn't work with WebGL, 3D content, or cross-iframe scenarios. Use the cooperative `mgmt_capture_request` protocol instead.\n\n---\n\n## Image Resizing for Claude\n\nAll screenshot methods auto-resize images to **<=1568px on the longest edge** before returning them. Larger images provide zero quality benefit.\n\n| Source | Where resize happens |\n|--------|---------------------|\n| Hydrogen screenshots | Server-side in the hydrogen API |\n| Pup browser_screenshot | adom-desktop CLI, defaults to `maxWidth: 1568` |\n| Adom Desktop screenshots | MCP server via `sharp` |\n| AV avShot/tabShot | MCP server via `sharp` |\n\n- Full-resolution images are still saved to disk when `savePath` is specified\n- Resize preserves aspect ratio using Lanczos resampling\n- If resize fails, the original image is returned as fallback\n\nFor manual resizing:\n```bash\nshotlog resize large-image.png              # Resize in-place to max 1400px\nshotlog resize large-image.png -o small.png # Resize to new file\n```\n\n---\n\n## When the user pastes a screenshot directly into Claude chat\n\nYou can SEE pasted images via the multimodal channel, but you CANNOT extract\nthem to a file path on disk. There is no `/tmp/...` or other location where\nthe bytes land. So if you need that exact image as a file -- to upload to a\nGitHub issue, attach to a wiki page, commit to a repo, share in Slack -- you\nhave to bridge it through `shotlog`.\n\n**HARD RULE: never substitute a fresh `adom-cli hydrogen screenshot ...`\ncapture for the user's pasted image.** It loses the essence of what they\nshowed you (the specific moment, state, scroll position, error popup, tab\noverflow, mouse cursor, highlight, etc.) and the user will (rightly) push\nback. A \"similar\" screenshot is not the screenshot.\n\n### The bridge: shotlog paste flow\n\nWhen you need the user's pasted image as a real file:\n\n1. Make sure `shotlog serve` is running (`shotlog health`)\n2. Open the shotlog webview on a **non-VS-Code** pane (see\n   `adom-workspace-control` -- never park new tabs on the VS Code leaf):\n   ```bash\n   shotlog open --channel <task-channel>\n   ```\n3. **Tell the user explicitly:** \"Paste your screenshot (Ctrl+V) into the\n   Shotlog tab I just opened.\" Don't assume they know -- name the tab,\n   name the keystroke.\n4. Wait for paste. The shotlog webview auto-injects with a clipboard\n   source, resizes to <=1400px, and writes a file under\n   `~/project/screenshots/shotlog/<channel>/`.\n5. Find the new file -- newest entry in the channel folder, or check\n   `shotlog` server logs.\n6. Use that file path however you need (gh issue asset upload, wiki\n   submission, embed, etc.).\n\n### Why not just ask the user to save the file themselves?\n\nYou can, but it's worse UX. They'd need to find the clipboard image, save\nit somewhere, tell you the path. Shotlog is one Ctrl+V in a tab you've\nalready opened for them, and the file lands in a known location with a\nknown channel.\n\n### When the workspace API is in 409 \"Editor is not open\" state\n\nSometimes `adom-cli hydrogen workspace get` returns 409. You can still\ncall `shotlog open` -- it has its own panel-targeting logic -- but you\nmust verify afterward (`adom-cli hydrogen workspace tabs` once the editor\nsession reconnects, or visually with the user) that the Shotlog tab did\nnot land on the VS Code pane. If it did, move it.\n\n---\n\n## Key Files\n\n| File | Role |\n|------|------|\n| `~/gallia/viewer/mcp/server.js` | MCP tool definitions (`av_capture`, `av_tab_capture`) + image resize |\n| `~/gallia/viewer/mgmt-server.js` | Management relay (port 8772) -- routes avShot requests |\n| `~/gallia/viewer/server.js` | Main viewer server -- handles tabShot API |\n| `~/gallia/viewer/viewer/index.html` | Browser-side avShot orchestration |\n| `~/gallia/viewer/viewer/capture.html` | Screen Capture API companion tab for tabShot |\n| `~/gallia/server/mcp/server.js` | Conduit MCP tools (`desktop_screenshot_*`) + image resize |\n",
  "author": {
    "id": "695820315b5f1e4db2fcf602",
    "name": "Kyle Bergstedt",
    "email": "[email protected]"
  },
  "visibility": {
    "public": true
  },
  "hero": null,
  "discovery_triggers": [],
  "discovery_pitch": null,
  "metadata": {},
  "created_at": "2026-05-28T05:29:41.018Z",
  "updated_at": "2026-05-28T05:29:41.018Z",
  "sub_skills": [],
  "parent_app": null
}