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

@natoe/colab

v0.1.24

Published

Real-time, study-aware collaboration UI for radiology workflows. Drop-in chat components and React hooks built on Phoenix Channels — designed to plug into a host app that already manages auth, file uploads, and DICOM viewing.

Readme

@natoe/colab

Real-time, study-aware collaboration UI for radiology workflows. Drop-in chat components and React hooks built on Phoenix Channels — designed to plug into a host app that already manages auth, file uploads, and DICOM viewing.

Install

npm install @natoe/colab
# or
pnpm add @natoe/colab
# or
yarn add @natoe/colab

Peer dependencies: react >= 18, react-dom >= 18.

Quick start

Wrap your app in CollabProvider, then drop <CollabPopup> (or one of the other surfaces) wherever you want chat to appear.

import { CollabProvider, CollabPopup } from '@natoe/colab';

function App() {
  const config = {
    socketUrl: 'wss://your-backend/socket',
    getToken: () => localStorage.getItem('jwt') ?? '',
    userId: currentUser.id,
    userRole: 'radiologist',          // 'lab' | 'radiologist' | 'physician' | 'admin'
    userName: currentUser.name,

    // Host-app callbacks — colab calls these, never implements them.
    onOpenDicom: (studyId, storageId) => openViewer(studyId, storageId),
    onUploadFile: async (file, fileName) => {
      const url = await uploadToS3(file, fileName);
      return url;
    },
    onDeepLink: (path) => router.push(path),
    onError: (err) => reportToSentry(err),
  };

  return (
    <CollabProvider config={config} apiBaseUrl="https://your-backend">
      {/* ...your app... */}

      <CollabPopup
        orderId={order.id}
        patientData={{
          orderId: order.id,
          patientName: 'Jane Doe',
          patientAge: '54',
          patientSex: 'F',
          studyType: 'CT Chest',
          studyId: order.dicomStudyId,   // PACS UID — gates the "View DICOM" button
          storageId: 'PACS',             // required alongside studyId
        }}
        participantIds={[labUserId, radiologistId, adminId]}
        isOpen={chatOpen}
        onClose={() => setChatOpen(false)}
      />
    </CollabProvider>
  );
}

What's in the box

Surfaces (pick one or compose)

| Component | Use when… | | --------------- | ------------------------------------------------------------------------ | | CollabPopup | Floating, draggable chat window — typical for table-row "Open chat". | | CollabPanel | Full-bleed panel, e.g. side-docked next to a viewer. | | CollabInline | Last-N-messages preview embedded directly in a table row. | | CollabInbox | WhatsApp-style two-pane inbox: conversation list + active conversation. |

Provider

CollabProvider owns the socket connection, identity, and host callbacks. Wrap your app once near the root.

Hooks (for custom UI)

  • useCollab — read config, socket, error stream
  • useConversation — single conversation: messages, typing, send/edit/delete
  • useConversationList — inbox list with unread counts and last activity
  • useMessages — paginated message history with realtime appends
  • useInlineCollab — batch preview fetcher for table rows
  • useUnreadCount — total unread badge for nav bars
  • useChannelSettings, usePinnedMessages, useDeepLinks, useAudioRecorder

Composable parts

If the prebuilt surfaces don't fit, compose your own from ConversationList, MessageList, MessageBubble, MessageInput, PatientHeader, ChannelSettings, etc.

CollabConfig reference

| Field | Type | Notes | | ------------- | ----------------------------------------------- | ------------------------------------------------ | | socketUrl | string | Phoenix WebSocket endpoint. | | getToken | () => string | Returns the current JWT. Re-read each connect. | | userId | string | Must match your backend's user identity. | | userRole | 'lab' \| 'radiologist' \| 'physician' \| 'admin' | | | userName | string | Displayed in messages and presence. | | userAvatar | string? | Optional avatar URL. | | onOpenDicom | (studyId, storageId) => void | Opens the host's DICOM viewer. Gates the button. | | onUploadFile| (file, fileName?) => Promise<string> | Returns a public URL after upload. | | onDeepLink | (path: string) => void | Resolves natoe://... links inside messages. | | onError | (err: CollabError) => void | Centralized error sink. |

PatientData reference

Conversations are study-aware. Pass a PatientData object per chat surface so the header and "View DICOM" button render with the right context.

| Field | Required | Notes | | -------------------- | -------- | -------------------------------------------------------- | | orderId | yes | Stable per-case identifier. | | patientName | yes | | | studyId | no | PACS study UID. Pair with storageId to show "View DICOM". | | storageId | no | e.g. 'PACS'. Required alongside studyId. | | patientAge, patientSex, studyType, bodyParts, referringPhysician, labName, displayOrderId | no | Header chrome. |

How conversations are created

A conversation is materialized the first time someone sends a message — empty chats don't clutter the inbox. After that first message, anyone with access to the case can join.

Who can send the first message:

  • Lab / Admin — any case
  • Radiologist — only after being assigned
  • Physician — only after the case is completed

When a new role joins (e.g. an assigned radiologist), they see the full backlog from before they joined.

Message types

Text, voice (record-and-send), images, files (up to 20 MB), system events, and deep links (natoe://...) that the host app resolves via onDeepLink.

Key features

  • Reply to a specific message (frozen quote snapshot)
  • Pin up to 3 messages per channel
  • Read receipts ("Seen by …")
  • Typing indicators (debounced)
  • Unread counts per channel and total
  • Channel settings: rename, picture, add/remove participants

TypeScript

Fully typed. All public types are exported from the root:

import type {
  CollabConfig,
  PatientData,
  Conversation,
  Message,
  Participant,
  CollabError,
} from '@natoe/colab';