Authenticated Web Screenshot (login → capture → stitch → watermark)
Overview
Capture a complete, paginated web page that sits behind a login. The skill logs
in once through a visible browser, persists the auth state, then repeatedly
produces full-page images headlessly — scrolling the page in fixed-height
segments, stitching them into a single tall image, and applying a watermark.
Everything is driven by CLI arguments; nothing about a particular site is
baked in, so it works for any authenticated URL.
Use this skill for: daily dashboard snapshots, archiving signed-in reports,
capturing charts/tables behind auth, building visual backups of web apps.
When to use
- The user wants a screenshot of a page that requires login (dashboard, report,
admin panel, OIDC/SAML site). - The user wants a full-page image longer than one viewport, including canvas
or SVG charts that break under a naive fullPage shot. - The user wants to stitch multiple captures, or stamp a date/logo watermark.
- A repeating/automated screenshot job is requested (e.g. "every day at 18:00").
Do not use this for public pages with no login — a single headless
page.screenshot is enough there. Also avoid for pages that need heavy
interaction before rendering (multi-step wizards); handle the interaction first,
then capture.
Prerequisites & setup
- A Chrome/Chromium browser installed on the machine (auto-detected; override
with--chrome-pathorCHROME_PATH). - Install Node dependencies once into the skill's
scripts/folder:
bash cd {skill}/scripts && npm init -y && npm install puppeteer-core sharp
(puppeteer-coredoes not download a browser. To reuse a shared workspace
instead, run scripts withNODE_PATH=<workspace>/node_modules.)
Workflow
Step 1 — Establish login state (one time, or when session expires)
The easiest path is the guided mode: it opens the login page in a visible
browser with a floating panel, and you click to choose the selectors — no
need to inspect HTML or guess CSS.
node scripts/login.js --url "{LOGIN_URL}" --guided
Then follow the on-screen panel in the browser:
- Click ① 选择登录元素, then click the login-related element on the page
(e.g. the login button or form). - Log in manually as you normally would.
- After login succeeds, click ② 选择登录完成标识, then click the element
that only appears once logged in (e.g. your avatar / username). - Click ✅ 完成并保存.
The script records both selectors and the auth state. The selectors are saved
to scripts/selectors.json keyed by domain, so capture.js reuses them
automatically (no need to pass --login-selector / --logged-in-selector).
Non-guided alternatives (when a human cannot click, e.g. headless CI):
# Auto-save when a post-login marker appears:
node scripts/login.js --url "{LOGIN_URL}" --profile "{PROFILE_DIR}" \
--wait-selector "{CSS_AFTER_LOGIN}"
# Or: save live state continuously, then close the browser to finish:
node scripts/login.js --url "{LOGIN_URL}" --profile "{PROFILE_DIR}"
--profileis a dedicated Chrome user-data dir (auto-derived from the URL host
when omitted); reuse the same value later in capture. The auth state persists
in this directory AND in<profile>/auth-state.json(portable backup).--selectors-fileoverrides where the per-domain selectors are stored
(default:scripts/selectors.json).
Step 2 — Capture + stitch + watermark (repeatable / automatable)
Run scripts/capture.js headlessly:
node scripts/capture.js --url "{TARGET_URL}" --profile "{PROFILE_DIR}" \
--output "{OUT.png}" \
--render-selector "{CONTENT_ELEMENT}" \
--watermark "2026-01-01 09:00 自动截图" --watermark-pos bottom \
--date-suffix
- After Step 1 (
--guided), the--login-selector/--logged-in-selectorare
loaded automatically fromselectors.jsonby domain — you can omit them.
Pass them explicitly to override, or use--no-auto-detectto disable the
heuristic login-page fallback. - Segment capture (default) preserves canvas/SVG content; add
--full-pageto
use Chrome's native fullPage instead. - If the server session expired, the saved login selector triggers an auto
re-import ofauth-state.jsonand reload. If that still fails, re-run Step 1.
Step 3 (optional) — Standalone stitch / watermark
scripts/stitch.js --inputs a.png b.png --output full.png [--direction vertical|horizontal] [--gap N]scripts/watermark.js --input in.png --output out.png --text "..." [--position top|bottom|tile] [--logo path]
For the complete argument reference and troubleshooting, load
references/usage.md. An editable settings template is at
assets/config.example.json.
Scripts index
| Script | Purpose |
|---|---|
scripts/login.js |
Guided login (--guided click-to-pick selectors) or auto-save; persists profile + auth-state.json + selectors.json |
scripts/capture.js |
Headless capture → segment → stitch → watermark |
scripts/stitch.js |
Join images vertically/horizontally |
scripts/watermark.js |
Stamp text/logo watermark on an image |
scripts/lib.js |
Shared helpers (Chrome detection, watermark, stitch) |
Notes / gotchas
- Use a separate
--profileper site; sharing one profile across sites can
cause cookie cross-contamination. - Sticky/fixed headers repeat in each segment slice (segment mode). Mitigate with
a smaller--segment-heightor--full-page. - For scheduled jobs, ensure the profile's session is still valid; rebuild it via
login.jswhen captures start showing the login page. - Guided mode needs a visible browser. If you run it through an environment
that hides the browser window (sandboxed agent, headless host), the click panel
is not interactable. Run it on the user's own desktop instead — e.g. execute
node scripts/login.js --url "https://..." --guidedthere and paste the login
URL when prompted. The savedselectors.json+ profile are then reused by
headlesscapture.jsruns. (The skill is cross-platform; do not ship
Windows-only.bathelpers inside the skill folder — published skills must not
contain them. Wrap the command in a local.baton your own machine if you want
a double-click shortcut.) - Never store passwords or tokens in the skill or config — only the browser
profile and exported cookie JSON hold auth, kept locally on the user's machine.