Project

pinmark

0.0
No release in over 3 years
Pinmark adds a dev-only floating overlay to any Rails app. Click a component or any element on the page, leave a comment, and Claude Code consumes the queue via MCP — closing the loop between visual feedback and source edits. Works with Phlex, ViewComponent, and ERB partials.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies

Development

Runtime

~> 0.14
>= 7.1
 Project Readme

Pinmark

CI Gem Version Ruby Rails License: MIT

Pin-style UI annotations that flow into Claude Code via MCP.

demo

Status: early but usable. API may shift before 1.0 — pin a version in your Gemfile.

Table of contents

  • Why Pinmark?
  • What you get
  • Quickstart
  • Install
  • Configure
  • Use
  • Connect to Claude Code
  • How it works
  • Renderer support
  • Documentation
  • Development
  • License

Why Pinmark?

You spot something off in the browser — a misaligned card, a copy bug, the wrong shade of orange. Normally you'd switch contexts: open the editor, hunt for the component, type the prompt, paste a screenshot, describe the spot.

Pinmark closes that loop. Click the element on the page, type the comment, keep working. Claude Code reads the queue over MCP and edits the right file — because each annotation already carries its source file:line, the component class, and the DOM selector.

What you get

  • Floating "Enable annotations" activator that survives Turbo navigation.
  • Click-to-pin overlay with popover, side panel, on-page pin markers, marquee select, hover label, and a dual-highlight context for component vs. element.
  • <!-- pinmark:begin/end --> HTML markers around every Phlex / ViewComponent / ERB partial render — automatically.
  • File-backed atomic queue. Survives reloads, no database needed.
  • Rack-mountable MCP HTTP server, in-process with your Rails app.
  • Four MCP tools so Claude Code can list, resolve, and clear annotations.

Quickstart

# 1. Add the gem (development group)
bundle add pinmark --group development

# 2. Wire it into your app
bin/rails generate pinmark:install

# 3. Boot the server, then point Claude Code at the in-process MCP endpoint
bin/rails server
claude mcp add pinmark --transport http \
  http://localhost:3000/dev/pinmark/annotations/mcp

# 4. Open any page, click "Enable annotations", drop a pin, type a comment.
#    Then ask Claude Code: "list pending annotations and fix them."

The generator handles routes and importmap pinning. The remaining manual steps (Current attribute, Session concern, layout partials, optional Phlex include) are listed in Configure.

Install

In the host app's Gemfile:

group :development do
  gem "pinmark"
end

Then:

bundle install
bin/rails generate pinmark:install

The generator mounts Pinmark::Engine at /dev/pinmark (only when Rails.env.local?) and pins the engine's Stimulus controller into your importmap.

For webpack / esbuild hosts, the same controller is published as an exports map in package.json — add "pinmark": "*" (or a file: path during local development) to your package.json and import it from your Stimulus entry point:

import PinmarkController from "pinmark"
application.register("pinmark", PinmarkController)

Configure

A few host touch-points stay manual because they live in host-owned classes.

  1. Current attribute — Pinmark stores the per-request tracker on ActiveSupport::CurrentAttributes:

    class Current < ActiveSupport::CurrentAttributes
      attribute :pinmark
    end
  2. Controller — include the session concern in any controller whose responses should support annotations:

    class ApplicationController < ActionController::Base
      include Pinmark::Session
    end
  3. Layout — render the activator + overlay near the bottom of <body> in your dev layout:

    <% if Rails.env.development? && Current.pinmark.present? %>
      <%= render "pinmark/activator" %>
      <%= render "pinmark/overlay" %>
    <% end %>
  4. Phlex base class (optional) — only if your host uses Phlex:

    class Components::Base < Phlex::HTML
      include Pinmark::Phlex if Rails.env.development?
    end

    ViewComponent and ERB partial wrapping are auto-applied at engine boot — no per-host wiring needed.

  5. Mount — the install generator adds this, but for reference:

    # config/routes.rb
    mount Pinmark::Engine, at: "/dev/pinmark" if Rails.env.local?

