Alt+click any React or Svelte element in your localhost dev server to open its source file in Zed or Neovim at the exact line and column.
No build step. No bundler. Just a Manifest V3 browser extension and a tiny Node.js native messaging host.
If the player doesn't load inline on your client, download or open the demo here.
localhost / 127.0.0.1. When you Alt+hover an element it gets highlighted; when you Alt+click it walks the React fiber tree (_debugSource) or the Svelte __svelte_meta chain to find the file, line, and column.zed path/to/file:line:colnvim --server <socket> --remote-send into a running nvim --listen instanceWorks on any localhost dev server (Vite, Next.js, CRA, SvelteKit, Remix, Astro, etc.) as long as source-location info is preserved by your bundler.
@babel/plugin-transform-react-jsx-source (default in Vite-React, CRA, and Next.js dev builds).dev: true (the SvelteKit / Vite Svelte plugin default in dev).git clone https://github.com/morethancoder/click-to-source.git
cd click-to-source
chrome://extensions (or brave://extensions, edge://extensions, etc.).extension/ folder.The extension's ID is pinned to kjlafkbahaheimiplchgfmefgeljmbhn on every machine via the key field in manifest.json, so the native host's allowlist is the same for everyone — no need to copy IDs.
node server/install.js
This writes a small JSON manifest into the native-messaging-hosts directory of every supported browser it finds:
~/Library/Application Support/Google/Chrome/NativeMessagingHosts/~/Library/Application Support/Chromium/NativeMessagingHosts/~/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts/~/Library/Application Support/Microsoft Edge/NativeMessagingHosts/~/Library/Application Support/Arc/User Data/NativeMessagingHosts/To uninstall:
node server/install.js --uninstall
Click the extension's toolbar icon. The popup should say "Native host ready — editor: zed". If it says the host is not registered, re-run step 3 and reload the extension.
On first run the host writes a default config to ~/.click-to-source.json:
{
"editor": "zed", // "zed" or "nvim"
"zedBin": "zed", // binary name or absolute path
"nvimBin": "nvim",
"nvimSocket": "/tmp/nvim.sock", // start nvim with: nvim --listen /tmp/nvim.sock
"originRoots": {}, // { "http://localhost:5173": "/abs/path/to/project" }
"pathRewrites": {} // { "/app/src/": "/Users/me/repo/src/" }
}
originRoots — required for relative pathsSome bundlers (Vite, SvelteKit) report file paths relative to the project root. The host has no way to know which project an http://localhost:5173 page belongs to, so you have to tell it:
{
"originRoots": {
"http://localhost:5173": "/Users/me/code/my-app",
"http://localhost:3000": "/Users/me/code/other-app"
}
}
If a page reports an absolute path (CRA, Next.js dev), originRoots isn't needed.
pathRewrites — for Docker / remote sourcesIf your dev server reports paths as they appear inside a container (e.g. /app/src/...) but the same files live somewhere else on your host:
{
"pathRewrites": {
"/app/src/": "/Users/me/code/my-app/src/"
}
}
Set editor to "nvim" (or "neovim"), then start an nvim instance listening on the configured socket:
nvim --listen /tmp/nvim.sock
Subsequent Alt+clicks will jump that nvim window to the file/line.
http://localhost:<port>.If nothing happens, open the page's DevTools console — both the extension and the native host log clear errors there. The native host also writes a log to /tmp/click-to-source-host.log:
tail -f /tmp/click-to-source-host.log
This is a localhost-only tool. The extension's content script and web_accessible_resources are scoped to localhost, 127.0.0.1, and *.localhost only. It does not run on any public site.
The native host:
allowed_origins field of the host manifest installed by server/install.js. By default this is just the pinned ID kjlafkbahaheimiplchgfmefgeljmbhn.~/.click-to-source.json. It refuses to open files that don't exist on disk after path resolution.PATH (/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin).If you fork the extension and regenerate its key, update PINNED_EXTENSION_ID in server/install.js and re-run install.
The install script currently writes manifests only into macOS-style paths. Linux and Windows users can still use the project — they just need to drop com.clicktosource.host.json (the same content install.js generates) into their browser's native messaging host directory by hand. PRs welcome.
| Symptom | Fix |
|---|---|
| Popup says "Native host not registered" | Run node server/install.js, then reload the extension. |
| Click does nothing, console says "No source info on this element" | Your bundler stripped debug info. For React, ensure @babel/plugin-transform-react-jsx-source runs (it does by default in dev). For Svelte, ensure dev: true. |
| Error: no projectRoot configured for origin "..." | Add an entry under originRoots in ~/.click-to-source.json. |
| Error: file does not exist on disk: ... | Your bundler reports a path that doesn't match disk — add a pathRewrites entry to remap it. |
| Error: nvim socket not found at /tmp/nvim.sock | Start nvim with nvim --listen /tmp/nvim.sock, or change nvimSocket in the config. |
zed / nvim not found |
The host spawns with a minimal PATH. Set zedBin / nvimBin to an absolute path in the config. |
There's no build step or test suite. Edit files in place:
chrome://extensions.See CLAUDE.md for an architecture overview.