Leave notes on your running app, then let Claude Code work through them in one go.
Hold Option on any page, your localhost app or a live site, click the element you mean, and write what should change. Each note is saved with the page URL, the element's selector and HTML, and in React dev builds the component names and the file and line that rendered it. When you're done, tell Claude Code "done with my notes". It takes the whole batch, finds the code behind each element and reports back per note.
Each site keeps its own notes, so notes on your localhost app never mix with notes on a live site. Once a site has notes, a pill in the bottom right corner counts them. Click it to read, edit or delete them.
- Google Chrome on macOS. The setup script registers the native host where Chrome on macOS looks for it.
- Any
httporhttpspage. Notes are kept per hostname: all ports of one host share a queue (localhost:3000andlocalhost:3026are one site), whilelocalhost,127.0.0.1andexample.comare three. - Bun, which runs the small native host that writes the notes file. A Chrome extension cannot write files by itself.
- Claude Code, through the bundled skill. The notes are plain JSON lines, so any agent that can read a file can use them too.
-
Clone the repo where it can stay. Chrome identifies an unpacked extension by its folder path, so if you move the folder later, run step 2 again and reload the extension.
git clone https://github.com/simonorzel26/ui-notes-for-claude-code.git ~/ui-notes-for-claude-code cd ~/ui-notes-for-claude-code
-
Register the native host:
bun setup.ts
This writes two files into
~/Library/Application Support/Google/Chrome/NativeMessagingHosts/:ui_notes_for_claude_code.json, which tells Chrome which extension may start the host, andui_notes_for_claude_code.sh, which startshost.tswith your Bun. -
Load the extension. Open
chrome://extensions, turn on Developer mode (top right), click Load unpacked and choose theextensionfolder inside the repo. In the file dialog, Cmd+Shift+G lets you paste a path. Tabs that are already open get the script straight away. -
Give Claude Code the skill:
mkdir -p ~/.claude/skills && ln -s "$PWD/skill" ~/.claude/skills/ui-notes
-
Open any page, hold Option and click an element, and save a test note. If the bottom right corner says 1 note, it works. Click it and delete the note again.
- Hold Option. The element under the pointer is highlighted with its tag, classes and size, like Chrome's element picker.
- Option-click it. The click never reaches the page, so buttons don't fire and links don't open. Write the note; paragraphs are fine. Cmd+Enter saves, Esc cancels.
- Click the pill to see every note waiting for this site, as tall as the window. Edit changes a note in place (Cmd+Enter saves, Esc leaves the edit). Delete asks for a second click. The link opens the page the note was left on.
- Tell Claude Code "done with my notes", and which site if more than one has notes (Claude asks otherwise). The skill first moves that site's
queue.jsonlinto itsbatches/folder under a timestamp, so anything you add while Claude works starts a new queue. Then it makes the changes and reports per note. The pill disappears the next time you switch to the window.
One JSON object per line in ~/.ui-notes/sites/<hostname>/queue.jsonl:
| Field | Content |
|---|---|
text |
What you wrote |
id, at |
A UUID and the time it was saved |
url, heading, viewport |
The full URL, the page's h1, the window size |
name, selector |
The element as the highlight labels it, and a CSS path to it |
innerText, element, parent |
The element's text and its HTML and its parent's, truncated |
components, source |
React dev builds only: the nearest component names, and the stack frames where the element's JSX was written |
Your notes stay on your machine. The extension makes no network requests, and the host writes only to ~/.ui-notes.
extension/content.jsruns on every page. It draws the highlight, the note modal, the pill and the list, each inside a closed shadow root so the page's CSS can't restyle them and the page's scripts can't read them. It ignores clicks and keys the page fakes, so a site cannot open the note modal by itself.extension/react.jsruns in the page's own JavaScript world, where React keeps the dev info (__reactFiber$…,_debugStack) that content scripts can't see.extension/background.jspasses each request on to the native host, because content scripts cannot talk to one. It adds the site, which it takes from the hostname Chrome reports for the sending tab, so a page only ever sees and changes its own site's notes.host.tsis the native messaging host. Chrome starts it once per request; it adds, lists, edits or deletes notes in that site's queue and replies with the site's whole queue, so the count and the list always match the file. One more copy runs while Chrome is open and watchesextension/: edit a file there and the extension reloads itself and re-injects into open tabs.
When something fails, the pill turns red and shows Chrome's error.
- Specified native messaging host not found. Setup hasn't run. Run
bun setup.ts. - Access to the specified native messaging host is forbidden. The loaded folder isn't the one setup registered, usually because the repo moved. Run
bun setup.tsfrom the folder you loaded, then click reload on the extension inchrome://extensions. - Any other host error after updating or moving Bun. The launcher holds Bun's full path. Run
bun setup.tsagain.
Remove the extension in chrome://extensions, then:
rm ~/Library/Application\ Support/Google/Chrome/NativeMessagingHosts/ui_notes_for_claude_code.{json,sh}
rm ~/.claude/skills/ui-notesYour notes stay in ~/.ui-notes until you delete that folder.
MIT


