npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mikuexe/annotator-react

v0.2.2

Published

React overlay for source-aware UI annotations copied into coding agents.

Downloads

306

Readme

@mikuexe/annotator-react

React devtool overlay for source-aware UI annotations. Select live DOM elements, write notes, then copy an agent-ready Markdown prompt.

Start here first

If you want a polished, supported tool in this space, look at these first:

This package exists for my own local-agent UI workflow. It is intentionally small, experimental, and likely to change whenever my needs change.

Important credit: Aiden Bai created react-grab and element-source, the source-resolution library this package builds on.

I made this repo because I wanted custom UI behavior on top of element-source. You are welcome to use it, but please treat it as personal tooling, not a supported product. I am not committing to issue triage, feature requests, roadmap stability, or compatibility guarantees. Requests are fine, but this package will continue to follow my own needs first.

Install

npm install @mikuexe/annotator-react

Peer deps:

npm install react react-dom

Quick start

register must load before React.

// src/main.tsx
import "@mikuexe/annotator-react/register";

import { createRoot } from "react-dom/client";
import { SourceAnnotator } from "@mikuexe/annotator-react";
import { App } from "./App";

createRoot(document.getElementById("root")!).render(
  <>
    <App />
    <SourceAnnotator />
  </>,
);

SourceAnnotator is a client component: render it only from browser/client React trees.

Click Annotate, select an element, write a note, then click Collect.

Desktop multi-select: while annotating, hold Ctrl (Windows/Linux) or ⌘ Cmd (macOS) and click more elements to attach them to the same note. You can also use Link element on an existing annotation to attach one more element to that same comment.

API

type SourceAnnotatorProps = {
  enabled?: boolean;
  hotkey?: string; // default: "alt+a"
  output?: "markdown" | "json" | "both"; // default: "markdown"
  target?: Document | HTMLIFrameElement | null; // default: document
  onCollect?: (payload: AnnotationCollection) => void;
  renderToaster?: boolean; // default: true
};

onCollect(payload) fires after the clipboard write succeeds. The payload includes collection-level page context as page: { domain, path }.

Annotating same-origin iframes

Pass a same-origin iframe element as target when the annotator UI lives in a parent shell but users need to select elements inside the framed app. The annotator listens inside the iframe document, captures the actual clicked element, and offsets highlights, pins, and popovers into the parent viewport.

import { useState } from "react";
import { SourceAnnotator } from "@mikuexe/annotator-react";

export function ReviewShell() {
  const [iframe, setIframe] = useState<HTMLIFrameElement | null>(null);

  return (
    <>
      <iframe ref={setIframe} src="/prototype/" />
      <SourceAnnotator target={iframe} />
    </>
  );
}

The iframe must be same-origin so the browser allows access to iframe.contentDocument. Cross-origin iframes cannot expose their internal DOM to the parent page.

Sonner toaster ownership

By default, SourceAnnotator renders its own Sonner <Toaster /> so copy success and failure toasts work without host setup. If your app already renders a Sonner toaster, disable the internal one to avoid duplicate toaster roots:

<SourceAnnotator renderToaster={false} />

Output modes

Default output is Markdown only:

<SourceAnnotator />

Opt into structured JSON:

<SourceAnnotator output="both" />
<SourceAnnotator output="json" />

Markdown includes available fields only. Missing source data is omitted instead of printed as Unavailable.

Example copied Markdown:

Please update the UI based on these source-linked annotations.

Collected at: 2026-04-25T00:00:00.000Z
Domain: example.com
Path: /prototype

## Annotation 1

ID: ann-1
Note: Make this CTA full width.
Source: src/App.tsx:42:7
Nearest React component: ActionButton
React owner path: ActionButton › HeroSection › App
React source stack:
- src/App.tsx:42:7 (ActionButton)
- src/App.tsx:18:3 (HeroSection)
Element tag: button
Element HTML: <button class="primary-cta" type="button">Start annotation pass</button>
Element text: Start annotation pass
Selector: #root main.app-shell section.hero button.primary-cta:nth-of-type(1)

Captured data

type AnnotationSource = {
  filePath: string;
  lineNumber: number | null;
  columnNumber: number | null;
  componentName: string | null;
};

type AnnotationTarget = {
  source: AnnotationSource | null;
  sourceStack: AnnotationSource[];
  componentPath: string[];
  element: {
    tagName: string;
    text: string;
    html: string;
    selector: string;
  };
};

type Annotation = {
  id: string;
  note: string;
  targets: AnnotationTarget[];
};

type AnnotationCollection = {
  createdAt: string;
  page: {
    domain: string;
    path: string;
  };
  annotations: Annotation[];
};

How source capture works

@mikuexe/annotator-react/register installs the React DevTools hook through bippy/install-hook-only. element-source then uses React ownership data to resolve selected DOM elements back to React components/source.

If source resolution fails, annotations still include DOM context: tag, HTML, text, selector, and any nearest React component/owner path that could be resolved. Copied/exported payloads contain only serializable data and never internal DOM references.

Local example

cd examples/vite-react
npm install
npm run dev

The example app aliases linked source to one React copy in Vite to avoid duplicate-React invalid hook errors during local development.

Current constraints

  • Vite-first support.
  • React-first API; not framework agnostic.
  • No backend, accounts, persistence, screenshots, or collaboration in v1.
  • Next.js support is not validated yet.
  • Source capture depends on register running before React imports.