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

beads-map

v0.3.6

Published

Interactive dependency graph viewer for beads (bd) issues

Downloads

1,418

Readme

beads-map

Interactive dependency graph viewer for beads (bd) issues.

beads-map showing a multi-project dependency graph

See your entire project's issues, epics, and dependencies as a live, explorable graph. beads-map auto-discovers your .beads/ directory and renders everything instantly -- no configuration needed.


Features

Graph Visualization

  • 5 graph layouts -- Switch between layout modes via the toolbar:
    • Force -- Organic force simulation with collision avoidance and variable link distances
    • DAG -- Clean top-down directed acyclic graph with topological layering
    • Radial -- Concentric rings by dependency depth. Root nodes (no blockers) sit at center, deeper dependencies on outer rings. Ring spacing scales with node count.
    • Cluster -- Groups nodes spatially by project prefix. Each prefix gets its own cluster center arranged in a circle. Cross-project dependencies stretch visibly between clusters.
    • Spread -- Like Force but maximally spaced for readability and screenshots. Stronger repulsion, wider link distances.
  • Collapse / Expand -- Collapse all epics at once with a single button, or right-click individual epics to collapse/uncollapse them. Shows collapsed task count on each epic node.
  • Visual encoding -- Node size = dependency importance (connection count), fill color = configurable via legend (see below), ring color = project prefix (Catppuccin Latte palette). Larger nodes are more connected; epics get a size boost.
  • Legend color modes -- Switch node fill color between 5 modes via the bottom-right legend panel:
    • Status (default) -- Open (green), In Progress (amber), Blocked (red), Deferred (zinc), Closed (emerald)
    • Priority -- P0 Critical (red), P1 High (orange), P2 Medium (blue), P3 Low (zinc), P4 Backlog (zinc-300)
    • Owner -- Color by createdBy field using Catppuccin Latte accent palette (14 colors)
    • Assignee -- Color by assignee field using Catppuccin Latte accent palette
    • Prefix -- Color by project prefix using Catppuccin Latte accent palette
  • Catppuccin Latte palette -- All prefix-colored elements (node rings, cluster circles, tooltip accent bars, prefix fill mode) use the Catppuccin Latte accent palette (14 saturated colors optimized for light backgrounds). Person/prefix mapping is deterministic via FNV-1a hash.
  • Dependency arrows -- Solid emerald arrows with flow particles for blocking relationships, dashed zinc lines for parent-child hierarchy. Curved links with configurable curvature.
  • Semantic zoom -- When zooming out, individual nodes smoothly fade and are replaced by epic cluster labels at each cluster's centroid. Clusters show the epic title, ID, and member count, surrounded by a dashed circle in the project's prefix color. Cluster visibility can be toggled with the "Clusters" button in the top-left toolbar.
  • Spawn & exit animations -- New nodes pop in with an overshoot easing (easeOutBack), removed nodes shrink out, and status changes trigger a ripple animation. New links flash bright emerald on arrival.
  • Hover tooltips -- Hover over any node to see a tooltip card with the project prefix, issue ID, title, creation date, blocker list, priority, owner, and assignee. Smart viewport clamping (prefers above cursor, flips below).
  • Resizable minimap -- Always-visible minimap (bottom-left) showing all nodes, links, claimed avatars, and the current viewport rectangle. Click to navigate. Drag the top, right, or top-right corner handles to resize (100-500px wide, 80-400px tall).

Live Updates

  • SSE streaming -- File watchers detect changes to issues.jsonl (and all additional repo JSONL files) and push updates via Server-Sent Events. No refresh needed.
  • Diff/merge pipeline -- Incoming data is diffed against current state (diffBeadsData) and merged with position preservation (mergeBeadsData) so the graph layout doesn't reset. Animation metadata (_spawnTime, _removeTime, _changedAt) is stamped during merge.

Timeline Replay

  • Step-based playback -- Replay the entire history of your project as a step-by-step animation. Each step corresponds to one temporal event (issue creation, dependency addition).
  • Play/pause controls -- Play, pause, and scrub through the timeline with a slider.
  • Speed toggle -- 1x, 2x, 4x playback speed (2 seconds per event at 1x).
  • Uses the same diff/merge pipeline as live updates, so nodes get proper positions and spawn animations during replay.

