@natoe/colab 0.1.14 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -78,6 +78,13 @@ interface CollabConfig {
78
78
  theme?: Theme;
79
79
  /** Host app callbacks — colab calls these, never implements them */
80
80
  onOpenDicom?: (studyId: string, storageId: string) => void;
81
+ /**
82
+ * Open the case behind this conversation. Wired to the package's
83
+ * PatientHeader "Open" button (sibling to "View DICOM"). The host
84
+ * decides how to route — typically role-aware: lab → case page,
85
+ * radiologist → viewer, physician → order history.
86
+ */
87
+ onOpenCase?: (orderId: string, displayOrderId?: string) => void;
81
88
  onUploadFile?: (file: File | Blob, fileName?: string) => Promise<string>;
82
89
  onDeepLink?: (path: string) => void;
83
90
  onError?: (error: CollabError) => void;
@@ -256,6 +263,14 @@ interface CollabPanelProps {
256
263
  onBack?: () => void;
257
264
  /** Hide the patient name row in the header — set when a parent already shows it (e.g. CollabPopup title bar) */
258
265
  hidePatientName?: boolean;
266
+ /**
267
+ * Suppress the PatientHeader "Open" button even when the host has
268
+ * supplied `config.onOpenCase`. Set this when the panel is rendered
269
+ * for the same case the user is already on (e.g. embedded in the
270
+ * viewer's left panel) — otherwise clicking Open would re-fetch the
271
+ * current order and overwrite in-progress context.
272
+ */
273
+ hideOpenCase?: boolean;
259
274
  /**
260
275
  * Notified whenever the underlying conversation state changes (initial
261
276
  * preview load, create, channel deletion). Lets a parent like CollabPopup
@@ -280,7 +295,7 @@ interface CollabPanelProps {
280
295
  * Full collaboration panel: patient header + pinned bar + message thread + input.
281
296
  * Supports reply, pin/unpin, and (optionally) seen-by indicators.
282
297
  */
283
- declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, onConversationChange, themeMode, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
298
+ declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
284
299
 
285
300
  interface CollabPopupProps {
286
301
  /** Order ID for this conversation */
@@ -380,6 +395,20 @@ type PresenceCallback = (presences: Record<string, {
380
395
  userId: string;
381
396
  isOnline: boolean;
382
397
  }>) => void;
398
+ /**
399
+ * Server-pushed unread counts. The backend broadcasts BOTH keyings in
400
+ * one payload so consumers can render badges at either granularity (FAB
401
+ * + inbox row use `conversation`; table-row chat icons use `order`) off
402
+ * a single socket event. Legacy backends that send a flat
403
+ * `Record<string, number>` are normalised at the socket boundary into
404
+ * `{ conversation: <flat>, order: {} }` for forward compatibility.
405
+ */
406
+ interface UnreadCounts {
407
+ /** keyed by conversation id */
408
+ conversation: Record<string, number>;
409
+ /** keyed by order id — used for table-row chat-icon badges */
410
+ order: Record<string, number>;
411
+ }
383
412
  interface ConversationCallbacks {
384
413
  onMessage?: MessageCallback;
385
414
  onTyping?: TypingCallback;
@@ -433,8 +462,10 @@ declare class CollabSocket {
433
462
  connect(config: CollabConfig): void;
434
463
  /** Subscribe to user-level events (unread counts, notifications) */
435
464
  private joinUserChannel;
436
- /** Register callback for unread count changes */
437
- onUnreadCountUpdate(callback: (counts: Record<string, number>) => void): void;
465
+ /** Register callback for unread count changes. The callback receives
466
+ * the normalised {@link UnreadCounts} shape regardless of which
467
+ * payload format the backend pushed. */
468
+ onUnreadCountUpdate(callback: (counts: UnreadCounts) => void): void;
438
469
  /**
439
470
  * Join a conversation channel and subscribe to events.
440
471
  *
@@ -512,6 +543,13 @@ interface CollabContextValue {
512
543
  * mounting their own useConversation.
513
544
  */
514
545
  unreadCounts: Record<string, number>;
546
+ /**
547
+ * Same unread counts, but keyed by **order ID** instead of
548
+ * conversation ID. Useful for table-row chat-icon badges where the
549
+ * row only knows its order_id, not the conversation_id. Same socket
550
+ * push updates both maps in lock-step.
551
+ */
552
+ unreadCountsByOrder: Record<string, number>;
515
553
  /** Batched preview lookup — coalesces calls within a microtask */
516
554
  requestPreview: (orderId: string) => Promise<ConversationPreview | null>;
517
555
  /** Invalidate a cached preview (e.g. when a new message arrives) */
@@ -834,30 +872,37 @@ interface PatientHeaderProps {
834
872
  patientData: PatientData;
835
873
  participants: Participant[];
836
874
  onOpenDicom?: () => void;
875
+ /**
876
+ * Open the case behind this conversation — bound to the "Open" button
877
+ * that sits next to "View DICOM". When undefined, the button is
878
+ * hidden.
879
+ */
880
+ onOpenCase?: () => void;
837
881
  onOpenSettings?: () => void;
838
882
  /** When set, renders a back-arrow button (compact inbox returning to list) */
839
883
  onBack?: () => void;
840
- /** Suppress the channel name row — use when the name is already shown in a parent title bar */
884
+ /** Suppress the patient name row — use when the name is already shown in a parent title bar */
841
885
  hideName?: boolean;
842
886
  /** Override the displayed name — use the stored conversation.name to avoid reconstructing from patientData components */
843
887
  displayName?: string;
844
888
  className?: string;
845
889
  }
846
890
  /**
847
- * "Case card" header that sits below the window title bar. Tinted blue-50
848
- * background to set it apart from the conversation body. Renders a row
849
- * of labelled key-value columns (label uppercase on top, value below) for
850
- * the clinical context the host passed in, with the action buttons
851
- * (View DICOM + Settings) right-aligned.
891
+ * Compact case header. One zone, two rows:
892
+ *
893
+ * Row 1: [back?] Patient Name [View DICOM] [Open] [👥 4]
894
+ * Row 2: 54 · M · CT · Knee
895
+ *
896
+ * Replaces the older "case card" layout (labelled meta columns —
897
+ * Age/Sex, Modality, Body part — stacked below the name row). That
898
+ * layout cost ~70-80px of vertical real estate on every chat surface
899
+ * and pushed the message thread further down the panel. This version
900
+ * lands at ~52-60px and conveys the same identifying context inline.
852
901
  *
853
- * Used in two layouts:
854
- * - Popup path (`hideName=true`): the surrounding title bar already
855
- * shows the patient name, so we render the case card only.
856
- * - Compact-inbox path (`hideName=false`): the panel is a full-page
857
- * experience and there's no outer title, so we still show the name +
858
- * back button stacked above the case card.
902
+ * When `hideName=true` (CollabPopup wraps the conversation in its own
903
+ * window title), only the meta + actions row renders.
859
904
  */
860
- declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenSettings, onBack, hideName, displayName, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
905
+ declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenCase, onOpenSettings, onBack, hideName, displayName, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
861
906
 
862
907
  interface MessageListHandle {
863
908
  /** Scroll to a specific message by ID (used by pin jump-to) */
package/dist/index.d.ts CHANGED
@@ -78,6 +78,13 @@ interface CollabConfig {
78
78
  theme?: Theme;
79
79
  /** Host app callbacks — colab calls these, never implements them */
80
80
  onOpenDicom?: (studyId: string, storageId: string) => void;
81
+ /**
82
+ * Open the case behind this conversation. Wired to the package's
83
+ * PatientHeader "Open" button (sibling to "View DICOM"). The host
84
+ * decides how to route — typically role-aware: lab → case page,
85
+ * radiologist → viewer, physician → order history.
86
+ */
87
+ onOpenCase?: (orderId: string, displayOrderId?: string) => void;
81
88
  onUploadFile?: (file: File | Blob, fileName?: string) => Promise<string>;
82
89
  onDeepLink?: (path: string) => void;
83
90
  onError?: (error: CollabError) => void;
@@ -256,6 +263,14 @@ interface CollabPanelProps {
256
263
  onBack?: () => void;
257
264
  /** Hide the patient name row in the header — set when a parent already shows it (e.g. CollabPopup title bar) */
258
265
  hidePatientName?: boolean;
266
+ /**
267
+ * Suppress the PatientHeader "Open" button even when the host has
268
+ * supplied `config.onOpenCase`. Set this when the panel is rendered
269
+ * for the same case the user is already on (e.g. embedded in the
270
+ * viewer's left panel) — otherwise clicking Open would re-fetch the
271
+ * current order and overwrite in-progress context.
272
+ */
273
+ hideOpenCase?: boolean;
259
274
  /**
260
275
  * Notified whenever the underlying conversation state changes (initial
261
276
  * preview load, create, channel deletion). Lets a parent like CollabPopup
@@ -280,7 +295,7 @@ interface CollabPanelProps {
280
295
  * Full collaboration panel: patient header + pinned bar + message thread + input.
281
296
  * Supports reply, pin/unpin, and (optionally) seen-by indicators.
282
297
  */
283
- declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, onConversationChange, themeMode, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
298
+ declare function CollabPanel({ orderId, patientData, participantIds, showSeenBy, onBack, hidePatientName, hideOpenCase, onConversationChange, themeMode, className, style, }: CollabPanelProps): react_jsx_runtime.JSX.Element;
284
299
 
285
300
  interface CollabPopupProps {
286
301
  /** Order ID for this conversation */
@@ -380,6 +395,20 @@ type PresenceCallback = (presences: Record<string, {
380
395
  userId: string;
381
396
  isOnline: boolean;
382
397
  }>) => void;
398
+ /**
399
+ * Server-pushed unread counts. The backend broadcasts BOTH keyings in
400
+ * one payload so consumers can render badges at either granularity (FAB
401
+ * + inbox row use `conversation`; table-row chat icons use `order`) off
402
+ * a single socket event. Legacy backends that send a flat
403
+ * `Record<string, number>` are normalised at the socket boundary into
404
+ * `{ conversation: <flat>, order: {} }` for forward compatibility.
405
+ */
406
+ interface UnreadCounts {
407
+ /** keyed by conversation id */
408
+ conversation: Record<string, number>;
409
+ /** keyed by order id — used for table-row chat-icon badges */
410
+ order: Record<string, number>;
411
+ }
383
412
  interface ConversationCallbacks {
384
413
  onMessage?: MessageCallback;
385
414
  onTyping?: TypingCallback;
@@ -433,8 +462,10 @@ declare class CollabSocket {
433
462
  connect(config: CollabConfig): void;
434
463
  /** Subscribe to user-level events (unread counts, notifications) */
435
464
  private joinUserChannel;
436
- /** Register callback for unread count changes */
437
- onUnreadCountUpdate(callback: (counts: Record<string, number>) => void): void;
465
+ /** Register callback for unread count changes. The callback receives
466
+ * the normalised {@link UnreadCounts} shape regardless of which
467
+ * payload format the backend pushed. */
468
+ onUnreadCountUpdate(callback: (counts: UnreadCounts) => void): void;
438
469
  /**
439
470
  * Join a conversation channel and subscribe to events.
440
471
  *
@@ -512,6 +543,13 @@ interface CollabContextValue {
512
543
  * mounting their own useConversation.
513
544
  */
514
545
  unreadCounts: Record<string, number>;
546
+ /**
547
+ * Same unread counts, but keyed by **order ID** instead of
548
+ * conversation ID. Useful for table-row chat-icon badges where the
549
+ * row only knows its order_id, not the conversation_id. Same socket
550
+ * push updates both maps in lock-step.
551
+ */
552
+ unreadCountsByOrder: Record<string, number>;
515
553
  /** Batched preview lookup — coalesces calls within a microtask */
516
554
  requestPreview: (orderId: string) => Promise<ConversationPreview | null>;
517
555
  /** Invalidate a cached preview (e.g. when a new message arrives) */
@@ -834,30 +872,37 @@ interface PatientHeaderProps {
834
872
  patientData: PatientData;
835
873
  participants: Participant[];
836
874
  onOpenDicom?: () => void;
875
+ /**
876
+ * Open the case behind this conversation — bound to the "Open" button
877
+ * that sits next to "View DICOM". When undefined, the button is
878
+ * hidden.
879
+ */
880
+ onOpenCase?: () => void;
837
881
  onOpenSettings?: () => void;
838
882
  /** When set, renders a back-arrow button (compact inbox returning to list) */
839
883
  onBack?: () => void;
840
- /** Suppress the channel name row — use when the name is already shown in a parent title bar */
884
+ /** Suppress the patient name row — use when the name is already shown in a parent title bar */
841
885
  hideName?: boolean;
842
886
  /** Override the displayed name — use the stored conversation.name to avoid reconstructing from patientData components */
843
887
  displayName?: string;
844
888
  className?: string;
845
889
  }
846
890
  /**
847
- * "Case card" header that sits below the window title bar. Tinted blue-50
848
- * background to set it apart from the conversation body. Renders a row
849
- * of labelled key-value columns (label uppercase on top, value below) for
850
- * the clinical context the host passed in, with the action buttons
851
- * (View DICOM + Settings) right-aligned.
891
+ * Compact case header. One zone, two rows:
892
+ *
893
+ * Row 1: [back?] Patient Name [View DICOM] [Open] [👥 4]
894
+ * Row 2: 54 · M · CT · Knee
895
+ *
896
+ * Replaces the older "case card" layout (labelled meta columns —
897
+ * Age/Sex, Modality, Body part — stacked below the name row). That
898
+ * layout cost ~70-80px of vertical real estate on every chat surface
899
+ * and pushed the message thread further down the panel. This version
900
+ * lands at ~52-60px and conveys the same identifying context inline.
852
901
  *
853
- * Used in two layouts:
854
- * - Popup path (`hideName=true`): the surrounding title bar already
855
- * shows the patient name, so we render the case card only.
856
- * - Compact-inbox path (`hideName=false`): the panel is a full-page
857
- * experience and there's no outer title, so we still show the name +
858
- * back button stacked above the case card.
902
+ * When `hideName=true` (CollabPopup wraps the conversation in its own
903
+ * window title), only the meta + actions row renders.
859
904
  */
860
- declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenSettings, onBack, hideName, displayName, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
905
+ declare function PatientHeader({ patientData, participants, onOpenDicom, onOpenCase, onOpenSettings, onBack, hideName, displayName, className, }: PatientHeaderProps): react_jsx_runtime.JSX.Element;
861
906
 
862
907
  interface MessageListHandle {
863
908
  /** Scroll to a specific message by ID (used by pin jump-to) */