Skip to main content

app-screenshots

Capture screenshots of the running Mailspring dev app for docs, PRs, or visual verification — launching with a CDP port, driving the UI, anonymizing or seeding data, and clipping to an element. Use whenever a task needs a picture of the app.

来源信息

仓库
Foundry376/Mailspring
最近来源活动
2026年10月7日 19:01
检测到的 SKILL.md 语言
英语
星标
17,902
分支
1,051

安装方式

默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。

检查来源文件

决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。

文件资源管理器
5 个文件

正在显示 SKILL.md

SKILL.md
来源说明 · 只读预览
name
app-screenshots
description
Capture screenshots of the running Mailspring dev app for docs, PRs, or visual verification — launching with a CDP port, driving the UI, anonymizing or seeding data, and clipping to an element. Use whenever a task needs a picture of the app.
# Screenshots of the running app `screencapture` is blocked in this environment (no Screen Recording permission), and the app has no test-id hooks. The reliable path is Chrome DevTools Protocol against a dev build launched with a remote-debugging port. Everything below runs from the repo root unless noted. Scripts live in `.claude/skills/app-screenshots/scripts/`. ## 1. Launch Only one instance can run at a time, so check first. If one is running that you didn't start, it is probably the user's: ask before closing it. ```bash pgrep -fl "[Ee]lectron ./app" # anything listed will be closed by the next line pkill -f "[Ee]lectron ./app"; pkill -f "app/mailsync --mode sync"; sleep 2 (./node_modules/.bin/electron ./app --enable-logging --dev --remote-debugging-port=9333 > /tmp/app.log 2>&1 &) sleep 18 # window + plugins + first sync curl -s localhost:9333/json | grep -c webSocketDebuggerUrl # >0 means ready ``` - Always kill orphaned `mailsync` processes too; they outlive the Electron process and hold the SQLite DB open (a later seed script will silently fail to commit). - `location.reload()` does nothing in the renderer. Pick up renderer changes with `AppEnv.commands.dispatch('window:reload')`; main-process changes (`app/src/browser`) need a full relaunch. Edited LESS can be hot-loaded with `AppEnv.themes.reloadCoreStyles()`. - For doc-sized captures, shrink the window first: `AppEnv.getCurrentWindow().setSize(1280, 800)`. ## 2. Drive the UI `scripts/eval.mjs` evaluates JS in the main window (picks the target whose `document.body.className` contains `window-type-default`) and optionally captures the whole window or dispatches a real mouse click: ```bash node --experimental-websocket scripts/eval.mjs "<js>" [out.png] [clickX clickY] ``` Useful snippets inside the JS: - Navigate to a sheet: `$m.Actions.focusMailboxPerspective(new (require('../internal_packages/activity/lib/activity-mailbox-perspective').default)($m.FocusedPerspectiveStore.sidebarAccountIds()))` - Pick from a `DropdownMenu`: click `.dropdown-menu > div`, wait 300ms, then find the item by text under `.menu div` and dispatch `mousedown` **and** `click` on it. - Drive a controlled `<input>`: set the value through `Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value').set` and dispatch `input`; plain `.value =` doesn't reach React. - Test real selection/hover behaviour with the click args (`Input.dispatchMouseEvent`), not synthetic `el.click()` — the two differ for caret placement and `:hover`. - Wait after every action; the Reports tab has a 2s minimum "thinking" cover. ## 3. Capture an element `scripts/shoot.mjs` runs setup JS, then captures one element by selector using `Page.captureScreenshot` with a `clip` in CSS px: ```bash node --experimental-websocket scripts/shoot.mjs "<setup-js>" "<selector>" out.png [pad] [maxHeight] ``` - `scale: 1` already yields device pixels (2x on retina). Don't pass `scale: 2` — that produces 4x images. - Clip by element rect; don't crop afterwards with `sips -c`, which crops from the centre and pads with black. Use `maxHeight` to trim tall columns instead. - Connecting a CDP session blurs the window (`body.is-blurred`, greyed traffic lights). The script strips the class before capturing. - **Popovers close on blur.** Open the popover inside the same `setup-js` that the capture runs in, never in a previous invocation, and never call `activeElement.blur()` to remove the focus ring — inject a `<style>` with `outline: none !important` instead. ## 4. Make the data presentable ### Anonymize what's on screen The dev mailbox contains real correspondents. Before any capture that leaves the machine, rewrite the rendered DOM: `scripts/anonymize.example.js` walks every text node and `title` attribute and applies a regex map. Copy it, edit the map for the current mailbox, and pass its contents as the `setup-js` (after the view has loaded). - React splits text into separate nodes, so match whole-node values (`/^ben$/`) rather than phrases (`/ben opened/`). - Replace links with `example.com` paths and your own accounts with `you@gmail.com` / `you@yourcompany.com`; sidebar account headers are text nodes too. - Re-run the anonymizer after any action that re-renders (tab switch, dropdown). ### Seed the database when there's nothing to show `scripts/seed-tracking-metadata.py` writes fake open/click metadata onto real sent messages. Pattern for any similar seeding: 1. Stop the app **and** mailsync; `cp edgehill.db edgehill.db.bak-<reason>`. 2. Write both places the UI reads from — the model's JSON blob (`Message.data`, `metadata` array) and the join table (`ModelPluginMetadata`) that queries filter on. 3. Keep timestamps relative to the message's own date so "N days after sending" and relative times look sane. 4. Tell the user where the backup is; the DB is ~1GB. ### Stage a specific layout For illustrative shots (e.g. every kanban column populated) it's fine to move or clone rendered nodes with DOM APIs in the setup JS — but React will throw on its next re-render because its tree no longer matches. So: **take staged captures last**, one per launch, and relaunch before doing anything interactive again. A blank/"loading" view right after staging is that crash, not a product bug. ## 5. Review before shipping Read every PNG back (the Read tool renders images). Things that slipped past otherwise: focus rings, blurred title bars, un-anonymized names in a second text node, a broken `<img>` from a guessed asset path (copy an existing icon's resolved `src` and swap the filename), and letterboxed crops.
在 GitHub 查看