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

@alive-game/alive-tagger

v0.2.1

Published

Element selection and source tagging for Alive workbench

Readme

@alive-game/alive-tagger

Element selection and source tagging for Alive workbench. Enables click-to-select UI elements with source file information, allowing Claude to know exactly which component and file the user is referring to.

Features

  • JSX Source Tagging: Attaches source file info (fileName, lineNumber, columnNumber) to every DOM element at build time
  • Element Selection: Client script for click-to-select element selection with visual feedback (activated by the workbench select button)
  • PostMessage Bridge: Sends selected element context to parent frame for Claude integration

Installation

# In your Vite React project
bun add @alive-game/alive-tagger

Usage

Vite Plugin (Required)

Add the plugin to your vite.config.ts:

import { aliveTagger } from "@alive-game/alive-tagger"

export default defineConfig(({ mode }) => ({
  plugins: [
    react(),
    mode === "development" && aliveTagger()
  ].filter(Boolean),
}))

Client Script (Optional - for iframe selection UI)

The client script provides click-to-select with visual feedback. It's automatically initialized when imported:

// In your app entry point (only needed if in iframe)
import "@alive-game/alive-tagger/client"

Or inject via HTML:

<script type="module">
  import { initAliveTagger } from "@alive-game/alive-tagger/client"
  initAliveTagger()
</script>

How It Works

1. JSX Source Tagging (Vite Plugin)

The plugin intercepts React's jsx-dev-runtime and wraps the jsxDEV function to inject refs that store source information on DOM nodes:

// Every DOM element gets this attached via Symbol:
element[Symbol.for("__aliveSource__")] = {
  fileName: "client/pages/Index.tsx",
  lineNumber: 42,
  columnNumber: 8,
  displayName: "Button"
}

// Global map for reverse lookups:
window.aliveSourceMap = Map<string, Set<WeakRef<Element>>>

2. Element Selection (Client Script)

When running in an iframe:

  1. Activate: The parent frame posts TAGGER_ACTIVATE (the workbench select button); cursor becomes crosshair
  2. Hover: Elements with source info get highlighted with green border and label showing ComponentName · path/to/file.tsx:line
  3. Click: Captures element context and sends to parent frame via postMessage

3. PostMessage Communication

Selected element context is sent to the parent frame:

window.parent.postMessage({
  type: "alive-element-selected",
  context: {
    fileName: "client/pages/Index.tsx",
    lineNumber: 42,
    columnNumber: 8,
    displayName: "Button",
    html: "<button class='...'>Click me</button>",
    tagName: "button",
    className: "btn-primary",
    id: "submit-btn",
    parentComponents: ["Form", "Page"]
  }
}, "*")

API Reference

Plugin Options

interface AliveTaggerOptions {
  /** Enable source tagging (default: true in dev mode) */
  enabled?: boolean
  /** Enable debug logging */
  debug?: boolean
}

aliveTagger({ enabled: true, debug: false })

Types

import {
  SOURCE_KEY,                    // Symbol.for("__aliveSource__")
  ELEMENT_SELECTED_MESSAGE_TYPE, // "alive-element-selected"
  type SourceInfo,
  type ElementSelectedContext,
  type ElementSelectedMessage,
  type AliveTaggerOptions,
} from "@alive-game/alive-tagger"

Utility Functions

import {
  getElementSource,      // Get source info from element
  hasSourceInfo,         // Check if element has source info
  formatSourceLocation,  // Format as "file.tsx:42:8"
  isElementSelectedMessage, // Type guard for messages
} from "@alive-game/alive-tagger"

// Example usage
const source = getElementSource(document.querySelector("button"))
if (source) {
  console.log(formatSourceLocation(source)) // "client/pages/Index.tsx:42:8"
}

Client Functions

import { initAliveTagger } from "@alive-game/alive-tagger/client"

// Initialize selector (auto-called on import, but can be manual)
const cleanup = initAliveTagger()

// Later: cleanup()

Architecture

┌─────────────────────────────────────────────────────────────────────────┐
│  Alive (terminal.alive.best)                                    │
│  ┌──────────────────────┐  ┌──────────────────────────────────────────┐ │
│  │  Chat Interface      │  │  Workbench Component                       │ │
│  │                      │  │  - Listens for 'alive-element-selected'  │ │
│  │  Shows selected      │◀─┤  - Displays source info to user          │ │
│  │  element context     │  │  - Injects into next Claude message      │ │
│  └──────────────────────┘  └──────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────────┘
                                        ▲
                                        │ postMessage
┌─────────────────────────────────────────────────────────────────────────┐
│  User Site (in preview iframe)                                          │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │  alive-tagger plugin (Vite)                                       │  │
│  │  - Intercepts jsx-dev-runtime                                     │  │
│  │  - Attaches Symbol.for('__aliveSource__') to every DOM element    │  │
│  │  - Maintains window.aliveSourceMap for lookups                    │  │
│  └───────────────────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │  alive-tagger/client (optional)                                   │  │
│  │  - Handles click-to-select (button-activated)                     │  │
│  │  - Visual highlight on hover                                      │  │
│  │  - Posts context to parent frame                                  │  │
│  └───────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘

Comparison: alive-tagger vs React Grab

| Feature | alive-tagger | React Grab | |---------|-------------|------------| | Approach | Vite plugin + client | Runtime fiber traversal | | Data attachment | Build-time via refs | Runtime fiber inspection | | Symbol key | __aliveSource__ | __jsxSource__ | | Selection UI | Built-in (button-activated click) | Built-in | | Communication | postMessage | HTTP localhost:4567 | | Env requirement | None (dev mode auto) | None | | Designed for | Alive iframe | Claude Agent SDK CLI |

Integration with Alive

The alive-tagger is integrated with Alive's workbench preview. When a user activates the select button and clicks an element:

  1. Workbench receives postMessage: Workbench.tsx and WorkbenchMobile.tsx listen for alive-element-selected messages
  2. Context propagates via WorkbenchContext: The selection is passed through useWorkbenchContext()
  3. Chat input auto-fills: The selected element is formatted as @ComponentName in src/path/file.tsx:lineNumber and inserted into the chat input
  4. User can send to Claude: The reference helps Claude understand exactly which element/component to modify

Key files:

  • apps/web/features/chat/lib/workbench-context.tsx - Context with setSelectedElement and registerElementSelectHandler
  • apps/web/features/chat/components/workbench/Workbench.tsx - Listens for postMessage, calls setSelectedElement
  • apps/web/app/chat/page.tsx - Registers handler to insert selection into chat input

Caddy Configuration

For HMR to work properly in the workbench iframe, the preview domain must NOT use forward_auth. The auth check interferes with WebSocket connections and causes the iframe to flash/reload every few seconds.

Preview domain Caddy configs must be generated WITHOUT forward_auth to avoid this issue.

Development

# Build
bun run build

# Watch mode
bun run dev

# Type check
bun run type-check

# Run tests
bun run test

Files

packages/alive-tagger/
├── src/
│   ├── index.ts      # Main exports (plugin + utilities)
│   ├── plugin.ts     # Vite plugin
│   ├── client.ts     # Client selection script
│   └── types.ts      # TypeScript types
├── dist/             # Built output
├── package.json
├── tsconfig.json
└── tsup.config.ts

Security Considerations

  1. Development only: Source tagging only active in dev mode
  2. No production impact: Plugin does nothing in production builds
  3. File paths: Relative paths exposed (not full system paths)
  4. node_modules filtered: Library internals not tagged

License

MIT