Search

  • Smart search -- Cmd/Ctrl+F to fuzzy search across all issues by ID, title, project prefix, owner, assignee, or commenter username. Keyboard navigation through results.

Right-Click Context Menu

  • Multi-action menu -- Right-click any node to open a context menu with actions:
    • Show description -- Opens a full-screen modal with the issue's markdown description
    • Add comment -- Opens the comment tooltip for posting
    • Claim task -- Posts a claim comment (@handle) to mark yourself as working on the issue (only shown when authenticated and node is unclaimed)
    • Unclaim task -- Removes your claim from the node (only shown when you are the claimant)
    • Collapse/Uncollapse epic -- Toggle individual epic collapse on right-click (only shown on epic nodes)

Task Claiming

  • Claim tasks -- Right-click a node and select "Claim task" to mark yourself as working on it. Posts a special @handle comment via ATProto.
  • Avatar badges -- Claimed nodes display the claimant's circular avatar at the bottom-right of the node on the graph canvas. Avatars are drawn at constant screen-space size and also appear on the minimap.
  • Avatar hover tooltip -- Hover over a claimed node's avatar to see the claimant's profile picture, handle, and when they claimed it (relative time).
  • Optimistic UI -- Claims and unclaims update immediately in the UI before the server round-trip completes. Optimistic claims show the avatar instantly; optimistic unclaims suppress it instantly.
  • Unclaim -- Remove your claim via the context menu. Deletes the underlying ATProto comment record.

Node Detail Sidebar

  • Click-to-inspect -- Click any node to open a detail sidebar with:
    • Issue ID, title, type icon, status/priority/prefix badges
    • GitHub repository link (auto-detected from git remote, shown as clickable badge + URL)
    • Metrics grid: blocks count, dependent count
    • Blocker and dependent lists with clickable navigation
    • Dates: created, updated, closed (with hour:minute precision)
    • Owner attribution
    • Full description rendered as Markdown (with GFM support)
    • Copy-to-clipboard button for descriptions (copies raw markdown with a header showing project prefix, issue ID, and GitHub repo URL)
    • Threaded comment section (see below)
  • Description modal -- "View in window" opens a full-screen modal with the rendered description and a copy button. Also accessible via right-click context menu "Show description".
  • Mobile drawer -- On small screens, the detail panel slides up as a bottom drawer instead of a sidebar.

ATProto Authentication & Comments

  • OAuth 2.0 login -- Sign in with your Bluesky/ATProto account via OAuth 2.0 with PKCE. Avatar and handle shown in the header. Dual mode: public client for dev, confidential (ES256 JWK) for production.
  • Comment annotations -- Right-click any node to post a comment via ATProto (org.impactindexer.review.comment lexicon). Comments stored on the AT Protocol, fetched from the Hypergoat GraphQL indexer.
  • Threaded replies -- Comments support nested replies (replyTo field). Root comments sorted newest-first, replies sorted chronologically. Rendered with indentation (ml-4 pl-3 border-l).
  • Likes -- Heart-toggle likes on comments (org.impactindexer.review.like lexicon). Rose-colored when liked by the current user.
  • Delete -- Delete your own comments and likes.
  • Comment badges -- Nodes with comments show a red notification badge with the comment count on the graph canvas.
  • All Comments panel -- Slide-in sidebar showing all comments across all nodes with threaded replies, clickable node navigation pills, and like/delete actions.

Multi-Repo Support

  • Automatic aggregation -- If your .beads/config.yaml lists additional repositories, beads-map loads all of them into a unified graph.
  • Per-project colors -- Each project prefix gets a deterministic color from the Catppuccin Latte palette (14 accent colors). Shown as node rings, prefix badges, cluster borders, and tooltip accent bars.
  • GitHub repo links -- Auto-detects git remote URLs for each repository (primary + additional). Shown in the node detail card as a clickable GitHub link. Also included in copied description text.

Header / Navbar

  • Frosted glass header -- bg-white/95 backdrop-blur-sm sticky header inspired by plresearch.org.
  • Animated logo -- BeadsLogo component: an animated hexagonal network SVG with rotating concentric hexagons, pulsing vertex nodes, and a breathing center node -- representing the dependency graph.
  • Pill navigation -- Replay and Comments toggle buttons styled as rounded-full pill items. Auth button with rounded avatar dropdown.
  • Centered search -- Rounded-full search bar with keyboard shortcut hint and dropdown results panel.

Info Panel & Legend

  • Bottom-right info panel -- Shows issue count, dependency count, project count, color mode selector (Status / Priority / Owner / Assignee / Prefix), dynamic legend dots for the active mode, and visual encoding hints. Hidden during timeline replay. Legend shows only items present in visible nodes for owner/assignee/prefix modes.

Quick Start

cd ~/my-project
npx beads-map@latest

beads-map walks up from your current directory to find .beads/, just like git finds .git/.

With an explicit path

npx beads-map@latest --beads-dir ~/projects/my-project/.beads

Multi-repo aggregation

If your .beads/config.yaml lists additional repositories, beads-map automatically loads all of them:

# .beads/config.yaml
repos:
  additional:
    - ../backend
    - ../frontend
    - ../shared-lib

CLI Reference

beads-map [options]

Options:
  --port <number>       Port to serve on (default: 3000)
  --beads-dir <path>    Explicit .beads/ directory path
  --dev                 Run in development mode (hot reload)
  --help, -h            Show this help message

Environment:
  BEADS_DIR             Override .beads/ discovery (same as --beads-dir)

Examples:
  npx beads-map@latest                                    # Auto-discover from cwd
  npx beads-map@latest --port 4000                        # Custom port
  npx beads-map@latest --beads-dir ~/projects/hub/.beads  # Explicit path
  BEADS_DIR=../.beads npx beads-map@latest --dev          # Dev mode with env var

Development

git clone https://github.com/GainForest/beads-map.git
cd beads-map
pnpm install

# Run in dev mode against a beads project
BEADS_DIR=~/path/to/.beads pnpm dev

# Build for production
pnpm build

Quality gate

pnpm build must pass with zero errors before committing. There are no tests yet -- the build is the sole gate.


Architecture

beads-map/
├── app/
│   ├── page.tsx                  # Main page: SSE wiring, merge logic, search, layout, comments, claims
│   ├── layout.tsx                # Root layout, wraps in AuthProvider
│   ├── globals.css               # Timeline slider styles, markdown prose, scrollbar
│   └── api/
│       ├── beads/
│       │   ├── route.ts          # GET /api/beads -- one-shot full data load
│       │   └── stream/route.ts   # GET /api/beads/stream -- SSE live updates
│       ├── config/route.ts       # GET /api/config -- project name, repo count, repo URLs
│       ├── login/route.ts        # POST /api/login -- initiate ATProto OAuth
│       ├── logout/route.ts       # POST /api/logout -- clear session
│       ├── status/route.ts       # GET /api/status -- current auth state
│       ├── records/route.ts      # POST/PUT/DELETE /api/records -- ATProto record CRUD
│       └── oauth/
│           ├── callback/route.ts         # OAuth callback handler
│           ├── client-metadata.json/     # OAuth client metadata
│           └── jwks.json/route.ts        # JSON Web Key Set
├── components/
│   ├── AllCommentsPanel.tsx      # Slide-in panel: all comments across nodes, threaded
│   ├── AuthButton.tsx            # Sign-in modal + avatar dropdown (rounded-full pill style)
│   ├── BeadsGraph.tsx            # Force graph: paintNode/paintLink, minimap, semantic zoom, avatars
│   ├── BeadsLogo.tsx             # Animated hexagonal network SVG logo
│   ├── BeadTooltip.tsx           # Hover tooltip: prefix, ID, title, date, blockers, priority, owner
│   ├── CommentTooltip.tsx        # Floating right-click comment tooltip
│   ├── ContextMenu.tsx           # Right-click context menu: description, comment, claim/unclaim
│   ├── DescriptionModal.tsx      # Full-screen markdown description modal with copy button
│   ├── HeartIcon.tsx             # Shared heart SVG (outline/filled)
│   ├── NodeDetail.tsx            # Sidebar detail: metadata, deps, comments, repo link
│   ├── TimelineBar.tsx           # Timeline replay: play/pause, scrubber, speed toggle
│   ├── GraphStats.tsx            # Issue count statistics widget (unused, inlined in BeadsGraph)
│   └── StatusLegend.tsx          # Color legend for statuses (unused, inlined in BeadsGraph)
├── hooks/
│   └── useBeadsComments.ts       # Fetch comments + likes from Hypergoat, build thread trees
├── lib/
│   ├── auth.tsx                  # AuthProvider context + useAuth hook
│   ├── auth/client.ts            # OAuth client factory (public/confidential mode)
│   ├── agent.ts                  # Authenticated ATProto agent from session
│   ├── session.ts                # iron-session encrypted cookie setup
│   ├── env.ts                    # Environment variable validation
│   ├── discover.ts               # .beads/ auto-discovery + git remote URL detection
│   ├── parse-beads.ts            # JSONL parser, multi-repo hydration, graph builder
│   ├── types.ts                  # GraphNode, GraphLink, ColorMode, Catppuccin palette, color helpers
│   ├── diff-beads.ts             # Diff engine for detecting added/removed/changed nodes/links
│   ├── watch-beads.ts            # fs.watch wrapper with debounce for JSONL file changes
│   ├── timeline.ts               # buildTimelineEvents + filterDataAtTime for replay
│   └── utils.ts                  # formatRelativeTime, buildDescriptionCopyText, shared utilities
├── bin/
│   └── beads-map.mjs             # CLI entry point
├── scripts/
│   └── generate-jwk.js           # ES256 JWK key generation for OAuth
└── public/
    └── image.png                 # Screenshot for README

Data flow

  1. Discovery -- lib/discover.ts walks up from cwd (or reads BEADS_DIR) to find .beads/. Also detects git remote URLs for all repos.
  2. Parsing -- lib/parse-beads.ts reads issues.jsonl from the primary repo and any additional repos in config.yaml, deduplicates, extracts dependencies, and builds a graph structure with computed fields (blocker/dependent counts, prefix colors).
  3. Rendering -- components/BeadsGraph.tsx uses react-force-graph-2d with fully custom canvas rendering (paintNode, paintLink). All transient state (hover, selection, comments, claimed avatars) stored in refs to avoid re-rendering the force simulation.
  4. Live updates -- app/api/beads/stream/route.ts opens an SSE connection, watches all JSONL files, and pushes full data on each change. The client diffs and merges with position preservation.
  5. Timeline -- lib/timeline.ts extracts sorted temporal events from the data. During replay, filterDataAtTime() produces a time-slice that flows through the same diff/merge pipeline as live updates.
  6. Comments -- hooks/useBeadsComments.ts queries the Hypergoat GraphQL indexer for comments and likes, resolves profiles via public.api.bsky.app, and builds threaded trees client-side.
  7. Claims -- A claim is a comment with text @handle (starts with @, no spaces). The claimedNodeAvatars useMemo in page.tsx scans all comments to build a Map<nodeId, claimInfo>. Avatars are drawn on canvas nodes and the minimap by paintNode via claimedNodeAvatarsRef.

Key design decisions

  • paintNode/paintLink use refs, not props -- Callbacks have [] dependencies and read from selectedNodeRef, hoveredNodeRef, claimedNodeAvatarsRef, etc. This prevents re-creating the ForceGraph component on every interaction.
  • Position preservation is critical -- react-force-graph-2d mutates node objects in-place (x, y, vx, vy). The merge logic copies these from old nodes to new nodes to prevent layout resets.
  • Animation metadata convention -- Fields prefixed with _ on nodes/links (_spawnTime, _removeTime, _changedAt, _prevStatus) are transient, set by mergeBeadsData(), consumed by paintNode/paintLink, and garbage-collected after 600ms.
  • Bootstrap trick -- The graph starts in DAG mode for 15ms to spread nodes into good positions, then auto-switches to Force mode. This gives the organic layout a clean starting arrangement.
  • Link source/target normalization -- filterDataAtTime() must spread new link objects with string source/target (not the original mutated object refs from d3-force). Without this, links draw to wrong positions during timeline replay.
  • Optimistic claim/unclaim -- Two state variables: optimisticClaims (Map) for immediate avatar display, optimisticUnclaims (Set) for immediate avatar suppression. Both reconciled with comment-derived data in claimedNodeAvatars useMemo.
  • Avatar image cache -- Module-level avatarImageCache in BeadsGraph.tsx. No crossOrigin attribute on Image elements (Bluesky CDN CORS issue). getAvatarImage() returns cached HTMLImageElement or starts loading and returns null.
  • Portal for modals -- DescriptionModal uses createPortal(jsx, document.body) with z-[100] to escape any parent stacking contexts.

Tech Stack


License

MIT