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

@choirhq/widget

v0.1.6

Published

Embeddable Sela chat widget for Choir commerce partners — one component, ticket → chat → payment link → confirmation, and the merchant sees the whole conversation in Choir Inbox.

Readme

@choirhq/widget

Embeddable Sela chat for Choir commerce partners. Drop it into any site — a ticket landing page, an event storefront, a product catalog — and visitors can chat with Sela, receive a Paystack link, and buy inside the widget. The merchant sees the whole conversation in Choir Inbox on web + mobile as it happens.

Ships two entry points:

  • React — import { ChoirChat } from '@choirhq/widget'
  • Vanilla <script> — Choir.chat.mount('#el', {...})

Install

npm install @choirhq/widget

React usage

import { ChoirChat } from '@choirhq/widget';

export default function TicketPage({ ticket }) {
  return (
    <ChoirChat
      baseUrl="https://api.choirworkspace.com"
      // Recommended: the workspace slug — human-readable, stable, and
      // already public via the CAP JWKS URL. Prefer this over
      // `workspaceId` (which still works if you already wired the UUID).
      workspaceSlug="miss-intercontinental-zambia"
      catalogProductId={ticket.id}
      placeholder={`Ask us anything about the ${ticket.name}…`}
      onOrderPaid={(order) => {
        // Fires when the customer's Paystack checkout completes.
        // Widget itself will keep showing the confirmation card; use
        // this to navigate to your own receipt page if you have one.
        window.location.href = `/tickets/${order.order_id}`;
      }}
    />
  );
}

Vanilla usage

<script src="https://cdn.jsdelivr.net/npm/@choirhq/widget/dist/bootstrap.js" defer></script>

<div id="choir-chat" style="max-width:480px;margin:0 auto"></div>
<script>
  Choir.chat.mount('#choir-chat', {
    baseUrl: 'https://api.choirworkspace.com',
    // Recommended: the workspace slug. `workspaceId` (UUID) still works.
    workspaceSlug: 'miss-intercontinental-zambia',
    catalogProductId: 'tkt_grand_finale_vip',
    onOrderPaid: (order) => {
      window.location.href = '/tickets/' + order.order_id;
    },
  });
</script>

Props

| Prop | Type | Notes | |---|---|---| | baseUrl | string — required | Choir API root, e.g. https://api.choirworkspace.com. | | workspaceSlug | string — recommended | Human-readable workspace slug (e.g. miss-intercontinental-zambia). Same value that appears in the CAP JWKS URL. Prefer this over workspaceId. | | workspaceId | string — alternative | UUID from Workspaces → Settings → Advanced. Kept for partners who already wired the id. Pass at least one of workspaceSlug or workspaceId. | | catalogProductId | string — optional | If the visitor clicked a specific ticket, pass the product id. Sela's first reply confirms that exact ticket instead of re-asking what they want. | | customerHint | { externalSource, externalRef, displayName? } — optional | Partner-supplied identity. A repeat visit under the same identity resumes the same customer + conversation. | | onOrderPaid | (order) => void — optional | Fires on the COMMERCE_ORDER_PAID socket event. Typical use: navigate to a receipt / ticket-PDF page. | | onError | (err) => void — optional | Unrecoverable errors (network down, token refused). Widget also shows an inline banner. | | placeholder | string — optional | Composer placeholder text. | | theme | 'light' \| 'dark' \| 'auto' — optional (default 'auto') | Colour scheme. Auto respects prefers-color-scheme. |

What the visitor sees

Right-aligned Concord-indigo bubbles for their own messages. Left- aligned warm-cream bubbles for Sela and any merchant relay. Payment link renders as a centred card with an "Open checkout →" button; payment received arrives as a green confirmation card. All matches the merchant-side Commerce Conversation view on mobile — customer and merchant see the same shapes.

What the merchant sees

Every widget conversation lands in the existing Choir Commerce Inbox (/workspaces/<id>/commerce on web, /commerce on mobile) as a row with source WEB. Nothing new to install merchant-side; they reply from the same inbox they already use for WhatsApp / Instagram leads.

Security

Widget sessions get a short-lived scoped JWT (15 min) that grants subscribe-only rights to one conversation and send-as-that-one- customer rights. It's never a Choir user token. The site never handles Choir user credentials; the widget never handles anything more sensitive than the browser's own session cookie.

Version

0.1.3 — current stable. What ships in 0.1.x:

  • 0.1.0 — text chat + payment-link display + onOrderPaid callback + repeat-visit resume via localStorage.
  • 0.1.1 — image / file uploads from the visitor (the attach button in the composer; same OCR + attach_payment_proof pipeline as WhatsApp inbound); inline ticket QR rendering on paid receipts (client-side SVG via qrcode, no WhatsApp media URL dependency).
  • 0.1.2 — React is now correctly externalized from the library build. 0.1.0 and 0.1.1 bundled React into dist/index.*.js, which crashed with "Invalid hook call" whenever the host app also had its own React tree. If you tried 0.1.0 or 0.1.1, upgrade.
  • 0.1.3 — bootstrap emits an IIFE instead of an ES module, so the documented <script src=".../bootstrap.js"> drop-in works under a bare <script> tag (no type="module" required). 0.1.1 and 0.1.2 emitted ESM here and needed type="module" to run. Also ships @types/qrcode as a devDep (was leaking into dependencies).
  • 0.1.4 — workspaceSlug prop as an alternative to workspaceId. The slug is the stable, public identifier already exposed via the CAP JWKS URL, and it survives a UUID rotation. Backend accepts either; slug wins when both are provided. workspaceId remains fully supported.
  • 0.1.6 — three visitor-experience fixes, needs the paired backend deploy (source: 'web' autonomous dispatch + widget-send client_ref echo) to be fully effective. Sela's autonomous reply now reaches the visitor over the socket (previously dispatchAutonomous had no web branch and posted a "channel doesn't support autonomous send" handoff instead). The widget drops operator-facing system messages (sela_handoff, widget_context, drafts) instead of rendering them as if they were Sela replies. And a client_ref correlation id makes the send flow reconcile the optimistic bubble with the server echo, so the visitor's own message doesn't double-render.
  • 0.1.5 — Socket.IO client now connects to the /ws namespace (same one the mobile app has used since day one). 0.1.0–0.1.4 connected to the default namespace, which Engine.IO accepts but where no gateway handlers are bound; subscribe_channel never got an ack and no Sela reply ever reached the widget. Anyone on ≤ 0.1.4 must upgrade — visible symptom was a UI stuck on "Reconnecting…" with no incoming messages.

Roadmap:

  • 0.2: customerHint identity merge across devices (Google OAuth handoff).
  • 0.3: partial hosted-page fallback for very restrictive CSP hosts.
  • 0.4: analytics events into existing metrics pipeline + workspace-level rate limiting on session bootstrap.

Getting your workspace_id

Log into choirworkspace.com, open your workspace, Settings → Advanced. The UUID under "Workspace ID" is what you pass to workspaceId.

License

MIT — see LICENSE at the repo root.