Adom Desktop - Puppeteer Bridge
Public Made by Adomby adom
The Adom Desktop Puppeteer (pup) bridge — drive a real headful browser on the user's desktop from the cloud: open/screenshot/eval/record. Cold-start drives the browser ALREADY on the machine (installed Chrome, else Microsoft Edge — every Windows PC ships with Edge) with a fresh isolated profile, so the common case needs NO download; Chrome for Testing is fetched only as a last resort. Two artifacts: the Releases zip is the bridge runtime (also bundled in Adom Desktop); the pkg installs the container-side Claude skills.
name: pup-screenshots-recording user-invocable: true description: "Capturing pup (Puppeteer bridge) windows: SCREENSHOTS (browser_screenshot — lossless PNG, full-page vs viewport, full-resolution via browser_screenshot_full_res, and how to actually READ the image) and RECORDING (browser_record_start/stop records ONE pup tab/window's page content to a video; desktop_record_start/stop records the WHOLE desktop across apps). Covers framing a window at an exact size for a clean shot WITHOUT foregrounding it, and why pup shots are true-to-life (real rendered window, not headless). Read when screenshotting a page, capturing full-page vs above-the-fold, recording a demo/walkthrough of an app, or choosing the window-recorder vs the desktop-recorder. Trigger words: browser_screenshot, browser_screenshot_full_res, full page screenshot, screenshot the page, read the screenshot, browser_record_start, browser_record_stop, desktop_record_start, desktop_record_stop, record my app, record a demo, screen recording, walkthrough video, pup screenshot, pup recording."
Parent skill: adom-desktop-puppeteer-bridge · See also: pup (start here), pup-windows-sessions-tabs, pup-browsers-and-chrome.
pup — screenshots & recording
pup windows are real, GPU-rendered windows (not headless), so screenshots and recordings are
true-to-life — fonts, canvas, WebGL, video, and CSS all render exactly as a user would see them. And it
all works while the window is in the background (see pup-windows-sessions-tabs), so you capture
without disturbing the user.
Screenshots
adom-desktop browser_screenshot '{"sessionId":"mytask-web"}' # viewport, lossless PNG
adom-desktop browser_screenshot '{"sessionId":"mytask-web","fullPage":true}' # entire scrollable page
adom-desktop browser_screenshot '{"sessionId":"mytask-web","tabId":"tab-2"}' # a specific tab
- Returns a PNG saved on the container (the machine running the CLI) — the response gives you the path.
Readthat path to actually see it; a returned path is not the same as having looked. Downscaled to stay under the image-read limit by default. fullPage:truestitches the whole scrollable page; omit for just the viewport (above the fold).browser_screenshot_full_rescaptures at full native resolution (no downscale) when you need to read fine detail — larger file; use when the default shrink loses text you need.- Framing for a clean shot without showing the user: size/position the window on open —
browser_open_window {width,height,x,y}— which places it without foregrounding (background stays background). Great for a deterministic, repeatable capture frame. To capture the OS window plus any owned popups/dialogs on top, use AD'sdesktop_screenshot_windowinstead (it returns the window with its child popups). - Sanity-check, don't assume: a page that 404'd or hit a login wall still screenshots — pair with
browser_evalondocument.titlewhen in doubt. - Rendering your OWN HTML to screenshot it? Use
browser_render_html {sessionId, html}— it renders the string viapage.setContent, which is UTF-8-correct. (Adata:text/html,…URL mojibakes accented characters —Café→Café— unless you hand-add;charset=utf-8;render_htmljust does it right.) Then screenshot/drive it like any page. Great for previewing generated markup without writing a temp file.
Recording — two recorders, pick by what you're capturing
| Recorder | Captures | Use for |
|---|---|---|
browser_record_start / browser_record_stop |
ONE pup tab/window — the page content | "record my app", a feature walkthrough, a focused UI flow |
desktop_record_start / desktop_record_stop |
the WHOLE desktop (multiple apps, KiCad/Fusion, OS dialogs) | anything spanning more than the pup window |
# Recording a pup window the user will watch → DO bring it forward first
adom-desktop browser_raise_os_window '{"sessionId":"demo-web"}'
adom-desktop browser_record_start '{"sessionId":"demo-web"}'
# ...drive the scene: navigate, click, type, scroll...
adom-desktop browser_record_stop '{"sessionId":"demo-web"}' # → { path: "...webm" }
adom-desktop browser_lower_os_window '{"sessionId":"demo-web"}' # tuck it away again when done
browser_record_stopreturns the saved video path. Recording status/list verbs:browser_record_status/browser_record_list(window recorder) anddesktop_record_status/desktop_record_list/desktop_recorder_open/desktop_recorder_close(desktop recorder).- Closing a tab/window mid-recording auto-stops that recording.
- For a narrated walkthrough, drive the scene here and hand the video to the
video-post/adom-ttsflow for voiceover — pup just produces the raw capture. - A pup window can record from the background too, but for a demo the user will actually watch, raise it
first (that's an explicit "show me" case where
foregroundis warranted).
Driving & verifying tricky pages (shadow DOM, nested scroll) — use the first-class verbs
Component-heavy pages (an app, a wiki widget) break the naive path: document.querySelector misses shadow
content, window.scrollTo no-ops when the page scrolls inside a nested <div>, fullPage returns only the
viewport, and a sized window can even come up ~170px wide. As of v1.8.21 pup has first-class verbs for
all of it — reach for these instead of hand-rolling evals:
browser_screenshot_element { sessionId, selector? | text?, closest?, padding?, deep? }— screenshot ONE element. Resolve by CSS selector (most reliable) or visible text (pierces shadow DOM), scrolled into view first. This is usually the whole answer to "capture just this card." Text matches the tightest text node — to grab the enclosing card addclosest:".card"; and if the phrase is split across spans or appears in prose too, use a selector (e.g..download-card).browser_scroll { sessionId, dy?/dx? | toSelector?/toText?, block?, behavior? }— scrolls the ACTUAL scrolling element (nearest scrollable ancestor, notwindow). Scroll by delta or to an element/text (shadow-piercing). Returns{ scrollElement, scrollTop, scrollHeight, clientHeight, atBottom }so you can confirm it landed — no more overshoot from hand-computed offsets.browser_set_viewport { sessionId, width, height, deviceScaleFactor? }— pins the viewport deterministically (fixes a too-small innerWidth).browser_open_window { width, height }now applies it on open, so a sized window renders at the size you asked for.$deep(sel)/$$deep(sel)/deepText(txt)are auto-injected into every page, sobrowser_evalcan pierce shadow roots directly — no hand-rolled walk:
adom-desktop browser_eval '{"sessionId":"...","expr":"$$deep(\"a[href]\").length"}' # counts across shadow roots
Still verify by DOM, then by eye. To count cards/links/rows, count in browser_eval by document-Y
(rect.top + window.scrollY) — immune to an overshot screenshot — then browser_screenshot_element (or
browser_scroll + browser_screenshot) to confirm. And first check whether a page is even shadow DOM
before assuming: a shadow survey ($$deep finding more than querySelectorAll) tells you; often an "empty"
result just means a wrong selector on plain DOM.
fullPage caveat: browser_screenshot { fullPage:true } only covers document-level scroll. On a
nested-scroll SPA it returns just the viewport and a nestedScroll:true hint — use browser_screenshot_element
for one region, or browser_scroll through the container.
Reminder: browser_eval allows no semicolons — use the comma operator or an IIFE. If a
screenshot lands on the wrong content, don't trust it — it almost always means an inner scroll container;
re-scroll the element, don't nudge the window.
---
name: pup-screenshots-recording
user-invocable: true
description: "Capturing pup (Puppeteer bridge) windows: SCREENSHOTS (browser_screenshot — lossless PNG, full-page vs viewport, full-resolution via browser_screenshot_full_res, and how to actually READ the image) and RECORDING (browser_record_start/stop records ONE pup tab/window's page content to a video; desktop_record_start/stop records the WHOLE desktop across apps). Covers framing a window at an exact size for a clean shot WITHOUT foregrounding it, and why pup shots are true-to-life (real rendered window, not headless). Read when screenshotting a page, capturing full-page vs above-the-fold, recording a demo/walkthrough of an app, or choosing the window-recorder vs the desktop-recorder. Trigger words: browser_screenshot, browser_screenshot_full_res, full page screenshot, screenshot the page, read the screenshot, browser_record_start, browser_record_stop, desktop_record_start, desktop_record_stop, record my app, record a demo, screen recording, walkthrough video, pup screenshot, pup recording."
---
**Parent skill:** adom-desktop-puppeteer-bridge · See also: `pup` (start here), `pup-windows-sessions-tabs`, `pup-browsers-and-chrome`.
# pup — screenshots & recording
pup windows are **real, GPU-rendered windows** (not headless), so screenshots and recordings are
true-to-life — fonts, canvas, WebGL, video, and CSS all render exactly as a user would see them. And it
all works while the window is in the **background** (see `pup-windows-sessions-tabs`), so you capture
without disturbing the user.
## Screenshots
```bash
adom-desktop browser_screenshot '{"sessionId":"mytask-web"}' # viewport, lossless PNG
adom-desktop browser_screenshot '{"sessionId":"mytask-web","fullPage":true}' # entire scrollable page
adom-desktop browser_screenshot '{"sessionId":"mytask-web","tabId":"tab-2"}' # a specific tab
```
- Returns a **PNG saved on the container** (the machine running the CLI) — the response gives you the path.
**`Read` that path** to actually see it; a returned path is not the same as having looked. Downscaled to
stay under the image-read limit by default.
- **`fullPage:true`** stitches the whole scrollable page; omit for just the viewport (above the fold).
- **`browser_screenshot_full_res`** captures at full native resolution (no downscale) when you need to read
fine detail — larger file; use when the default shrink loses text you need.
- **Framing for a clean shot without showing the user:** size/position the window on open —
`browser_open_window {width,height,x,y}` — which places it **without foregrounding** (background stays
background). Great for a deterministic, repeatable capture frame. To capture the OS window *plus* any
owned popups/dialogs on top, use AD's `desktop_screenshot_window` instead (it returns the window with its
child popups).
- **Sanity-check, don't assume:** a page that 404'd or hit a login wall still screenshots — pair with
`browser_eval` on `document.title` when in doubt.
- **Rendering your OWN HTML to screenshot it?** Use **`browser_render_html {sessionId, html}`** — it renders
the string via `page.setContent`, which is **UTF-8-correct**. (A `data:text/html,…` URL mojibakes accented
characters — `Café` → `Café` — unless you hand-add `;charset=utf-8`; `render_html` just does it right.)
Then screenshot/drive it like any page. Great for previewing generated markup without writing a temp file.
## Recording — two recorders, pick by what you're capturing
| Recorder | Captures | Use for |
|---|---|---|
| **`browser_record_start` / `browser_record_stop`** | ONE pup tab/window — the page content | "record my app", a feature walkthrough, a focused UI flow |
| **`desktop_record_start` / `desktop_record_stop`** | the WHOLE desktop (multiple apps, KiCad/Fusion, OS dialogs) | anything spanning more than the pup window |
```bash
# Recording a pup window the user will watch → DO bring it forward first
adom-desktop browser_raise_os_window '{"sessionId":"demo-web"}'
adom-desktop browser_record_start '{"sessionId":"demo-web"}'
# ...drive the scene: navigate, click, type, scroll...
adom-desktop browser_record_stop '{"sessionId":"demo-web"}' # → { path: "...webm" }
adom-desktop browser_lower_os_window '{"sessionId":"demo-web"}' # tuck it away again when done
```
- `browser_record_stop` returns the saved video **path**. Recording status/list verbs:
`browser_record_status` / `browser_record_list` (window recorder) and `desktop_record_status` /
`desktop_record_list` / `desktop_recorder_open` / `desktop_recorder_close` (desktop recorder).
- Closing a tab/window mid-recording auto-stops that recording.
- For a **narrated** walkthrough, drive the scene here and hand the video to the `video-post` / `adom-tts`
flow for voiceover — pup just produces the raw capture.
- A pup window can record from the **background** too, but for a demo the user will actually watch, raise it
first (that's an explicit "show me" case where `foreground` is warranted).
## Driving & verifying tricky pages (shadow DOM, nested scroll) — use the first-class verbs
Component-heavy pages (an app, a wiki widget) break the naive path: `document.querySelector` misses shadow
content, `window.scrollTo` no-ops when the page scrolls inside a nested `<div>`, `fullPage` returns only the
viewport, and a sized window can even come up ~170px wide. As of **v1.8.21 pup has first-class verbs for
all of it** — reach for these instead of hand-rolling evals:
- **`browser_screenshot_element { sessionId, selector? | text?, closest?, padding?, deep? }`** — screenshot
ONE element. Resolve by CSS **selector** (most reliable) or visible **text** (pierces shadow DOM), scrolled
into view first. **This is usually the whole answer to "capture just this card."** Text matches the
*tightest* text node — to grab the enclosing card add `closest:".card"`; and if the phrase is split across
spans or appears in prose too, use a **selector** (e.g. `.download-card`).
- **`browser_scroll { sessionId, dy?/dx? | toSelector?/toText?, block?, behavior? }`** — scrolls the ACTUAL
scrolling element (nearest scrollable ancestor, not `window`). Scroll by delta or to an element/text
(shadow-piercing). Returns `{ scrollElement, scrollTop, scrollHeight, clientHeight, atBottom }` so you can
confirm it landed — no more overshoot from hand-computed offsets.
- **`browser_set_viewport { sessionId, width, height, deviceScaleFactor? }`** — pins the viewport
deterministically (fixes a too-small innerWidth). `browser_open_window { width, height }` now applies it on
open, so a sized window renders at the size you asked for.
- **`$deep(sel)` / `$$deep(sel)` / `deepText(txt)`** are auto-injected into every page, so `browser_eval` can
pierce shadow roots directly — no hand-rolled walk:
```bash
adom-desktop browser_eval '{"sessionId":"...","expr":"$$deep(\"a[href]\").length"}' # counts across shadow roots
```
**Still verify by DOM, then by eye.** To count cards/links/rows, count in `browser_eval` by document-Y
(`rect.top + window.scrollY`) — immune to an overshot screenshot — then `browser_screenshot_element` (or
`browser_scroll` + `browser_screenshot`) to confirm. And **first check whether a page is even shadow DOM**
before assuming: a shadow survey (`$$deep` finding more than `querySelectorAll`) tells you; often an "empty"
result just means a wrong selector on plain DOM.
**fullPage caveat:** `browser_screenshot { fullPage:true }` only covers *document*-level scroll. On a
nested-scroll SPA it returns just the viewport and a `nestedScroll:true` hint — use `browser_screenshot_element`
for one region, or `browser_scroll` through the container.
Reminder: `browser_eval` allows **no semicolons** — use the comma operator or an IIFE. If a
screenshot lands on the wrong content, don't trust it — it almost always means an inner scroll container;
re-scroll the element, don't nudge the window.