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

pindrop.js

v0.1.17

Published

Drop-in comment overlay for any web page. Pin comments, threaded replies, and share via JSON export/import.

Readme

Pindrop.js 📌

A zero-dependency, hyper-lightweight visual feedback layer for the web.

Pindrop allows your team (or clients) to drop pins directly onto your live website or web app, leave threaded comments, and seamlessly collaborate—without ever leaving the browser.

Unlike heavy collaborative tools, Pindrop requires no backend, no accounts, and no dependencies. Out of the box, it runs entirely offline using your browser's local storage and generates shareable JSON files.

Need real-time sync for a larger team? Simply attach your own storage adapter to connect it to Firebase, Supabase, PostgreSQL, or any database you choose!


✨ Features

  • Zero Dependencies: Pure vanilla JS and CSS. Plugs into React, Vue, Svelte, or vanilla HTML alike without bloating your bundle.
  • Offline First: Out of the box, saves comments to localStorage and generates .json export blobs so you can share feedback securely over email, Slack, or Jira.
  • Bring Your Own Database: Want 'multiplayer' sync? Just pass a custom adapter object with load() and save() handlers to hook into your own API.
  • Smart Anchoring: Pins perfectly attach to layout components and automatically survive CSS class name mangling from modern compilers (Tailwind, CSS Modules).
  • Automation Ready: Comes with a built-in Node CLI to convert your exported JSON files into Markdown reports. Also exposes a complete programmatic API for Playwright/AI bots to add and resolve feedback automatically.
  • Dark Mode Support: Seamlessly respects your OS theme preference or can be forced into Dark/Light mode.

📦 Installation

Install Pindrop via NPM: ```bash npm install pindrop.js ```


🚀 Quick Start

Drop Pindrop into the root of your frontend application:

```javascript import { Pindrop } from 'pindrop.js'; import 'pindrop.js/dist/style.css'; // Don't forget the CSS!

// Initialize Pindrop const pindrop = Pindrop.init({ storageKey: 'my-app-feedback', // LocalStorage prefix key theme: 'auto', // 'light' | 'dark' | 'auto' }); ```

You're done! Hit the c key to enter Comment Mode, click anywhere on the page, and leave a pin. Hit the v key to switch back to View Mode.


🛠 Advanced Usage

Connecting a Custom Database (Multiplayer Sync)

To upgrade Pindrop from offline-only to a fully synced collaborative tool, provide an adapter in the options.

```javascript const pindrop = Pindrop.init({ adapter: { load: async () => { // Fetch the comments from your backend API const res = await fetch('/api/comments'); return await res.json(); }, save: async (comments) => { // Save the entire array back to your database whenever a comment changes await fetch('/api/comments', { method: 'POST', body: JSON.stringify(comments) }); } } }); ```

Event Hooks

Listen to Pindrop events to trigger custom actions (like sending an email notification when someone leaves a comment).

```javascript pindrop.on('comment:add', (comment) => { console.log('New feedback left by', comment.author); });

pindrop.on('comment:resolve', (comment) => { console.log('Issue resolved! Send a webhook to Jira.'); }); ```

AI / Bot Automation API

Pindrop exposes a programmatic API so Playwright test suites or AI agents can directly manage the board without simulating mouse clicks.

```javascript // Discover semantic elements on the page before commenting const elements = pindrop.getCommentableElements(); // [{ selector: '#checkout-btn', label: 'Place order', rect: { x, y, width, height } }, ...]

// Drop a pin by CSS selector — returns the Comment or null if element not found const comment = pindrop.addComment({ selector: 'header > button.checkout', text: 'This button is failing the automated contrast test.', author: 'QA Bot', meta: { source: 'agent', model: 'gpt-4o' }, });

// Drop a pin by viewport coordinates (0–1) — useful when reasoning from a screenshot pindrop.addComment({ x: 0.5, y: 0.2, text: 'Header looks broken on mobile', author: 'Agent' });

// Reply to an existing comment — returns the Reply or null if (comment) { pindrop.addReply({ commentId: comment.id, text: 'Contrast ratio is 2.1:1, needs 4.5:1', author: 'QA Bot' }); }

// Resolve a comment — returns the updated Comment or null pindrop.resolveComment(comment.id, 'QA Bot'); ```

Scope Comments To Views Or Page States

If your app reuses selectors across routes, tabs, or stateful screens, you can optionally attach scope metadata to each comment when it is created and decide whether that scope is currently active.

```javascript let activeScreen = 'checkout';

const pindrop = Pindrop.init({ getScope: (element) => { const screen = element.closest('[data-screen]')?.getAttribute('data-screen'); return screen ? { screen } : undefined; }, isScopeActive: (scope) => scope.screen === activeScreen, });

// Later, when your app changes screens/state: activeScreen = 'confirmation'; pindrop.refresh(); ```

Scoped comments only render when isScopeActive(scope) returns true. Unscoped legacy comments still render normally, and comments attached to hidden elements (e.g., in a hidden tab or modal) are automatically suppressed. Call pindrop.refresh() after your app switches routes or UI states so the overlay can re-evaluate visibility.

The pindrop-cli Export Terminal

Convert offline JSON exports into readable Markdown reports straight from your terminal.

```bash npx pindrop report ./my-comments.json ``` Outputs a beautifully formatted pindrop-report.md file!


🧑‍💻 Contributing

Pull requests are welcome!

  1. npm install
  2. npm run dev to start the live development server.
  3. npm run build to compile the final library.

📄 License

MIT License