Click an element on any webpage in Safari — its selector, HTML, computed styles, box model, and (where available) React component + source file land in Claude Code via a /picked slash command.
Mirrors the element-picker from the VS Code Preview Extension, but for everyday browsing instead of an embedded webview.
"/picked make this button rounder" → Claude sees the actual element you clicked, no need to describe it.
| Path | What |
|---|---|
extension/ |
Web extension (Safari Web Extension, also runs as MV3 in chromium browsers) |
safari-app/ |
Xcode project (Safari Web Extension Container App) — built via rebuild.sh |
server/ |
Local Bun HTTP server on 127.0.0.1:7654 |
launchd/ |
macOS plist to auto-start the server on login |
commands/picked.md |
The /picked slash command for Claude Code |
cd server
bun run server.tsYou should see browser-picker server: http://127.0.0.1:7654.
To auto-start on login (recommended):
# adjust the bun path inside the plist first if you didn't install via the official bun installer
cp launchd/de.leminkozey.browser-picker.plist ~/Library/LaunchAgents/
launchctl load ~/Library/LaunchAgents/de.leminkozey.browser-picker.plistLogs: /tmp/browser-picker.log, /tmp/browser-picker.err.log.
Two routes — pick one:
Builds a signed .app wrapper. Safari treats it as a regular signed extension — no warnings, persists across restarts.
# from the repo root; edits the pbxproj's DEVELOPMENT_TEAM the first time you run it
./safari-app/rebuild.shThen in Safari:
- Open /Applications/Browser Picker.app once (registers with Safari)
- Safari → Settings → Extensions → enable "Browser Picker for Claude Code"
- "Always Allow on Every Website…"
- Pin the toolbar icon
No Xcode needed, but you re-add it after every Safari restart.
- Safari → Settings → Advanced → enable "Show features for web developers" ✓
- Safari → Settings → Developer → enable "Allow unsigned extensions" ✓
- Same tab → "Add Temporary Extension…" → ⌘⇧G → paste the absolute path to the
extension/folder in this repo → Enter - Safari → Settings → Extensions → enable Browser Picker → "Always Allow on Every Website…"
Chrome/Arc/Edge/Brave: extensions page → Developer mode → Load unpacked → pick extension/. Same MV3 code.
Copy or symlink the command into your Claude Code commands directory:
mkdir -p ~/.claude/commands
ln -s "$PWD/commands/picked.md" ~/.claude/commands/picked.mdVerify with /help inside Claude Code — /picked should appear.
- Open the page you want to talk about
- Click the Browser Picker icon in the toolbar → popup → "Pick elements"
- Hover gives you a box-model overlay; click to confirm. Each pick gets a
p1,p2… badge stuck on the element - In Claude Code:
/picked make this button rounder— picked elements prepend Claude's context
⌘⇧P is suggested as a keyboard shortcut, but Safari binds that to print — configure your own in Safari → Settings → Extensions → Browser Picker → Keyboard Shortcuts.
Move your mouse near the status pill while picking and a pencil drop pops out sideways. Click it to capture the viewport, then annotate with:
- Pencil (freehand) / Line / Rectangle / Circle / Text
- 4 colors (red, blue, green, dark)
- Undo / Clear-all
Hit "Zum Chat hinzufügen" to send the annotated PNG along with picked elements via /picked. Claude Code will see the image as a multimodal input.
Files land in /tmp/browser-picker/screenshots/ and are auto-cleaned after 24h.
GET /latest returns markdown (used by /picked). GET /latest.json returns the raw object:
{
"selector": "main > section.hero > h1.title",
"tag": "h1", "id": null, "classes": ["title"],
"text": "Hello world",
"html": "<h1 class=\"title\">…</h1>",
"rect": { "x": 40, "y": 120, "width": 720, "height": 56 },
"boxModel": { "margin": {…}, "border": {…}, "padding": {…}, "content": {…} },
"styles": { "display": "block", "color": "rgb(…)", "fontSize": "48px", … },
"parent": { "tag": "section", "classes": ["hero"], "display": "flex" },
"children": ["span.eyebrow", "span.headline"],
"component": "HeroTitle",
"file": "/src/components/Hero.tsx",
"line": 18,
"pageUrl": "https://example.com/landing",
"pageTitle": "Example",
"pickedAt": "2026-05-17T16:20:55.693Z"
}| Method | Path | Returns |
|---|---|---|
| POST | /selected |
{ok: true, sessionIndex} — used by the extension |
| POST | /screenshot |
{ok, sessionIndex, path} — body is raw image/png, headers X-Page-Url + X-Page-Title |
| POST | /render |
Markdown summary for the slash command (body: tpl=...); screenshots rendered as  first |
| GET | /latest |
Markdown of the most recent element pick (screenshots skipped) |
| GET | /latest.json |
Raw JSON of the last pick (element or screenshot) |
| GET | /session.json |
All picks in the current session |
| GET | /history.json |
Last 100 element picks |
| GET | /health |
{ok, sessionCount, historyCount} |
| POST | /session/clear |
Wipes session (current pick batch, not history) |
- Server keeps state in RAM only. Restart = empty.
- Component / file / line work in React dev-builds (via
__reactFiber$._debugSource). Production builds returnnull. Vue is detected via__vueParentComponent. - Content script is registered declaratively (
content_scriptsin manifest, not programmatic injection). Required for Safari, harmless for chromium. - Old tabs that were open BEFORE the extension was loaded need a refresh (⌘R) before they can be picked from.
- The picker is light liquid-glass styled — toolbar icon is a template image so Safari tints it correctly.
See CHANGELOG.md for version history. Current: 0.2.0 — Magicwand (screenshot + annotation), backdrop-filter perf, dock-button hardening.
MIT — see LICENSE.