Full configuration reference lives in docs/configuration.md.

Use

Visit any page in development. Click the floating "Enable annotations" button. Hover any component or element, click to drop a pin, leave a comment, save. Pins persist on the page and across navigation until they're addressed.

Activation triggers:

  • Cookiepinmark=1 (set by clicking the activator).
  • Query param?annotate=1 for a one-off page.

Both checks live in Pinmark::Session#pinmark_enabled? and are gated on Rails.env.development?, so production traffic is never instrumented.

Connect to Claude Code

claude mcp add pinmark --transport http \
  http://localhost:PORT/dev/pinmark/annotations/mcp

Tools exposed:

Tool Purpose
list_pending_annotations Every open annotation with file:line, component class, DOM selector, comment, page path, and ancestry chain.
list_resolved_annotations Annotations that have been addressed (audit / undo).
mark_addressed Flip a single annotation by id. Idempotent.
clear_addressed Purge the resolved bucket.

After Claude Code makes the change, it calls mark_addressed and the pin disappears from the page on the next reload.

Full schemas, sample payloads, and prompt patterns are in docs/mcp-tools.md.

How it works

┌─────────────────────────────────────────────────────────────────┐
│  Browser                                                         │
│  ─────────                                                       │
│  Stimulus controller walks the DOM, parses pinmark markers,      │
│  draws highlights, captures comment + selector + node_id, and    │
│  POSTs to the engine.                                            │
└──────────┬──────────────────────────────────────────────▲────────┘
           │ POST /dev/pinmark/annotations                │
           ▼                                              │
┌─────────────────────────────────────────────────────────┴────────┐
│  Rails (host process)                                            │
│  ──────────────────────                                          │
│  Per-request Tracker collects parent/child render hierarchy.     │
│  Wrapper emits <!-- pinmark:begin/end --> around every render.   │
│  AnnotationsController appends to tmp/pinmark/queue.json         │
│  atomically.                                                     │
│  MCP HTTP server reads the same JSON file in-process.            │
└──────────┬───────────────────────────────────────────────────────┘
           │ MCP tool calls (HTTP)
           ▼
        Claude Code
  • Render hooks emit HTML comment markers (<!-- pinmark:begin id=... --><!-- pinmark:end id=... -->) around every component render.
  • A per-request Pinmark::Tracker collects the parent/child hierarchy plus the source location for each marker.
  • The Stimulus controller in the browser walks the DOM, resolves which component is under the cursor, draws highlights, and POSTs annotations to the engine's controller.
  • Annotations are appended atomically to tmp/pinmark/queue.json.
  • The MCP server reads the same JSON file and exposes the four tools listed above. Claude Code talks to your local Rails app directly — no separate process.

A deeper walkthrough lives in docs/architecture.md.

Renderer support

Renderer Wiring
Phlex include Pinmark::Phlex in your component base class
ViewComponent Auto — render_in is prepended at engine boot
ERB partial Auto — render_partial_template is prepended at engine boot

Documentation

Doc What's in it
docs/architecture.md End-to-end data flow, lifecycle of an annotation, file layout.
docs/configuration.md Every config knob, the Pinmark.active? predicate, environment behavior.
docs/mcp-tools.md Full MCP tool reference: input schemas, sample output, prompt patterns.
docs/troubleshooting.md Common gotchas and how to debug them.
CHANGELOG.md Per-release notes.
CONTRIBUTING.md Local dev loop, test layout, PR expectations.

Development

git clone https://github.com/lluzak/pinmark.git
cd pinmark
bundle install
bundle exec rspec

Pull requests welcome. Please add specs for any new behavior and keep the diff focused — Pinmark stays intentionally small. See CONTRIBUTING.md for the full loop.

License

MIT — see LICENSE.txt.