Skip to content

morethancoder/click-to-source

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Click to Source

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.

Demo

If the player doesn't load inline on your client, download or open the demo here.

How it works

  1. Browser extension injects a script into pages on 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.
  2. The extension hands that info to a Node native messaging host running locally on your machine.
  3. The host opens the file in your configured editor:
    • Zed — via zed path/to/file:line:col
    • Neovim — via nvim --server <socket> --remote-send into a running nvim --listen instance

Works on any localhost dev server (Vite, Next.js, CRA, SvelteKit, Remix, Astro, etc.) as long as source-location info is preserved by your bundler.

Requirements

  • macOS (the install script targets macOS native messaging directories — see Other platforms below).
  • Node.js 18+.
  • A Chromium-based browser: Chrome, Chromium, Brave, Edge, or Arc.
  • Zed or Neovim installed.
  • A dev server with debug source info enabled:
    • React: needs @babel/plugin-transform-react-jsx-source (default in Vite-React, CRA, and Next.js dev builds).
    • Svelte: must be compiled with dev: true (the SvelteKit / Vite Svelte plugin default in dev).

Install

1. Clone the repo

git clone https://github.com/morethancoder/click-to-source.git
cd click-to-source

2. Load the extension

  1. Open chrome://extensions (or brave://extensions, edge://extensions, etc.).
  2. Toggle Developer mode on.
  3. Click Load unpacked and select the 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.

3. Install the native messaging host

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

4. Verify

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.

Configure

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 paths

Some 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 sources

If 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/"
  }
}

Switching to Neovim

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.

Use

  1. Open your dev app at http://localhost:<port>.
  2. Hold Alt (Option on macOS) — your cursor will outline elements that have source info.
  3. Click. Your editor jumps to the file and line.

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

Security model

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:

  • Runs on stdio (Chrome's native messaging protocol). It is started by Chrome on demand and exits after handling each message.
  • Only accepts messages from extensions whose ID is in the allowed_origins field of the host manifest installed by server/install.js. By default this is just the pinned ID kjlafkbahaheimiplchgfmefgeljmbhn.
  • Reads its config from ~/.click-to-source.json. It refuses to open files that don't exist on disk after path resolution.
  • Spawns the editor binary with a deliberately minimal 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.

Other platforms

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.

Troubleshooting

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.

Development

There's no build step or test suite. Edit files in place:

  • Extension changes: reload at chrome://extensions.
  • Native host changes: no reinstall needed — Chrome respawns the host per message.

See CLAUDE.md for an architecture overview.

License

MIT

About

Alt+click any React or Svelte element in your localhost dev server to open its source file in Zed or Neovim.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors