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

@sylergydigital/issue-pin-sdk

v0.6.1

Published

Issue Pin — in-app visual feedback SDK

Readme

@sylergydigital/issue-pin-sdk

In-app visual feedback SDK for Issue Pin.

Installation

1. Install

npm install @sylergydigital/issue-pin-sdk

2. Peer dependencies

Ensure these are installed in your app:

npm install react react-dom @supabase/supabase-js html2canvas lucide-react

The public GitHub repo at sylergydigital/issue-pin-sdk is a read-only mirror. Source changes should be made in the private source repo and will sync across automatically.

react-router-dom is not required. The SDK tracks window.location for thread fetching and ?highlight_thread= without router context.

Usage

The SDK is disabled by default. Pass enabled={true} (or bind it to state) to activate the feedback UI.

import { useState } from "react";
import { IssuePin } from "@sylergydigital/issue-pin-sdk";
import { supabase } from "./lib/supabase"; // your app's Supabase client

function App() {
  const [feedbackOn, setFeedbackOn] = useState(false);

  return (
    <>
      <nav>
        <button onClick={() => setFeedbackOn(f => !f)}>
          💬 Feedback
        </button>
      </nav>

      <Home />

      {/* Recommended: pass supabaseClient for automatic user identity */}
      <IssuePin
        apiKey="ew_live_..."
        enabled={feedbackOn}
        supabaseClient={supabase}
      />
    </>
  );
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | apiKey | string | — | Required. Your workspace API key | | enabled | boolean | false | Toggle feedback UI visibility without unmounting | | supabaseClient | SupabaseClient | — | Recommended. Your app's Supabase client — auto-extracts user identity from the auth session and auto-federates users into the workspace | | user.id | string | — | Manual user ID for attribution (overrides auto-detect) | | user.email | string | — | Manual user email (overrides auto-detect) | | user.displayName | string | — | Name shown on comments (overrides auto-detect) | | allowPinOnPage | boolean | true | Allow creating new element-based pins from the launcher | | allowScreenshot | boolean | true | Allow screenshot capture from the launcher | | showHints | boolean | true | Show SDK-owned instructional hints and coachmarks, including the "Open Issue Pin" toast | | scrollContainer | { key: string; ref: RefObject<HTMLElement> } | — | Store and render new element pins in a host-owned scroll container | | resolveAnchor | (target: Element) => { key: string; selector?: string \| null } \| null | — | Resolve a stable logical anchor key for clicked elements | | pinsVisible | boolean | true | Show historical thread pins independently from the rest of the SDK UI | | buttonPosition | "bottom-right" \| "bottom-left" | "bottom-right" | Floating button position | | renderLauncher | (props) => ReactNode | — | Replace the stock launcher while keeping SDK-owned state/actions | | renderModalCaptureButton | (props) => ReactNode | — | Replace the default screenshot button injected into open dialogs | | showModalCaptureButton | boolean | true | Control whether modal screenshot buttons are injected | | mode | "view" \| "annotate" | — | Controlled annotate mode — use with onModeChange | | onModeChange | (mode: FeedbackMode) => void | — | Called when annotate mode changes | | feedbackActive | boolean | — | Deprecated. Use mode: annotatetrue, viewfalse | | onFeedbackActiveChange | (active: boolean) => void | — | Deprecated. Use onModeChange |

Migration (v0.x → coordinate-first pins)

  • x_position / y_position remain the persisted source of truth.
  • Element pins now resolve in this order: anchor_keyselector → stored coordinates.
  • Without scrollContainer, element pins stay document-relative. With scrollContainer, new element pins are stored and rendered in that container's coordinate space.
  • Prefer mode / onModeChange instead of feedbackActive / onFeedbackActiveChange for clarity.
  • No <BrowserRouter> wrapper is required for the SDK. The SDK listens to history.pushState / replaceState and popstate so thread fetching and ?highlight_thread= stay in sync in SPAs without importing React Router.

TypeScript

import type { FeedbackMode } from "@sylergydigital/issue-pin-sdk";
// FeedbackMode = "view" | "annotate"

Logical anchors in a scroll container

import { useRef } from "react";
import { IssuePin, useIssuePinAnchor } from "@sylergydigital/issue-pin-sdk";

function VisitRow({ visit }: { visit: { id: string; name: string } }) {
  const ref = useIssuePinAnchor(`visit-row:${visit.id}`);
  return <div ref={ref} data-visit-id={visit.id}>{visit.name}</div>;
}

function AppShell() {
  const mainRef = useRef<HTMLDivElement>(null);

  return (
    <>
      <div ref={mainRef} style={{ overflow: "auto", height: 600 }}>
        <VisitRow visit={{ id: "123", name: "Annual review" }} />
      </div>

      <IssuePin
        apiKey="ew_live_..."
        enabled
        scrollContainer={{ key: "dashboard-main", ref: mainRef }}
        resolveAnchor={(target) => {
          const row = target.closest("[data-visit-id]");
          if (!row) return null;
          return {
            key: `visit-row:${row.getAttribute("data-visit-id")}`,
          };
        }}
      />
    </>
  );
}

Layer stacking (z-index)

UI layers use a single internal scale exported as Z (pins → overlay → popover → launcher → screenshot modal → flash). If your app uses very high z-index values (e.g. 99999 modals), raise them above Z.launcher (or import Z and position relative to it):

import { Z } from "@sylergydigital/issue-pin-sdk";

// Example: host modal above the SDK launcher
<div style={{ zIndex: Z.launcher + 1 }}>...</div>

Controlling visibility

Since enabled defaults to false, the SDK mounts its provider (preserving state and subscriptions) but renders no UI until activated. Common patterns:

Navbar toggle (recommended)

const [feedbackOn, setFeedbackOn] = useState(false);

<button onClick={() => setFeedbackOn(f => !f)}>Feedback</button>
<IssuePin apiKey="ew_live_..." enabled={feedbackOn} />

Role-based

{["qa", "admin"].includes(currentUser.role) && (
  <IssuePin apiKey="ew_live_..." enabled={feedbackOn} user={currentUser} />
)}

Environment-based

{import.meta.env.VITE_APP_ENV !== "production" && (
  <IssuePin apiKey="ew_live_..." enabled={feedbackOn} />
)}

Advanced usage

Use the built-in customization props first. Composing low-level SDK internals directly is no longer the recommended way to customize launcher behavior.

Launcher action flags

<IssuePin
  apiKey="ew_live_..."
  supabaseClient={supabase}
  enabled={feedbackOn}
  allowPinOnPage={false}
/>

If only one launcher action is enabled, the stock launcher triggers it directly instead of opening the menu.

Compose feature flags

<IssuePin
  apiKey="ew_live_..."
  enabled={feedbackEnabled}
  allowPinOnPage={pinOnPageEnabled}
  allowScreenshot={screenshotEnabled}
  pinsVisible={pinsVisible}
  showHints={false}
/>

Hide historical pins without disabling feedback

<IssuePin
  apiKey="ew_live_..."
  supabaseClient={supabase}
  enabled
  pinsVisible={showHistoricalPins}
/>

Custom launcher

<IssuePin
  apiKey="ew_live_..."
  supabaseClient={supabase}
  enabled
  renderLauncher={({ mode, canPinOnPage, canScreenshot, toggleMenu, enterPinMode, exitPinMode, startScreenshotCapture }) => (
    <div style={{ position: "fixed", right: 24, bottom: 24 }}>
      {mode === "annotate" ? (
        <button onClick={exitPinMode}>Exit pin mode</button>
      ) : canPinOnPage ? (
        <button onClick={enterPinMode}>Pin on page</button>
      ) : canScreenshot ? (
        <button onClick={() => void startScreenshotCapture()}>Screenshot</button>
      ) : (
        <button onClick={toggleMenu}>Feedback</button>
      )}
    </div>
  )}
/>

Custom modal screenshot button

<IssuePin
  apiKey="ew_live_..."
  supabaseClient={supabase}
  enabled
  renderModalCaptureButton={({ captureScreenshot }) => (
    <button
      onClick={() => void captureScreenshot()}
      style={{ position: "absolute", top: 12, right: 12 }}
    >
      Capture
    </button>
  )}
/>

Low-level composition (advanced only)

Import individual components only if you need full custom assembly:

import { FeedbackProvider, FeedbackButton, FeedbackOverlay, ThreadPins } from "@sylergydigital/issue-pin-sdk";

function App() {
  return (
    <FeedbackProvider apiKey="ew_live_...">
      <YourApp />
      <FeedbackOverlay />
      <ThreadPins />
      <FeedbackButton />
    </FeedbackProvider>
  );
}

ThreadPins renders markers at stored document-percentage coordinates and updates on scroll/resize (no CSS selector resolution).

User identity & auto-federation

Supabase auth (recommended — zero config)

If your app uses Supabase for authentication, just pass your existing client. The SDK:

  1. Auto-detects identity from auth.getSession() — no manual user props needed
  2. Auto-federates the user into the Issue Pin workspace as a commenter — they appear in the workspace member list and their comments are properly attributed
  3. Uses the dashboard org site_url for thread deep-links when a user clicks an in-app pin
import { supabase } from "./lib/supabase";

<IssuePin apiKey="ew_live_..." supabaseClient={supabase} enabled={feedbackOn} />

No backend code, edge functions, or sync secrets needed. The SDK handles everything transparently.

How auto-federation works

When a user is detected via supabaseClient:

  1. The SDK calls the sdk-federate endpoint with the API key + user identity
  2. The endpoint validates the API key, resolves the workspace, and upserts the user as a client_federated workspace member with commenter role
  3. An identity source (sdk:<workspace-slug>) is auto-created if it doesn't exist
sequenceDiagram
    participant App as Host App
    participant SDK as IssuePin SDK
    participant EF as sdk-federate (Edge Fn)
    participant DB as Database

    App->>SDK: Mount with apiKey + supabaseClient
    SDK->>SDK: auth.getSession() → extract id, email, name
    SDK->>EF: POST { apiKey, externalId, email, displayName }
    EF->>DB: SHA-256(apiKey) → resolve_api_key()
    DB-->>EF: workspace_id
    EF->>DB: Upsert user_identities
    EF->>DB: Upsert workspace_members (commenter role)
    DB-->>EF: OK
    EF-->>SDK: { success, user_id, workspace_id }
    SDK-->>App: User can now post feedback
    Note over SDK: Result cached — runs once per session

Other auth providers (manual)

For non-Supabase auth systems, pass identity props directly:

// NextAuth / Auth.js
const { data: session } = useSession();
<IssuePin apiKey="ew_live_..." user={{ email: session?.user?.email, displayName: session?.user?.name }} />
// Clerk
const { user } = useUser();
<IssuePin apiKey="ew_live_..." user={{ id: user?.id, email: user?.primaryEmailAddress?.emailAddress, displayName: user?.fullName }} />

If neither supabaseClient nor user props are provided, a console warning will remind you during development.

Server-to-server federation (advanced)

For bulk provisioning or non-Supabase apps, use the federate-user edge function with a sync_secret. See the Federation docs for details.

Security

The API key is a publishable key — safe to include in client-side code. It only grants scoped access to create threads and comments for the associated workspace, enforced server-side via Row Level Security.

AI Agent Prompt

Paste this into Claude Code, Cursor, or Codex to integrate the SDK automatically:

Add the Issue Pin feedback SDK to this React app.

Install: npm install @sylergydigital/issue-pin-sdk
Peer deps: npm install @supabase/supabase-js html2canvas lucide-react

.npmrc setup (required for GitHub Packages):
//npm.pkg.github.com/:_authToken=${GITHUB_TOKEN}
@sylergydigital:registry=https://npm.pkg.github.com

Integration:
1. Import { IssuePin } from "@sylergydigital/issue-pin-sdk"
2. Add a boolean state: const [feedbackOn, setFeedbackOn] = useState(false)
3. Place <IssuePin apiKey={import.meta.env.VITE_ISSUE_PIN_API_KEY} enabled={feedbackOn} supabaseClient={supabase} /> in your app tree (e.g. layout root)
4. Add a toggle button: <button onClick={() => setFeedbackOn(f => !f)}>💬 Feedback</button>
5. Import your app's Supabase client: import { supabase } from "./lib/supabase"

Constraints:
- enabled defaults to false; SDK mounts but renders no UI until true
- apiKey is a publishable key (safe for client-side code)
- Pass supabaseClient for automatic user identity (recommended for Supabase auth apps)
- Alternative: pass user={{ email: "[email protected]", displayName: "Name" }} for non-Supabase auth
- Optional feature flags: allowPinOnPage, allowScreenshot, pinsVisible, showHints
- Use showHints={false} if the host app wants to suppress SDK instructional coachmarks such as the "Open Issue Pin" toast