A Claude Code skill that turns a website into a live in-page commenting surface. Highlight text, click an element, or drop a note — the comment lands in a local inbox that Claude reads and responds to by editing the source. The page reloads with a walkthrough of what changed.
This is a fork of paraschopra/make-pages-interactive. The original works on a folder of plain static HTML (it assumes the file you view is the file you edit). This fork adds framework mode so it also works on Eleventy, Astro, and Next.js projects, where the served HTML is throwaway build output and real edits go to source templates. All credit for the original design, client library, and server goes to Paras Chopra.
| Upstream | This fork | |
|---|---|---|
| Plain static HTML | ✅ | ✅ (unchanged) |
| Eleventy / Astro / Next.js | ❌ | ✅ framework mode |
| Injection target | tags written into every *.html |
static: every page · framework: one dev-gated block in the base layout |
| Who serves the pages | the bundled Python server | static: Python server · framework: your own dev server (keeps live-reload) |
| Python server role | serves pages and feedback API | static: both · framework: CORS sidecar API on a separate port |
| Production safety | n/a (static) | framework block is dev-gated — never ships to production |
Everything else — the client library, the comment UX, the inbox/history protocol, the auto-shutting-down server — is from the original.
- Agent → page notes. The agent can write
feedback/notes.json(info / question / blocked); the page polls it and surfaces the note in the panel. The loop is now two-way — "can't do that, it's a build asset" or "the header or the hero?" reaches you instead of a silent skip. - Quick-reaction chips. The comment editor offers one-tap chips ("too big", "wrong colour", "tighten spacing", …) that append to your note, so common feedback is a click, not retyping.
- Agent-side screenshots. SKILL.md now directs the agent to open the page and screenshot the commented element for visual feedback — editing against what you actually see, not just markup.
- Complete teardown.
inject.py --removenow also stops this project's sidecar and drops its registry row (via~/.claude/cf-registry.json) — one clean undo of tags + server + registry.
- Revert-by-click. The agent commits each applied batch to git and records
the SHA in
history.json; the History tab then shows a ⏪ revert button that asks the agent togit revertit. Feedback becomes a safe, undoable experiment instead of a one-way edit. - Reply on a change. Each History item has a ↳ reply button that threads
a follow-up to that exact change (
in_reply_to), so "almost — make it darker" reaches the agent as feedback on the edit it just made, not a context-free request. The comment loop is now an actual conversation. - Durable anchors. Element ids are now content-derived (tag + text hash)
instead of positional (
el-1,el-2…), so comments and change markers survive reloads and DOM reordering — directly attacking the "anchor not found after reload" failure mode. - Viewport context. Every batch reports
{width, height, label}(mobile / tablet / desktop), so a layout complaint tells the agent which breakpoint.
- Binds
127.0.0.1by default. The sidecar previously listened on all interfaces with no auth — anyone on your network could POST feedback (which drives source edits). Now localhost-only unless you opt in with--host 0.0.0.0, and that path warns loudly without a token. - Optional shared-secret token.
server.py --token <secret>(orinject.py --token <secret>, persisted tofeedback/.cf-token) requiresX-CF-Tokenon every POST —401otherwise. For when you do expose it (phone testing, LAN). - Live-sidecar registry. Every server records itself in
~/.claude/cf-registry.json;python lib/server.py --listshows every wired project (id, port, dir, pid) across sessions. Lock-guarded against concurrent writes, self-heals dead entries, cleans up onSIGTERM/exit. --auto-port. If the requested port is taken, bind the next free one (static mode — framework pages bake the port intodata-cf-api).- Comments now carry computed styles. Element comments include a small slice
of the rendered styling (
font-size,color,width/height, spacing, …) so the agent can act on visual feedback ("too big", "wrong colour") without guessing from markup.
Fixes the "feedback lands in the wrong Claude session" problem when several projects run at once, and generalizes the element picker.
- Per-project identity, verified end-to-end. Each project gets a stable id
(
feedback/.cf-project, derived from its path) thatinject.pystamps into the page asdata-cf-project.feedback.jssends it on every batch (theX-CF-Projectheader + in the body), andserver.pyrejects a mismatched batch with HTTP 409 instead of appending it. A page pointed at the wrong port now shows an actionable toast — "this page is X but :PORT serves Y" — and keeps your pending comments, instead of silently dropping them in another session's inbox. /infonow reportsproject_id, so a session can confirm the inbox it monitors matches the page it's looking at.- Selection generalized. Element-selection mode now accepts common containers
(
div,main,header,footer,nav,aside,section,form, …) and any element carrying a class — superseding the v1.0.1 landmark + named-grid lists with a single any-class rule, so arbitrary wrapper blocks no longer fall through to the wrong ancestor. - Multi-project guidance.
inject.pywarns when projects share the default:5050, andSKILL.mddocuments the one-port-per-project rule. - Batches now send the full
page_url(location.href) rather than just the path.
Backwards compatible: a page with no project id (plain static / older inject) still posts and is accepted as before.
- Selectable HTML5 landmarks —
<footer>,<header>,<nav>,<aside>, and<main>recognised by the picker (previously they walked up to<body>and selected nothing). - Selectable layout containers — common grid/row wrappers (
three-col,two-col,matrix,metrics,stats,row-list,chips) became commentable, so a whole grid/row could be selected as one unit. (v1.1.0 replaces this named list with a general any-class rule.)
- Framework mode for Eleventy / Astro / Next.js: one dev-gated block injected
into the base layout,
server.pyrunning as a CORS sidecar API, client API base read fromdata-cf-api. - LICENSE copyright lines cleaned for SPDX detection; README screenshot added.
Static mode (plain HTML) — identical to upstream:
user highlights / clicks → feedback.js (in every page) → POST /feedback
→ server.py (stdlib HTTP) → feedback/inbox.jsonl
→ Claude edits HTML, appends feedback/history.json → page reloads with walkthrough
Framework mode (Eleventy / Astro / Next.js) — dev-server companion:
your framework dev server Python sidecar (:5050)
┌───────────────────────────┐ ┌─────────────────────────┐
browser →│ renders pages + live-reload│ │ /feedback (POST) │
│ base layout has a │ CORS │ /feedback/history.json │
│ dev-gated <script │ ───────▶ │ /lib/feedback.{js,css} │
│ data-cf-api=:5050> │ │ → feedback/inbox.jsonl │
└───────────────────────────┘ └───────────┬─────────────┘
│ Monitor
▼
Claude edits SOURCE templates
(.njk / .astro / .tsx),
appends feedback/history.json
The key move: inject once into the base layout, dev-gated
(eleventy.env.runMode == "serve", import.meta.env.DEV, or
process.env.NODE_ENV === "development"), so it survives rebuilds and never
reaches production. The framework serves the pages (you keep live-reload); the
Python server runs as a CORS-enabled API sidecar.
git clone https://github.com/flenard/make-pages-interactive-frameworks \
~/.claude/skills/make-pages-interactiveClaude Code auto-discovers any folder under ~/.claude/skills/ that contains a
SKILL.md. (Clone into a folder named make-pages-interactive so the trigger
phrases and docs line up.)
In any Claude Code session, say:
"Make this site interactive."
(or "make these pages interactive", "let me comment on this page", "add feedback to these pages")
Claude will detect whether the project is static or a framework and:
- Wire it up — static: inject tags into every
*.html; framework: inject one dev-gated block into the base layout (scripts/inject.pyauto-detects). - Create
feedback/inbox.jsonl+feedback/history.json, and add the runtime files to.gitignore. - Start the server(s) — static: the Python server; framework: your dev server plus the Python sidecar API.
- Hand you the URL to open (for framework projects, the framework dev-server URL — not the sidecar port).
- Monitor the inbox so any comment you leave is picked up immediately.
Comment away. Claude edits the source in response. Never edit build output
(_site/, dist/, .next/) — it's wiped on the next build.
# auto-detect (static / eleventy / astro / next)
python ~/.claude/skills/make-pages-interactive/scripts/inject.py ./your-project
# force a mode, or point at a specific base layout / sidecar port
python .../inject.py ./your-project --framework astro --api http://localhost:5052
python .../inject.py ./your-project --framework eleventy --layout src/_layouts/base.njk
# start the sidecar API (framework mode) or full server (static mode)
python ~/.claude/skills/make-pages-interactive/lib/server.py ./your-project --port 5050python ~/.claude/skills/make-pages-interactive/scripts/inject.py ./your-project --removeStatic: strips the tags from every page. Framework: removes the dev-gated block from the base layout. Idempotent; the round-trip is byte-identical.
- Text selection — highlight any text, a popup offers "comment".
- Element selection — the "select element" tool; click an image, table, card, section. Shift-click to add more. Anchored to a stable selector.
- Page-level — "+ general" leaves notes not tied to any element.
Comments batch client-side and submit as one POST, so Claude responds to a
coherent set rather than firing per keystroke. Each carries a stable id,
selector, tag, text snippet, and truncated outerHTML — enough for Claude to
locate the matching source in a framework project.
make-pages-interactive-frameworks/
├── SKILL.md # agent-facing skill spec (static + framework flows)
├── README.md # this file
├── LICENSE # MIT (Paras Chopra + framework-mode modifications)
├── lib/
│ ├── feedback.js # client library; data-cf-api + data-cf-project (forked)
│ ├── feedback.css # comment UI styles (upstream)
│ └── server.py # stdlib HTTP server / CORS sidecar + project-id guard (forked)
└── scripts/
├── inject.py # framework-aware inject/remove (forked)
└── update.py # fork notice (no upstream auto-pull)
Forked from paraschopra/make-pages-interactive by Paras Chopra. The client library, comment UX, inbox/history protocol, and the self-cleaning server are his original work. This fork adds Eleventy/Astro/Next.js support.
MIT. See LICENSE.
