@natoe/colab 0.1.12 → 0.1.13

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.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import React4, { createContext, forwardRef, useRef, useImperativeHandle, useEffect, useCallback, useState, useLayoutEffect, useContext, useId, useMemo } from 'react';
1
+ import React4, { createContext, forwardRef, useRef, useImperativeHandle, useLayoutEffect, useEffect, useCallback, useState, useContext, useId, useMemo } from 'react';
2
2
  import { Socket, Presence } from 'phoenix';
3
3
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
4
4
  import { createPortal } from 'react-dom';
@@ -315,6 +315,173 @@ var CollabSocket = class {
315
315
  this.config = null;
316
316
  }
317
317
  };
318
+
319
+ // src/core/theme.ts
320
+ var FONT_SIZE = {
321
+ xs: "12px",
322
+ sm: "13px",
323
+ md: "15px",
324
+ lg: "17px",
325
+ xxl: "28px"
326
+ };
327
+ var FONT_WEIGHT = {
328
+ regular: 400,
329
+ medium: 500,
330
+ semibold: 600,
331
+ bold: 700
332
+ };
333
+ var LINE_HEIGHT = {
334
+ tight: 1.3,
335
+ normal: 1.45};
336
+ var SPACE = {
337
+ S1: "4px",
338
+ S2: "8px",
339
+ S3: "12px",
340
+ S4: "16px",
341
+ S5: "20px",
342
+ S6: "24px",
343
+ S12: "48px"
344
+ };
345
+ var RADIUS = {
346
+ sm: "4px",
347
+ md: "8px",
348
+ lg: "12px",
349
+ xl: "16px",
350
+ pill: "9999px",
351
+ full: "50%"
352
+ };
353
+ var THEME_VAR = {
354
+ primary: "--natoe-colab-primary",
355
+ primaryHover: "--natoe-colab-primary-hover",
356
+ primaryBg: "--natoe-colab-primary-bg",
357
+ primaryFg: "--natoe-colab-primary-fg",
358
+ success: "--natoe-colab-success",
359
+ warning: "--natoe-colab-warning",
360
+ danger: "--natoe-colab-danger",
361
+ dangerBg: "--natoe-colab-danger-bg",
362
+ dangerBorder: "--natoe-colab-danger-border",
363
+ dangerFg: "--natoe-colab-danger-fg",
364
+ fontStack: "--natoe-colab-font-stack"
365
+ };
366
+ var THEME_DEFAULTS = {
367
+ primary: "#2563eb",
368
+ primaryHover: "#1d4ed8",
369
+ primaryBg: "#dbeafe",
370
+ primaryFg: "#1d4ed8",
371
+ success: "#059669",
372
+ warning: "#d97706",
373
+ danger: "#dc2626",
374
+ dangerBg: "#fef2f2",
375
+ dangerBorder: "#fecaca",
376
+ dangerFg: "#b91c1c",
377
+ fontStack: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
378
+ };
379
+ var cssVar = (name, fallback) => `var(${name}, ${fallback})`;
380
+ var COLOR = {
381
+ // Brand — themeable
382
+ primary: cssVar(THEME_VAR.primary, THEME_DEFAULTS.primary),
383
+ primaryHover: cssVar(THEME_VAR.primaryHover, THEME_DEFAULTS.primaryHover),
384
+ /** Tinted surface for chips/cards on brand-coloured states. */
385
+ primaryBg: cssVar(THEME_VAR.primaryBg, THEME_DEFAULTS.primaryBg),
386
+ primaryFg: cssVar(THEME_VAR.primaryFg, THEME_DEFAULTS.primaryFg),
387
+ // Neutral grayscale — not themeable, taken from Tailwind's zinc-leaning
388
+ // slate to match the package's existing tonal balance.
389
+ white: "#ffffff",
390
+ neutral50: "#f9fafb",
391
+ neutral100: "#f3f4f6",
392
+ neutral200: "#e5e7eb",
393
+ neutral300: "#d1d5db",
394
+ neutral400: "#9ca3af",
395
+ neutral500: "#6b7280",
396
+ neutral600: "#4b5563",
397
+ neutral700: "#374151",
398
+ neutral800: "#1f2937",
399
+ neutral900: "#111827",
400
+ slate800: "#1e293b",
401
+ // Semantic — themeable
402
+ success: cssVar(THEME_VAR.success, THEME_DEFAULTS.success),
403
+ warning: cssVar(THEME_VAR.warning, THEME_DEFAULTS.warning),
404
+ danger: cssVar(THEME_VAR.danger, THEME_DEFAULTS.danger),
405
+ dangerBg: cssVar(THEME_VAR.dangerBg, THEME_DEFAULTS.dangerBg),
406
+ dangerBorder: cssVar(THEME_VAR.dangerBorder, THEME_DEFAULTS.dangerBorder),
407
+ dangerFg: cssVar(THEME_VAR.dangerFg, THEME_DEFAULTS.dangerFg)
408
+ };
409
+ var SIZE = {
410
+ /** Default interactive height (text buttons, list rows). */
411
+ control: "40px",
412
+ /** Square icon-only buttons inside chrome (popup title, menu trigger). */
413
+ controlIcon: "36px"};
414
+ var SHADOW = {
415
+ /** Cards / floating popovers (message-actions menu). */
416
+ popover: "0 6px 18px rgba(0, 0, 0, 0.12)",
417
+ toast: "0 4px 12px rgba(0, 0, 0, 0.18)"
418
+ };
419
+ var Z_INDEX = {
420
+ /** Sticky day divider — above bubbles, below interactive popovers. */
421
+ sticky: 1,
422
+ /** Toasts inside a panel. */
423
+ toast: 20,
424
+ /** Floating CollabPopup dialog. */
425
+ dialog: 9999,
426
+ /** Portaled message-actions menu — must sit above the dialog. */
427
+ menu: 1e4
428
+ };
429
+
430
+ // src/core/styles.ts
431
+ var FONT_STACK = `var(${THEME_VAR.fontStack}, ${THEME_DEFAULTS.fontStack})`;
432
+ var ROOT_CLASS = "natoe-colab-root";
433
+ var GLOBAL_STYLE_ID = "natoe-colab-global-styles";
434
+ var ROOT_DEFAULTS_BLOCK = Object.keys(THEME_DEFAULTS).map((key) => ` ${THEME_VAR[key]}: ${THEME_DEFAULTS[key]};`).join("\n");
435
+ var GLOBAL_CSS = `
436
+ :root {
437
+ ${ROOT_DEFAULTS_BLOCK}
438
+ }
439
+
440
+ @keyframes natoe-colab-spin { to { transform: rotate(360deg); } }
441
+
442
+ @keyframes natoe-colab-message-in {
443
+ from { opacity: 0; transform: translateY(4px); }
444
+ to { opacity: 1; transform: translateY(0); }
445
+ }
446
+
447
+ /* High-contrast adjustments \u2014 older users on OS-level high-contrast mode
448
+ get heavier borders and stronger text without losing the package look. */
449
+ @media (prefers-contrast: more) {
450
+ .${ROOT_CLASS} {
451
+ color: #000000;
452
+ }
453
+ .${ROOT_CLASS} button {
454
+ outline: 1px solid currentColor;
455
+ }
456
+ }
457
+
458
+ /* Honour reduced-motion preferences by killing entry animations. */
459
+ @media (prefers-reduced-motion: reduce) {
460
+ .${ROOT_CLASS} [data-natoe-message-bubble] {
461
+ animation: none !important;
462
+ }
463
+ }
464
+ `;
465
+ function ensureGlobalStyles() {
466
+ if (typeof document === "undefined") return;
467
+ if (document.getElementById(GLOBAL_STYLE_ID)) return;
468
+ const style = document.createElement("style");
469
+ style.id = GLOBAL_STYLE_ID;
470
+ style.textContent = GLOBAL_CSS;
471
+ document.head.appendChild(style);
472
+ }
473
+ function applyThemeOverrides(theme) {
474
+ if (typeof document === "undefined") return;
475
+ const root = document.documentElement;
476
+ Object.keys(THEME_VAR).forEach((key) => {
477
+ const value = theme?.[key];
478
+ if (value) {
479
+ root.style.setProperty(THEME_VAR[key], value);
480
+ } else {
481
+ root.style.removeProperty(THEME_VAR[key]);
482
+ }
483
+ });
484
+ }
318
485
  var CollabContext = createContext(null);
319
486
  function useCollab() {
320
487
  const context = useContext(CollabContext);
@@ -333,6 +500,9 @@ function CollabProvider({ config, apiBaseUrl, children }) {
333
500
  const pendingResolvers = useRef(/* @__PURE__ */ new Map());
334
501
  const previewCache = useRef(/* @__PURE__ */ new Map());
335
502
  const batchScheduled = useRef(false);
503
+ useEffect(() => {
504
+ applyThemeOverrides(config.theme);
505
+ }, [config.theme]);
336
506
  useEffect(() => {
337
507
  if (typeof window === "undefined") return;
338
508
  let s = socket;
@@ -548,6 +718,7 @@ function CollabProvider({ config, apiBaseUrl, children }) {
548
718
  config,
549
719
  apiBaseUrl,
550
720
  totalUnread,
721
+ unreadCounts,
551
722
  requestPreview,
552
723
  invalidatePreview,
553
724
  fetchMessages,
@@ -725,26 +896,22 @@ function useConversation({
725
896
  setConversation(existingConv);
726
897
  setParticipants(preview.participants);
727
898
  joinChannel(existingConv);
728
- setMessages(preview.lastMessages);
729
- setHasMore(preview.messageCount > preview.lastMessages.length);
730
- setPinnedMessages(preview.lastMessages.filter((m) => m.isPinned));
731
- setIsLoading(false);
732
899
  if (loadHistory) {
733
900
  fetchMessages(preview.conversationId).then((history) => {
734
901
  if (cancelled) return;
735
- setMessages((prev) => {
736
- const seen = new Set(prev.map((m) => m.id));
737
- const older = history.filter((m) => !seen.has(m.id));
738
- return [...older, ...prev];
739
- });
740
- setHasMore(history.length > 0);
741
- setPinnedMessages((prev) => {
742
- const fromHistory = history.filter((m) => m.isPinned);
743
- const seen = new Set(prev.map((m) => m.id));
744
- return [...fromHistory.filter((m) => !seen.has(m.id)), ...prev];
745
- });
902
+ setMessages(history);
903
+ setHasMore(history.length >= MESSAGES_PAGE_SIZE);
904
+ setPinnedMessages(history.filter((m) => m.isPinned));
905
+ setIsLoading(false);
746
906
  }).catch(() => {
907
+ if (cancelled) return;
908
+ setIsLoading(false);
747
909
  });
910
+ } else {
911
+ setMessages([]);
912
+ setHasMore(false);
913
+ setPinnedMessages([]);
914
+ setIsLoading(false);
748
915
  }
749
916
  } catch (err) {
750
917
  if (cancelled) return;
@@ -825,7 +992,7 @@ function useConversation({
825
992
  const payload = {
826
993
  body,
827
994
  type: "text",
828
- ...replyTo ? { replyToId: replyTo.id } : {}
995
+ ...replyTo ? { reply_to_id: replyTo.id } : {}
829
996
  };
830
997
  const { conv, persistedByCreate } = await ensureConversation(payload);
831
998
  setReplyTo(null);
@@ -878,7 +1045,7 @@ function useConversation({
878
1045
  const payload = {
879
1046
  body: target.body,
880
1047
  type: "text",
881
- ...target.replyToId ? { replyToId: target.replyToId } : {}
1048
+ ...target.replyToId ? { reply_to_id: target.replyToId } : {}
882
1049
  };
883
1050
  try {
884
1051
  await socket.sendMessage(conversation.id, payload);
@@ -1203,102 +1370,6 @@ function BackIcon({ size = 22, color = "currentColor" }) {
1203
1370
  }
1204
1371
  );
1205
1372
  }
1206
-
1207
- // src/core/theme.ts
1208
- var FONT_SIZE = {
1209
- xs: "12px",
1210
- sm: "13px",
1211
- md: "15px",
1212
- lg: "17px",
1213
- xxl: "28px"
1214
- };
1215
- var FONT_WEIGHT = {
1216
- regular: 400,
1217
- medium: 500,
1218
- semibold: 600,
1219
- bold: 700
1220
- };
1221
- var LINE_HEIGHT = {
1222
- tight: 1.3,
1223
- normal: 1.45};
1224
- var SPACE = {
1225
- S1: "4px",
1226
- S2: "8px",
1227
- S3: "12px",
1228
- S4: "16px",
1229
- S5: "20px",
1230
- S6: "24px",
1231
- S12: "48px"
1232
- };
1233
- var RADIUS = {
1234
- sm: "4px",
1235
- md: "8px",
1236
- lg: "12px",
1237
- pill: "9999px",
1238
- full: "50%"
1239
- };
1240
- var COLOR = {
1241
- // Brand
1242
- primary: "#2563eb",
1243
- /** Tinted surface for chips/cards on brand-coloured states. */
1244
- primaryBg: "#dbeafe",
1245
- primaryFg: "#1d4ed8",
1246
- // Neutral grayscale — taken from Tailwind's zinc-leaning slate, the
1247
- // existing dominant family in the package.
1248
- white: "#ffffff",
1249
- neutral50: "#f9fafb",
1250
- neutral100: "#f3f4f6",
1251
- neutral200: "#e5e7eb",
1252
- neutral300: "#d1d5db",
1253
- neutral400: "#9ca3af",
1254
- neutral500: "#6b7280",
1255
- neutral600: "#4b5563",
1256
- neutral700: "#374151",
1257
- neutral800: "#1f2937",
1258
- neutral900: "#111827",
1259
- // Slate (used by day dividers + dialog title bar)
1260
- slate200: "#e2e8f0",
1261
- slate700: "#334155",
1262
- slate800: "#1e293b",
1263
- // Semantic
1264
- success: "#059669",
1265
- warning: "#d97706",
1266
- danger: "#dc2626",
1267
- dangerBg: "#fef2f2",
1268
- dangerBorder: "#fecaca",
1269
- dangerFg: "#b91c1c"
1270
- };
1271
- var SIZE = {
1272
- /** Default interactive height (text buttons, list rows). */
1273
- control: "40px",
1274
- /** Square icon-only buttons inside chrome (popup title, menu trigger). */
1275
- controlIcon: "36px",
1276
- /** Primary input controls (textarea send/attach). 44px = WCAG min. */
1277
- controlPrimary: "44px",
1278
- /** Avatar in the inbox row. */
1279
- avatar: "40px",
1280
- /** Unread dot. */
1281
- dot: "10px"
1282
- };
1283
- var SHADOW = {
1284
- /** Cards / floating popovers (message-actions menu). */
1285
- popover: "0 6px 18px rgba(0, 0, 0, 0.12)",
1286
- /** Dialog (CollabPopup). */
1287
- dialog: "0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.08)",
1288
- /** Subtle separator shadow (sticky day divider, toast). */
1289
- subtle: "0 1px 2px rgba(0, 0, 0, 0.04)",
1290
- toast: "0 4px 12px rgba(0, 0, 0, 0.18)"
1291
- };
1292
- var Z_INDEX = {
1293
- /** Sticky day divider — above bubbles, below interactive popovers. */
1294
- sticky: 1,
1295
- /** Toasts inside a panel. */
1296
- toast: 20,
1297
- /** Floating CollabPopup dialog. */
1298
- dialog: 9999,
1299
- /** Portaled message-actions menu — must sit above the dialog. */
1300
- menu: 1e4
1301
- };
1302
1373
  function resolveDisplayName(patientData, displayName) {
1303
1374
  const localComplete = !!patientData.labName && !!patientData.displayOrderId;
1304
1375
  if (localComplete) return buildChannelName(patientData);
@@ -1315,15 +1386,28 @@ function PatientHeader({
1315
1386
  }) {
1316
1387
  const hasDicom = !!(patientData.studyId && patientData.storageId);
1317
1388
  const meta = [];
1318
- if (patientData.patientAge) meta.push({ label: "Age", value: String(patientData.patientAge) });
1319
- if (patientData.patientSex) meta.push({ label: "Sex", value: String(patientData.patientSex) });
1320
- if (patientData.studyType) meta.push({ label: "Modality", value: patientData.studyType });
1321
- if (patientData.bodyParts && patientData.bodyParts.length > 0) {
1322
- meta.push({ label: "Body part", value: patientData.bodyParts.join(", ") });
1389
+ if (patientData.patientAge && patientData.patientSex) {
1390
+ meta.push({
1391
+ key: "ageSex",
1392
+ label: "Age / Sex",
1393
+ value: `${patientData.patientAge} \xB7 ${patientData.patientSex}`
1394
+ });
1395
+ } else if (patientData.patientAge) {
1396
+ meta.push({ key: "age", label: "Age", value: String(patientData.patientAge) });
1397
+ } else if (patientData.patientSex) {
1398
+ meta.push({ key: "sex", label: "Sex", value: String(patientData.patientSex) });
1399
+ }
1400
+ if (patientData.studyType) {
1401
+ meta.push({ key: "modality", label: "Modality", value: patientData.studyType });
1323
1402
  }
1324
- if (patientData.referringPhysician) {
1325
- meta.push({ label: "Ref", value: patientData.referringPhysician });
1403
+ if (patientData.bodyParts && patientData.bodyParts.length > 0) {
1404
+ meta.push({
1405
+ key: "body",
1406
+ label: "Body part",
1407
+ value: patientData.bodyParts.join(", ")
1408
+ });
1326
1409
  }
1410
+ const hasActions = hasDicom && onOpenDicom || onOpenSettings;
1327
1411
  return /* @__PURE__ */ jsxs("div", { className, style: styles.container, children: [
1328
1412
  !hideName && /* @__PURE__ */ jsxs("div", { style: styles.nameRow, children: [
1329
1413
  onBack && /* @__PURE__ */ jsx(
@@ -1339,54 +1423,55 @@ function PatientHeader({
1339
1423
  ),
1340
1424
  /* @__PURE__ */ jsx("span", { style: styles.nameText, children: resolveDisplayName(patientData, displayName) })
1341
1425
  ] }),
1342
- meta.length > 0 && /* @__PURE__ */ jsx("div", { style: styles.metaRow, children: meta.map((item) => /* @__PURE__ */ jsxs("span", { style: styles.metaItem, children: [
1343
- /* @__PURE__ */ jsxs("span", { style: styles.metaLabel, children: [
1344
- item.label,
1345
- ":"
1346
- ] }),
1347
- " ",
1348
- /* @__PURE__ */ jsx("span", { style: styles.metaValue, children: item.value })
1349
- ] }, item.label)) }),
1350
- hasDicom && onOpenDicom || onOpenSettings ? /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
1351
- hasDicom && onOpenDicom && /* @__PURE__ */ jsxs(
1352
- "button",
1353
- {
1354
- onClick: onOpenDicom,
1355
- style: styles.dicomButton,
1356
- type: "button",
1357
- "aria-label": "View DICOM study",
1358
- children: [
1359
- /* @__PURE__ */ jsx(DicomIcon, { size: 18, color: COLOR.white }),
1360
- /* @__PURE__ */ jsx("span", { children: "View DICOM" })
1361
- ]
1362
- }
1363
- ),
1364
- onOpenSettings && /* @__PURE__ */ jsxs(
1365
- "button",
1366
- {
1367
- onClick: onOpenSettings,
1368
- style: styles.settingsButton,
1369
- type: "button",
1370
- "aria-label": "Open channel settings",
1371
- children: [
1372
- /* @__PURE__ */ jsx(SettingsIcon, { size: 18, color: COLOR.neutral700 }),
1373
- /* @__PURE__ */ jsx("span", { children: "Settings" })
1374
- ]
1375
- }
1376
- )
1377
- ] }) : null
1426
+ /* @__PURE__ */ jsxs("div", { style: styles.caseCard, children: [
1427
+ meta.length > 0 && /* @__PURE__ */ jsx("div", { style: styles.metaRow, children: meta.map((item) => /* @__PURE__ */ jsxs("div", { style: styles.metaCol, children: [
1428
+ /* @__PURE__ */ jsx("span", { style: styles.metaLabel, children: item.label }),
1429
+ /* @__PURE__ */ jsx("span", { style: styles.metaValue, children: item.value })
1430
+ ] }, item.key)) }),
1431
+ hasActions && /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
1432
+ hasDicom && onOpenDicom && /* @__PURE__ */ jsxs(
1433
+ "button",
1434
+ {
1435
+ onClick: onOpenDicom,
1436
+ style: styles.dicomButton,
1437
+ type: "button",
1438
+ "aria-label": "View DICOM study",
1439
+ children: [
1440
+ /* @__PURE__ */ jsx(DicomIcon, { size: 18, color: COLOR.primary }),
1441
+ /* @__PURE__ */ jsx("span", { children: "View DICOM" })
1442
+ ]
1443
+ }
1444
+ ),
1445
+ onOpenSettings && /* @__PURE__ */ jsx(
1446
+ "button",
1447
+ {
1448
+ onClick: onOpenSettings,
1449
+ style: styles.settingsIconButton,
1450
+ type: "button",
1451
+ "aria-label": "Open channel settings",
1452
+ title: "Settings",
1453
+ children: /* @__PURE__ */ jsx(SettingsIcon, { size: 18, color: COLOR.neutral600 })
1454
+ }
1455
+ )
1456
+ ] })
1457
+ ] })
1378
1458
  ] });
1379
1459
  }
1380
1460
  var styles = {
1381
1461
  container: {
1382
- padding: `${SPACE.S3} ${SPACE.S4}`,
1383
- borderBottom: `1px solid ${COLOR.neutral200}`,
1384
- backgroundColor: COLOR.neutral50
1462
+ display: "flex",
1463
+ flexDirection: "column",
1464
+ backgroundColor: COLOR.primaryBg,
1465
+ borderBottom: `1px solid ${COLOR.neutral200}`
1385
1466
  },
1467
+ // Optional name row, only when `hideName` is false (compact-inbox path)
1386
1468
  nameRow: {
1387
1469
  display: "flex",
1388
1470
  alignItems: "center",
1389
- gap: SPACE.S2
1471
+ gap: SPACE.S2,
1472
+ padding: `${SPACE.S3} ${SPACE.S4} ${SPACE.S2}`,
1473
+ borderBottom: `1px solid ${COLOR.neutral200}`,
1474
+ backgroundColor: COLOR.white
1390
1475
  },
1391
1476
  backButton: {
1392
1477
  width: SIZE.controlIcon,
@@ -1403,65 +1488,90 @@ var styles = {
1403
1488
  },
1404
1489
  nameText: {
1405
1490
  fontSize: FONT_SIZE.lg,
1406
- fontWeight: FONT_WEIGHT.semibold,
1491
+ fontWeight: FONT_WEIGHT.bold,
1407
1492
  color: COLOR.neutral900,
1408
1493
  lineHeight: LINE_HEIGHT.tight,
1494
+ letterSpacing: "-0.01em",
1409
1495
  minWidth: 0,
1410
1496
  overflow: "hidden",
1411
1497
  textOverflow: "ellipsis",
1412
1498
  whiteSpace: "nowrap"
1413
1499
  },
1500
+ // Case card: meta columns flowing in a wrap-row, actions stacked
1501
+ // below. Each meta column is a small label-above-value pair so the
1502
+ // user can scan field names quickly without a legend.
1503
+ caseCard: {
1504
+ display: "flex",
1505
+ flexDirection: "column",
1506
+ gap: SPACE.S3,
1507
+ padding: `${SPACE.S3} ${SPACE.S5}`
1508
+ },
1509
+ // Wrap-row of stacked label/value columns. Compact column gap keeps
1510
+ // the row dense without crowding; row gap kicks in when columns
1511
+ // wrap to a second line on narrow popups.
1414
1512
  metaRow: {
1415
1513
  display: "flex",
1416
1514
  flexWrap: "wrap",
1417
- columnGap: SPACE.S3,
1418
- rowGap: SPACE.S1,
1419
- marginTop: SPACE.S1
1515
+ columnGap: SPACE.S5,
1516
+ rowGap: SPACE.S2,
1517
+ minWidth: 0
1420
1518
  },
1421
- metaItem: {
1422
- fontSize: FONT_SIZE.sm,
1423
- color: COLOR.neutral700,
1424
- whiteSpace: "nowrap"
1519
+ metaCol: {
1520
+ display: "flex",
1521
+ flexDirection: "column",
1522
+ gap: "2px",
1523
+ minWidth: 0
1425
1524
  },
1426
1525
  metaLabel: {
1526
+ fontSize: "11px",
1527
+ fontWeight: FONT_WEIGHT.bold,
1528
+ letterSpacing: "0.06em",
1529
+ textTransform: "uppercase",
1427
1530
  color: COLOR.neutral500,
1428
- fontWeight: FONT_WEIGHT.medium
1531
+ whiteSpace: "nowrap"
1429
1532
  },
1430
1533
  metaValue: {
1431
- color: COLOR.neutral900
1534
+ fontSize: FONT_SIZE.sm,
1535
+ fontWeight: FONT_WEIGHT.semibold,
1536
+ color: COLOR.neutral900,
1537
+ whiteSpace: "nowrap",
1538
+ overflow: "hidden",
1539
+ textOverflow: "ellipsis",
1540
+ maxWidth: "180px"
1432
1541
  },
1433
1542
  actions: {
1434
1543
  display: "flex",
1435
1544
  gap: SPACE.S2,
1436
- marginTop: SPACE.S3
1545
+ flexShrink: 0
1437
1546
  },
1438
1547
  dicomButton: {
1439
1548
  display: "inline-flex",
1440
1549
  alignItems: "center",
1441
1550
  gap: SPACE.S2,
1442
1551
  minHeight: SIZE.control,
1443
- padding: `${SPACE.S2} ${SPACE.S4}`,
1552
+ padding: `${SPACE.S2} 14px`,
1444
1553
  fontSize: FONT_SIZE.sm,
1445
- fontWeight: FONT_WEIGHT.medium,
1446
- color: COLOR.white,
1447
- backgroundColor: COLOR.primary,
1448
- border: "none",
1449
- borderRadius: RADIUS.md,
1554
+ fontWeight: FONT_WEIGHT.semibold,
1555
+ color: COLOR.primary,
1556
+ backgroundColor: COLOR.white,
1557
+ border: `1.5px solid ${COLOR.primary}`,
1558
+ borderRadius: RADIUS.lg,
1450
1559
  cursor: "pointer"
1451
1560
  },
1452
- settingsButton: {
1561
+ // Settings is icon-only (40x40 tile) since it's a secondary action
1562
+ // and View DICOM already carries a label.
1563
+ settingsIconButton: {
1564
+ width: SIZE.control,
1565
+ height: SIZE.control,
1453
1566
  display: "inline-flex",
1454
1567
  alignItems: "center",
1455
- gap: SPACE.S2,
1456
- minHeight: SIZE.control,
1457
- padding: `${SPACE.S2} ${SPACE.S4}`,
1458
- fontSize: FONT_SIZE.sm,
1459
- fontWeight: FONT_WEIGHT.medium,
1460
- color: COLOR.neutral700,
1568
+ justifyContent: "center",
1461
1569
  backgroundColor: COLOR.white,
1462
- border: `1px solid ${COLOR.neutral300}`,
1463
- borderRadius: RADIUS.md,
1464
- cursor: "pointer"
1570
+ border: `1px solid ${COLOR.neutral200}`,
1571
+ borderRadius: RADIUS.lg,
1572
+ color: COLOR.neutral600,
1573
+ cursor: "pointer",
1574
+ flexShrink: 0
1465
1575
  }
1466
1576
  };
1467
1577
  function ReplyQuoteBlock({
@@ -1470,10 +1580,10 @@ function ReplyQuoteBlock({
1470
1580
  onClick,
1471
1581
  className
1472
1582
  }) {
1473
- const baseBg = inOwnBubble ? "rgba(255,255,255,0.18)" : COLOR.neutral100;
1583
+ const baseBg = inOwnBubble ? "rgba(255,255,255,0.22)" : COLOR.white;
1474
1584
  const barColor = inOwnBubble ? COLOR.white : COLOR.primary;
1475
1585
  const nameColor = inOwnBubble ? COLOR.white : COLOR.primary;
1476
- const bodyColor = inOwnBubble ? "rgba(255,255,255,0.85)" : COLOR.neutral600;
1586
+ const bodyColor = inOwnBubble ? "rgba(255,255,255,0.9)" : COLOR.neutral700;
1477
1587
  const preview = renderSnapshotPreview(snapshot);
1478
1588
  return /* @__PURE__ */ jsxs(
1479
1589
  "div",
@@ -1845,43 +1955,50 @@ var styles4 = {
1845
1955
  position: "relative",
1846
1956
  display: "inline-block"
1847
1957
  },
1958
+ // Pill-shaped trigger matching the prototype's `.nc-msg-act` style —
1959
+ // always visible (touch-friendly) rather than hover-only.
1848
1960
  trigger: {
1849
- width: SIZE.controlIcon,
1850
- height: SIZE.controlIcon,
1851
- display: "flex",
1961
+ display: "inline-flex",
1852
1962
  alignItems: "center",
1853
1963
  justifyContent: "center",
1854
- backgroundColor: "transparent",
1855
- border: "none",
1856
- borderRadius: RADIUS.full,
1964
+ width: "32px",
1965
+ height: "32px",
1966
+ backgroundColor: COLOR.white,
1967
+ border: `1px solid ${COLOR.neutral200}`,
1968
+ borderRadius: "999px",
1857
1969
  cursor: "pointer",
1858
- color: COLOR.neutral600
1970
+ color: COLOR.neutral600,
1971
+ transition: "background-color 120ms ease, color 120ms ease, border-color 120ms ease"
1859
1972
  },
1860
1973
  menu: {
1861
1974
  position: "fixed",
1862
- minWidth: "160px",
1975
+ minWidth: "200px",
1863
1976
  backgroundColor: COLOR.white,
1864
- border: `1px solid ${COLOR.neutral200}`,
1865
- borderRadius: RADIUS.md,
1977
+ border: `1px solid ${COLOR.neutral300}`,
1978
+ borderRadius: RADIUS.lg,
1866
1979
  boxShadow: SHADOW.popover,
1867
- padding: SPACE.S1,
1980
+ padding: "6px",
1981
+ display: "flex",
1982
+ flexDirection: "column",
1983
+ gap: "2px",
1868
1984
  // Above the floating popup (z-index dialog) but below any future modal.
1869
1985
  zIndex: Z_INDEX.menu
1870
1986
  },
1871
1987
  item: {
1872
1988
  display: "flex",
1873
1989
  alignItems: "center",
1874
- gap: SPACE.S3,
1990
+ gap: SPACE.S2,
1875
1991
  width: "100%",
1876
1992
  minHeight: SIZE.control,
1877
- padding: `${SPACE.S2} ${SPACE.S3}`,
1993
+ padding: `10px ${SPACE.S3}`,
1878
1994
  fontSize: FONT_SIZE.sm,
1879
- color: COLOR.neutral900,
1995
+ color: COLOR.neutral700,
1880
1996
  backgroundColor: "transparent",
1881
1997
  border: "none",
1882
- borderRadius: RADIUS.sm,
1998
+ borderRadius: RADIUS.md,
1883
1999
  cursor: "pointer",
1884
- textAlign: "left"
2000
+ textAlign: "left",
2001
+ fontFamily: "inherit"
1885
2002
  },
1886
2003
  itemDisabled: {
1887
2004
  color: COLOR.neutral400,
@@ -1995,71 +2112,116 @@ function MessageBubble({
1995
2112
  },
1996
2113
  children: [
1997
2114
  isOwn && actionsSlot,
1998
- /* @__PURE__ */ jsx("div", { style: styles5.bubbleWrapper, children: /* @__PURE__ */ jsxs(
1999
- "div",
2000
- {
2001
- style: {
2002
- ...styles5.bubble,
2003
- ...isOwn ? styles5.ownBubble : styles5.otherBubble
2004
- },
2005
- children: [
2006
- message.isPinned && /* @__PURE__ */ jsxs("div", { style: styles5.pinnedBadge, children: [
2007
- /* @__PURE__ */ jsx(PinIcon, { size: 12 }),
2008
- /* @__PURE__ */ jsx("span", { children: "Pinned" })
2009
- ] }),
2010
- message.replyToSnapshot && /* @__PURE__ */ jsx(
2011
- ReplyQuoteBlock,
2012
- {
2013
- snapshot: message.replyToSnapshot,
2014
- inOwnBubble: isOwn,
2015
- onClick: onReplyJumpTo
2016
- }
2017
- ),
2018
- !isOwn && /* @__PURE__ */ jsxs("div", { style: styles5.senderRow, children: [
2019
- /* @__PURE__ */ jsx("span", { style: styles5.senderName, children: message.senderName }),
2020
- /* @__PURE__ */ jsx(RoleBadge, { role: message.senderRole })
2021
- ] }),
2022
- message.type === "text" && /* @__PURE__ */ jsx(TextContent, { body: message.body, onDeepLinkClick }),
2023
- message.type === "audio" && /* @__PURE__ */ jsx(AudioContent, { mediaUrl: message.mediaUrl, duration: message.mediaDuration }),
2024
- message.type === "image" && /* @__PURE__ */ jsx(ImageContent, { mediaUrl: message.mediaUrl, fileName: message.fileName }),
2025
- message.type === "file" && /* @__PURE__ */ jsx(FileContent, { mediaUrl: message.mediaUrl, fileName: message.fileName }),
2026
- message.type === "deep_link" && /* @__PURE__ */ jsx(DeepLinkContent, { body: message.body, metadata: message.metadata, onDeepLinkClick }),
2027
- /* @__PURE__ */ jsx("div", { style: styles5.timestamp, children: formatTime(message.insertedAt) }),
2028
- isOwn && message.status === "sending" && /* @__PURE__ */ jsxs("div", { style: styles5.statusRow, children: [
2029
- /* @__PURE__ */ jsx(Spinner, { size: 12, color: "rgba(255,255,255,0.85)" }),
2030
- /* @__PURE__ */ jsx("span", { style: styles5.statusText, children: "Sending\u2026" })
2031
- ] }),
2032
- isOwn && message.status === "failed" && /* @__PURE__ */ jsxs("div", { style: styles5.statusRow, children: [
2033
- /* @__PURE__ */ jsx(AlertIcon, { size: 12, color: "#fecaca" }),
2034
- /* @__PURE__ */ jsx("span", { style: styles5.statusText, children: "Not sent" }),
2035
- onRetry && /* @__PURE__ */ jsx(
2036
- "button",
2115
+ /* @__PURE__ */ jsxs("div", { style: styles5.bubbleWrapper, children: [
2116
+ !isOwn && /* @__PURE__ */ jsxs("div", { style: styles5.authorRow, children: [
2117
+ /* @__PURE__ */ jsx(
2118
+ "span",
2119
+ {
2120
+ style: {
2121
+ ...styles5.authorAvatar,
2122
+ backgroundColor: roleColor(message.senderRole)
2123
+ },
2124
+ "aria-hidden": "true",
2125
+ children: computeInitials(message.senderName)
2126
+ }
2127
+ ),
2128
+ /* @__PURE__ */ jsx("span", { style: styles5.authorName, children: message.senderName }),
2129
+ /* @__PURE__ */ jsx(RoleBadge, { role: message.senderRole })
2130
+ ] }),
2131
+ /* @__PURE__ */ jsxs(
2132
+ "div",
2133
+ {
2134
+ style: {
2135
+ ...styles5.bubble,
2136
+ ...isOwn ? styles5.ownBubble : styles5.otherBubble
2137
+ },
2138
+ children: [
2139
+ message.isPinned && /* @__PURE__ */ jsxs(
2140
+ "div",
2037
2141
  {
2038
- type: "button",
2039
- onClick: () => onRetry(message.id),
2040
- style: styles5.retryButton,
2041
- "aria-label": "Retry sending message",
2042
- children: "Retry"
2142
+ style: {
2143
+ ...styles5.pinnedBadge,
2144
+ ...isOwn ? styles5.pinnedBadgeOwn : styles5.pinnedBadgeOther
2145
+ },
2146
+ children: [
2147
+ /* @__PURE__ */ jsx(PinIcon, { size: 12 }),
2148
+ /* @__PURE__ */ jsx("span", { children: "Pinned" })
2149
+ ]
2043
2150
  }
2044
- )
2045
- ] }),
2046
- showSeenBy && isOwn && !message.status && /* @__PURE__ */ jsx(
2047
- SeenByIndicator,
2048
- {
2049
- readBy: message.readBy ?? [],
2050
- participants,
2051
- currentUserId,
2052
- senderId: message.senderId
2053
- }
2054
- )
2055
- ]
2056
- }
2057
- ) }),
2151
+ ),
2152
+ message.replyToSnapshot && /* @__PURE__ */ jsx(
2153
+ ReplyQuoteBlock,
2154
+ {
2155
+ snapshot: message.replyToSnapshot,
2156
+ inOwnBubble: isOwn,
2157
+ onClick: onReplyJumpTo
2158
+ }
2159
+ ),
2160
+ message.type === "text" && /* @__PURE__ */ jsx(TextContent, { body: message.body, onDeepLinkClick }),
2161
+ message.type === "audio" && /* @__PURE__ */ jsx(AudioContent, { mediaUrl: message.mediaUrl, duration: message.mediaDuration }),
2162
+ message.type === "image" && /* @__PURE__ */ jsx(ImageContent, { mediaUrl: message.mediaUrl, fileName: message.fileName }),
2163
+ message.type === "file" && /* @__PURE__ */ jsx(FileContent, { mediaUrl: message.mediaUrl, fileName: message.fileName }),
2164
+ message.type === "deep_link" && /* @__PURE__ */ jsx(DeepLinkContent, { body: message.body, metadata: message.metadata, onDeepLinkClick }),
2165
+ /* @__PURE__ */ jsxs("div", { style: styles5.bubbleFoot, children: [
2166
+ /* @__PURE__ */ jsx("span", { style: styles5.timestamp, children: formatTime(message.insertedAt) }),
2167
+ isOwn && message.status === "sending" && /* @__PURE__ */ jsxs("span", { style: styles5.footStatus, children: [
2168
+ /* @__PURE__ */ jsx(Spinner, { size: 11, color: "rgba(255,255,255,0.85)" }),
2169
+ /* @__PURE__ */ jsx("span", { children: "Sending\u2026" })
2170
+ ] }),
2171
+ isOwn && message.status === "failed" && /* @__PURE__ */ jsxs("span", { style: styles5.footStatusFailed, children: [
2172
+ /* @__PURE__ */ jsx(AlertIcon, { size: 11, color: "#fecaca" }),
2173
+ /* @__PURE__ */ jsx("span", { children: "Not sent" }),
2174
+ onRetry && /* @__PURE__ */ jsx(
2175
+ "button",
2176
+ {
2177
+ type: "button",
2178
+ onClick: () => onRetry(message.id),
2179
+ style: styles5.retryButton,
2180
+ "aria-label": "Retry sending message",
2181
+ children: "Retry"
2182
+ }
2183
+ )
2184
+ ] }),
2185
+ showSeenBy && isOwn && !message.status && /* @__PURE__ */ jsx(
2186
+ SeenByIndicator,
2187
+ {
2188
+ readBy: message.readBy ?? [],
2189
+ participants,
2190
+ currentUserId,
2191
+ senderId: message.senderId
2192
+ }
2193
+ )
2194
+ ] })
2195
+ ]
2196
+ }
2197
+ )
2198
+ ] }),
2058
2199
  !isOwn && actionsSlot
2059
2200
  ]
2060
2201
  }
2061
2202
  );
2062
2203
  }
2204
+ function computeInitials(name) {
2205
+ return name.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
2206
+ }
2207
+ function roleColor(role) {
2208
+ switch (role) {
2209
+ case "radiologist":
2210
+ return "#4f46e5";
2211
+ // indigo (blue-leaning)
2212
+ case "lab":
2213
+ return "#2563eb";
2214
+ // brand blue
2215
+ case "physician":
2216
+ return "#0284c7";
2217
+ // sky
2218
+ case "admin":
2219
+ return "#64748b";
2220
+ // slate (muted)
2221
+ default:
2222
+ return "#6b7280";
2223
+ }
2224
+ }
2063
2225
  function TextContent({ body, onDeepLinkClick }) {
2064
2226
  if (body.includes(DEEP_LINK_PREFIX)) {
2065
2227
  const parts = body.split(new RegExp(`(${escapeRegex2(DEEP_LINK_PREFIX)}[\\w/.-]+)`, "g"));
@@ -2121,19 +2283,13 @@ function DeepLinkContent({
2121
2283
  );
2122
2284
  }
2123
2285
  function SystemBubble({ message }) {
2124
- return /* @__PURE__ */ jsxs("div", { style: styles5.systemMessage, children: [
2286
+ return /* @__PURE__ */ jsx("div", { style: styles5.systemWrapper, children: /* @__PURE__ */ jsxs("span", { style: styles5.systemPill, children: [
2125
2287
  /* @__PURE__ */ jsx("span", { children: message.body }),
2126
2288
  /* @__PURE__ */ jsx("span", { style: styles5.systemTime, children: formatTime(message.insertedAt) })
2127
- ] });
2289
+ ] }) });
2128
2290
  }
2129
2291
  function RoleBadge({ role }) {
2130
- const colors = {
2131
- radiologist: "#7c3aed",
2132
- lab: "#2563eb",
2133
- physician: "#059669",
2134
- admin: "#dc2626"
2135
- };
2136
- return /* @__PURE__ */ jsx("span", { style: { ...styles5.roleBadge, backgroundColor: `${colors[role]}15`, color: colors[role] }, children: ROLE_LABELS[role] ?? role });
2292
+ return /* @__PURE__ */ jsx("span", { style: { ...styles5.roleBadge, backgroundColor: roleColor(role) }, children: ROLE_LABELS[role] ?? role });
2137
2293
  }
2138
2294
  function formatTime(iso) {
2139
2295
  try {
@@ -2155,89 +2311,134 @@ var styles5 = {
2155
2311
  display: "flex",
2156
2312
  alignItems: "flex-end",
2157
2313
  gap: SPACE.S1,
2158
- marginBottom: SPACE.S2,
2314
+ marginBottom: SPACE.S3,
2159
2315
  paddingLeft: SPACE.S4,
2160
2316
  paddingRight: SPACE.S4
2161
2317
  },
2162
2318
  bubbleWrapper: {
2163
2319
  position: "relative",
2164
- maxWidth: "75%"
2320
+ maxWidth: "78%",
2321
+ display: "flex",
2322
+ flexDirection: "column",
2323
+ gap: "4px"
2165
2324
  },
2166
2325
  actionsSlot: {
2167
2326
  flexShrink: 0,
2168
2327
  alignSelf: "flex-end"
2169
2328
  },
2329
+ // Author row above the bubble — only rendered for others' messages.
2330
+ authorRow: {
2331
+ display: "flex",
2332
+ alignItems: "center",
2333
+ gap: SPACE.S2
2334
+ },
2335
+ authorAvatar: {
2336
+ width: "36px",
2337
+ height: "36px",
2338
+ borderRadius: "50%",
2339
+ color: COLOR.white,
2340
+ fontSize: FONT_SIZE.sm,
2341
+ fontWeight: FONT_WEIGHT.bold,
2342
+ display: "flex",
2343
+ alignItems: "center",
2344
+ justifyContent: "center",
2345
+ flexShrink: 0,
2346
+ letterSpacing: "0.02em"
2347
+ },
2348
+ authorName: {
2349
+ fontSize: FONT_SIZE.sm,
2350
+ fontWeight: FONT_WEIGHT.semibold,
2351
+ color: COLOR.neutral700,
2352
+ overflow: "hidden",
2353
+ textOverflow: "ellipsis",
2354
+ whiteSpace: "nowrap",
2355
+ minWidth: 0
2356
+ },
2170
2357
  bubble: {
2171
2358
  padding: `${SPACE.S3} ${SPACE.S4}`,
2172
- borderRadius: RADIUS.lg,
2173
- wordBreak: "break-word"
2359
+ borderRadius: "18px",
2360
+ wordBreak: "break-word",
2361
+ border: "1px solid transparent"
2174
2362
  },
2175
2363
  ownBubble: {
2176
2364
  backgroundColor: COLOR.primary,
2177
2365
  color: COLOR.white,
2178
- borderBottomRightRadius: RADIUS.sm
2366
+ borderColor: COLOR.primary,
2367
+ borderBottomRightRadius: "6px"
2179
2368
  },
2180
2369
  otherBubble: {
2181
2370
  backgroundColor: COLOR.neutral100,
2182
2371
  color: COLOR.neutral900,
2183
- borderBottomLeftRadius: RADIUS.sm
2372
+ borderColor: COLOR.neutral200,
2373
+ borderBottomLeftRadius: "6px"
2184
2374
  },
2185
2375
  pinnedBadge: {
2186
2376
  display: "inline-flex",
2187
2377
  alignItems: "center",
2188
- gap: SPACE.S1,
2189
- fontSize: FONT_SIZE.xs,
2190
- fontWeight: FONT_WEIGHT.semibold,
2191
- marginBottom: SPACE.S1,
2192
- color: COLOR.neutral500
2378
+ gap: "4px",
2379
+ fontSize: "11px",
2380
+ fontWeight: FONT_WEIGHT.bold,
2381
+ marginBottom: SPACE.S2,
2382
+ padding: `2px ${SPACE.S2}`,
2383
+ borderRadius: "999px",
2384
+ textTransform: "uppercase",
2385
+ letterSpacing: "0.05em"
2193
2386
  },
2194
- senderRow: {
2195
- display: "flex",
2196
- alignItems: "center",
2197
- gap: SPACE.S2,
2198
- marginBottom: SPACE.S1
2387
+ pinnedBadgeOther: {
2388
+ color: "#b8680f",
2389
+ backgroundColor: "#fff7e0"
2199
2390
  },
2200
- senderName: {
2201
- fontSize: FONT_SIZE.sm,
2202
- fontWeight: FONT_WEIGHT.semibold,
2203
- color: COLOR.neutral700
2391
+ pinnedBadgeOwn: {
2392
+ color: "#fff4d6",
2393
+ backgroundColor: "rgba(255, 255, 255, 0.2)"
2204
2394
  },
2205
2395
  roleBadge: {
2206
- fontSize: FONT_SIZE.xs,
2207
- fontWeight: FONT_WEIGHT.semibold,
2208
- padding: `2px ${SPACE.S2}`,
2209
- borderRadius: RADIUS.sm
2396
+ fontSize: "10px",
2397
+ fontWeight: FONT_WEIGHT.bold,
2398
+ padding: "2px 7px",
2399
+ borderRadius: RADIUS.sm,
2400
+ textTransform: "uppercase",
2401
+ letterSpacing: "0.06em",
2402
+ color: COLOR.white,
2403
+ flexShrink: 0
2210
2404
  },
2211
2405
  textBody: {
2212
2406
  margin: 0,
2213
2407
  fontSize: FONT_SIZE.md,
2214
- lineHeight: LINE_HEIGHT.normal
2408
+ lineHeight: 1.5
2409
+ },
2410
+ bubbleFoot: {
2411
+ display: "flex",
2412
+ alignItems: "center",
2413
+ justifyContent: "flex-end",
2414
+ gap: SPACE.S2,
2415
+ marginTop: SPACE.S2,
2416
+ fontSize: "11px",
2417
+ opacity: 0.85
2215
2418
  },
2216
2419
  timestamp: {
2217
- fontSize: FONT_SIZE.xs,
2420
+ fontSize: "11px",
2218
2421
  color: "inherit",
2219
- opacity: 0.8,
2220
- marginTop: SPACE.S1,
2221
- textAlign: "right"
2422
+ fontVariantNumeric: "tabular-nums"
2222
2423
  },
2223
- statusRow: {
2224
- display: "flex",
2424
+ footStatus: {
2425
+ display: "inline-flex",
2225
2426
  alignItems: "center",
2226
- gap: SPACE.S2,
2227
- marginTop: SPACE.S1,
2228
- fontSize: FONT_SIZE.xs,
2229
- justifyContent: "flex-end",
2230
- color: "rgba(255, 255, 255, 0.9)"
2427
+ gap: "4px",
2428
+ fontSize: "11px"
2231
2429
  },
2232
- statusText: {
2233
- opacity: 0.9
2430
+ footStatusFailed: {
2431
+ display: "inline-flex",
2432
+ alignItems: "center",
2433
+ gap: "4px",
2434
+ fontSize: "11px"
2234
2435
  },
2235
2436
  retryButton: {
2236
2437
  background: "transparent",
2237
2438
  border: "none",
2238
2439
  color: COLOR.white,
2239
2440
  fontWeight: FONT_WEIGHT.semibold,
2240
- fontSize: FONT_SIZE.xs,
2441
+ fontSize: "11px",
2241
2442
  textDecoration: "underline",
2242
2443
  cursor: "pointer",
2243
2444
  padding: `0 ${SPACE.S1}`
@@ -2300,18 +2501,25 @@ var styles5 = {
2300
2501
  color: COLOR.neutral500,
2301
2502
  fontFamily: "monospace"
2302
2503
  },
2303
- systemMessage: {
2504
+ systemWrapper: {
2304
2505
  display: "flex",
2305
2506
  justifyContent: "center",
2507
+ margin: `${SPACE.S2} 0`
2508
+ },
2509
+ systemPill: {
2510
+ display: "inline-flex",
2306
2511
  alignItems: "center",
2307
2512
  gap: SPACE.S2,
2308
- padding: `${SPACE.S1} ${SPACE.S4}`,
2309
- fontSize: FONT_SIZE.sm,
2513
+ padding: `5px ${SPACE.S3}`,
2514
+ backgroundColor: COLOR.neutral100,
2515
+ borderRadius: "999px",
2516
+ fontSize: FONT_SIZE.xs,
2310
2517
  color: COLOR.neutral500
2311
2518
  },
2312
2519
  systemTime: {
2313
- fontSize: FONT_SIZE.xs,
2314
- color: COLOR.neutral400
2520
+ fontSize: "11px",
2521
+ color: COLOR.neutral400,
2522
+ fontVariantNumeric: "tabular-nums"
2315
2523
  }
2316
2524
  };
2317
2525
  function ChatIllustration({ size = 96 }) {
@@ -2450,6 +2658,7 @@ var MessageList = forwardRef(function MessageList2({
2450
2658
  const containerRef = useRef(null);
2451
2659
  const bottomRef = useRef(null);
2452
2660
  const prevMessageCount = useRef(messages.length);
2661
+ const hasInitiallyScrolledRef = useRef(false);
2453
2662
  useImperativeHandle(
2454
2663
  ref,
2455
2664
  () => ({
@@ -2467,7 +2676,31 @@ var MessageList = forwardRef(function MessageList2({
2467
2676
  }),
2468
2677
  []
2469
2678
  );
2679
+ useLayoutEffect(() => {
2680
+ const container = containerRef.current;
2681
+ if (!container || messages.length === 0) return;
2682
+ if (hasInitiallyScrolledRef.current) return;
2683
+ hasInitiallyScrolledRef.current = true;
2684
+ prevMessageCount.current = messages.length;
2685
+ const firstUnread = messages.find(
2686
+ (m) => m.type !== "system" && m.senderId !== currentUserId && !(m.readBy ?? []).includes(currentUserId)
2687
+ );
2688
+ if (firstUnread) {
2689
+ const el = container.querySelector(
2690
+ `[data-message-id="${firstUnread.id}"]`
2691
+ );
2692
+ if (el) {
2693
+ const containerRect = container.getBoundingClientRect();
2694
+ const elRect = el.getBoundingClientRect();
2695
+ const offset = elRect.top - containerRect.top + container.scrollTop;
2696
+ container.scrollTop = Math.max(0, offset - 16);
2697
+ return;
2698
+ }
2699
+ }
2700
+ container.scrollTop = container.scrollHeight;
2701
+ }, [messages, currentUserId]);
2470
2702
  useEffect(() => {
2703
+ if (!hasInitiallyScrolledRef.current) return;
2471
2704
  if (messages.length > prevMessageCount.current) {
2472
2705
  const lastMessage = messages[messages.length - 1];
2473
2706
  const isOwnMessage = lastMessage?.senderId === currentUserId;
@@ -2526,7 +2759,11 @@ var MessageList = forwardRef(function MessageList2({
2526
2759
  const showDivider = !!currentBucket && currentBucket !== lastValidBucket;
2527
2760
  if (currentBucket) lastValidBucket = currentBucket;
2528
2761
  return /* @__PURE__ */ jsxs(React4.Fragment, { children: [
2529
- showDivider && /* @__PURE__ */ jsx("div", { style: styles6.dayDivider, role: "separator", "aria-label": "Date", children: /* @__PURE__ */ jsx("span", { style: styles6.dayDividerPill, children: formatDayDivider(message.insertedAt) }) }),
2762
+ showDivider && /* @__PURE__ */ jsxs("div", { style: styles6.dayDivider, role: "separator", "aria-label": "Date", children: [
2763
+ /* @__PURE__ */ jsx("span", { style: styles6.dayHr, "aria-hidden": "true" }),
2764
+ /* @__PURE__ */ jsx("span", { style: styles6.dayLabel, children: formatDayDivider(message.insertedAt) }),
2765
+ /* @__PURE__ */ jsx("span", { style: styles6.dayHr, "aria-hidden": "true" })
2766
+ ] }),
2530
2767
  /* @__PURE__ */ jsx("div", { "data-message-id": message.id, "data-natoe-message-bubble": true, style: styles6.bubbleSlot, children: /* @__PURE__ */ jsx(
2531
2768
  MessageBubble,
2532
2769
  {
@@ -2625,23 +2862,32 @@ var styles6 = {
2625
2862
  },
2626
2863
  dayDivider: {
2627
2864
  display: "flex",
2628
- justifyContent: "center",
2629
- margin: `${SPACE.S3} 0 ${SPACE.S2}`,
2865
+ alignItems: "center",
2866
+ gap: SPACE.S3,
2867
+ margin: `${SPACE.S4} 0 ${SPACE.S3}`,
2630
2868
  position: "sticky",
2631
2869
  top: SPACE.S1,
2632
2870
  zIndex: Z_INDEX.sticky,
2633
2871
  pointerEvents: "none"
2634
2872
  },
2635
- dayDividerPill: {
2636
- display: "inline-block",
2637
- padding: `${SPACE.S1} ${SPACE.S3}`,
2638
- fontSize: FONT_SIZE.sm,
2639
- fontWeight: FONT_WEIGHT.medium,
2640
- color: COLOR.slate700,
2641
- backgroundColor: COLOR.slate200,
2642
- borderRadius: RADIUS.lg,
2643
- letterSpacing: "0.2px",
2644
- boxShadow: SHADOW.subtle
2873
+ dayHr: {
2874
+ flex: 1,
2875
+ height: "1px",
2876
+ backgroundColor: COLOR.neutral200
2877
+ },
2878
+ dayLabel: {
2879
+ flexShrink: 0,
2880
+ fontSize: FONT_SIZE.xs,
2881
+ fontWeight: FONT_WEIGHT.semibold,
2882
+ color: COLOR.neutral500,
2883
+ textTransform: "uppercase",
2884
+ letterSpacing: "0.06em",
2885
+ // Subtle pill background so the label is readable when it overlays
2886
+ // bubbles passing under it during sticky scroll. Without this the
2887
+ // hairlines visually run through the text on tinted backgrounds.
2888
+ padding: `2px ${SPACE.S2}`,
2889
+ backgroundColor: COLOR.white,
2890
+ borderRadius: RADIUS.sm
2645
2891
  },
2646
2892
  bubbleSlot: {
2647
2893
  animation: "natoe-colab-message-in 180ms ease-out"
@@ -2895,11 +3141,11 @@ function MessageInput({
2895
3141
  {
2896
3142
  onClick: () => fileInputRef.current?.click(),
2897
3143
  disabled: disabled || isSending,
2898
- style: styles8.iconButton,
3144
+ style: styles8.attachButton,
2899
3145
  "aria-label": "Attach file",
2900
3146
  title: "Attach file",
2901
3147
  type: "button",
2902
- children: /* @__PURE__ */ jsx(AttachIcon, { size: 22, color: "#374151" })
3148
+ children: /* @__PURE__ */ jsx(AttachIcon, { size: 22, color: COLOR.neutral700 })
2903
3149
  }
2904
3150
  ),
2905
3151
  /* @__PURE__ */ jsx(
@@ -2938,7 +3184,7 @@ function MessageInput({
2938
3184
  style: styles8.textarea
2939
3185
  }
2940
3186
  ),
2941
- /* @__PURE__ */ jsx(
3187
+ /* @__PURE__ */ jsxs(
2942
3188
  "button",
2943
3189
  {
2944
3190
  onClick: handleSendText,
@@ -2947,10 +3193,13 @@ function MessageInput({
2947
3193
  title: "Send message",
2948
3194
  style: {
2949
3195
  ...styles8.sendButton,
2950
- opacity: text.trim() ? 1 : 0.4
3196
+ opacity: text.trim() ? 1 : 0.5
2951
3197
  },
2952
3198
  type: "button",
2953
- children: /* @__PURE__ */ jsx(SendIcon, { size: 20, color: COLOR.white })
3199
+ children: [
3200
+ /* @__PURE__ */ jsx(SendIcon, { size: 20, color: COLOR.white }),
3201
+ /* @__PURE__ */ jsx("span", { style: styles8.sendLabel, children: "Send" })
3202
+ ]
2954
3203
  }
2955
3204
  )
2956
3205
  ] })
@@ -3012,46 +3261,64 @@ var styles8 = {
3012
3261
  inputBar: {
3013
3262
  display: "flex",
3014
3263
  alignItems: "flex-end",
3015
- gap: SPACE.S2,
3016
- padding: `${SPACE.S3} ${SPACE.S3}`
3017
- },
3018
- iconButton: {
3019
- width: SIZE.controlPrimary,
3020
- height: SIZE.controlPrimary,
3264
+ gap: "10px",
3265
+ padding: `${SPACE.S3} ${SPACE.S4} 14px`
3266
+ },
3267
+ // 48px square tile with a 14px corner radius — visually grounds the
3268
+ // attach affordance as part of the composer chrome rather than a stray
3269
+ // round icon button.
3270
+ attachButton: {
3271
+ width: "48px",
3272
+ height: "48px",
3021
3273
  display: "flex",
3022
3274
  alignItems: "center",
3023
3275
  justifyContent: "center",
3024
- backgroundColor: "transparent",
3025
- border: "none",
3026
- borderRadius: RADIUS.full,
3276
+ backgroundColor: COLOR.neutral100,
3277
+ border: `1.5px solid ${COLOR.neutral200}`,
3278
+ borderRadius: "14px",
3027
3279
  cursor: "pointer",
3028
- flexShrink: 0
3280
+ flexShrink: 0,
3281
+ color: COLOR.neutral700,
3282
+ transition: "background-color 120ms ease, color 120ms ease, border-color 120ms ease"
3029
3283
  },
3030
3284
  textarea: {
3031
3285
  flex: 1,
3032
- padding: `${SPACE.S3} ${SPACE.S4}`,
3286
+ minHeight: "48px",
3287
+ maxHeight: "140px",
3288
+ padding: "13px 16px",
3033
3289
  fontSize: FONT_SIZE.md,
3034
3290
  lineHeight: LINE_HEIGHT.normal,
3035
- border: `1px solid ${COLOR.neutral300}`,
3036
- borderRadius: RADIUS.pill,
3291
+ color: COLOR.neutral900,
3292
+ backgroundColor: COLOR.neutral100,
3293
+ border: `1.5px solid ${COLOR.neutral200}`,
3294
+ borderRadius: "14px",
3037
3295
  resize: "none",
3038
3296
  outline: "none",
3039
3297
  fontFamily: "inherit",
3040
- maxHeight: "120px",
3041
- minHeight: SIZE.controlPrimary
3298
+ transition: "background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease"
3042
3299
  },
3300
+ // Pill-shaped send: icon + "Send" label. Always labelled (never icon-
3301
+ // only) so the affordance is obvious to first-time users.
3043
3302
  sendButton: {
3044
- width: SIZE.controlPrimary,
3045
- height: SIZE.controlPrimary,
3046
- display: "flex",
3303
+ display: "inline-flex",
3047
3304
  alignItems: "center",
3048
- justifyContent: "center",
3305
+ gap: "6px",
3306
+ minWidth: "48px",
3307
+ height: "48px",
3308
+ padding: `0 ${SPACE.S4}`,
3049
3309
  backgroundColor: COLOR.primary,
3050
3310
  color: COLOR.white,
3051
3311
  border: "none",
3052
- borderRadius: RADIUS.full,
3312
+ borderRadius: "14px",
3053
3313
  cursor: "pointer",
3054
- flexShrink: 0
3314
+ flexShrink: 0,
3315
+ fontFamily: "inherit",
3316
+ fontSize: "15px",
3317
+ fontWeight: FONT_WEIGHT.semibold,
3318
+ transition: "opacity 120ms ease, background-color 120ms ease"
3319
+ },
3320
+ sendLabel: {
3321
+ lineHeight: 1
3055
3322
  }};
3056
3323
  function ParticipantsList({
3057
3324
  participants,
@@ -3108,10 +3375,14 @@ function fallbackName(role) {
3108
3375
  }
3109
3376
  function RoleBadge2({ role }) {
3110
3377
  const colors = {
3111
- radiologist: { bg: "#f5f3ff", text: "#7c3aed" },
3378
+ radiologist: { bg: "#eef2ff", text: "#4f46e5" },
3379
+ // indigo
3112
3380
  lab: { bg: "#eff6ff", text: "#2563eb" },
3113
- physician: { bg: "#ecfdf5", text: "#059669" },
3114
- admin: { bg: "#fef2f2", text: "#dc2626" }
3381
+ // brand blue
3382
+ physician: { bg: "#e0f2fe", text: "#0284c7" },
3383
+ // sky
3384
+ admin: { bg: "#f1f5f9", text: "#64748b" }
3385
+ // slate
3115
3386
  };
3116
3387
  const color = colors[role];
3117
3388
  return /* @__PURE__ */ jsx("span", { style: { ...styles9.roleBadge, backgroundColor: color.bg, color: color.text }, children: role });
@@ -3720,45 +3991,6 @@ var styles12 = {
3720
3991
  letterSpacing: "0.4px"
3721
3992
  }
3722
3993
  };
3723
-
3724
- // src/core/styles.ts
3725
- var FONT_STACK = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif';
3726
- var ROOT_CLASS = "natoe-colab-root";
3727
- var GLOBAL_STYLE_ID = "natoe-colab-global-styles";
3728
- var GLOBAL_CSS = `
3729
- @keyframes natoe-colab-spin { to { transform: rotate(360deg); } }
3730
-
3731
- @keyframes natoe-colab-message-in {
3732
- from { opacity: 0; transform: translateY(4px); }
3733
- to { opacity: 1; transform: translateY(0); }
3734
- }
3735
-
3736
- /* High-contrast adjustments \u2014 older users on OS-level high-contrast mode
3737
- get heavier borders and stronger text without losing the package look. */
3738
- @media (prefers-contrast: more) {
3739
- .${ROOT_CLASS} {
3740
- color: #000000;
3741
- }
3742
- .${ROOT_CLASS} button {
3743
- outline: 1px solid currentColor;
3744
- }
3745
- }
3746
-
3747
- /* Honour reduced-motion preferences by killing entry animations. */
3748
- @media (prefers-reduced-motion: reduce) {
3749
- .${ROOT_CLASS} [data-natoe-message-bubble] {
3750
- animation: none !important;
3751
- }
3752
- }
3753
- `;
3754
- function ensureGlobalStyles() {
3755
- if (typeof document === "undefined") return;
3756
- if (document.getElementById(GLOBAL_STYLE_ID)) return;
3757
- const style = document.createElement("style");
3758
- style.id = GLOBAL_STYLE_ID;
3759
- style.textContent = GLOBAL_CSS;
3760
- document.head.appendChild(style);
3761
- }
3762
3994
  ensureGlobalStyles();
3763
3995
  function CollabPanel({
3764
3996
  orderId,
@@ -4007,6 +4239,7 @@ function CollabPopup({
4007
4239
  isOpen,
4008
4240
  onClose,
4009
4241
  onBack,
4242
+ onMinimize,
4010
4243
  initialPosition,
4011
4244
  width = 380,
4012
4245
  height = 520,
@@ -4122,35 +4355,48 @@ function CollabPopup({
4122
4355
  "button",
4123
4356
  {
4124
4357
  onClick: onBack,
4125
- style: styles13.titleButton,
4126
- "aria-label": "Go back",
4358
+ style: styles13.titleSquare,
4359
+ "aria-label": "Back to messages",
4127
4360
  title: "Back",
4128
4361
  type: "button",
4129
- children: "\u2190"
4362
+ children: /* @__PURE__ */ jsx(BackIcon, { size: 18, color: COLOR.neutral700 })
4130
4363
  }
4131
4364
  ),
4132
- /* @__PURE__ */ jsx("span", { id: titleId, style: { ...styles13.titleText, flex: 1 }, children: titleText }),
4365
+ /* @__PURE__ */ jsxs("div", { style: styles13.titleCenter, children: [
4366
+ /* @__PURE__ */ jsx("h2", { id: titleId, style: styles13.patientName, children: patientData.patientName || titleText }),
4367
+ (patientData.labName || patientData.displayOrderId) && /* @__PURE__ */ jsxs("div", { style: styles13.subRow, children: [
4368
+ patientData.labName && /* @__PURE__ */ jsx("span", { children: patientData.labName }),
4369
+ patientData.labName && patientData.displayOrderId && /* @__PURE__ */ jsx("span", { style: styles13.subDot, "aria-hidden": "true", children: "\xB7" }),
4370
+ patientData.displayOrderId && /* @__PURE__ */ jsxs("span", { style: styles13.mono, children: [
4371
+ "#",
4372
+ patientData.displayOrderId.slice(-4)
4373
+ ] })
4374
+ ] })
4375
+ ] }),
4133
4376
  /* @__PURE__ */ jsxs("div", { style: styles13.titleActions, children: [
4134
4377
  /* @__PURE__ */ jsx(
4135
4378
  "button",
4136
4379
  {
4137
- onClick: () => setIsMinimized(!isMinimized),
4138
- style: styles13.titleButton,
4139
- "aria-label": isMinimized ? "Expand chat" : "Minimize chat",
4140
- title: isMinimized ? "Expand" : "Minimize",
4380
+ onClick: () => {
4381
+ if (onMinimize) onMinimize();
4382
+ else setIsMinimized(!isMinimized);
4383
+ },
4384
+ style: styles13.titleSquare,
4385
+ "aria-label": onMinimize ? "Minimize chat" : isMinimized ? "Expand chat" : "Minimize chat",
4386
+ title: onMinimize ? "Minimize" : isMinimized ? "Expand" : "Minimize",
4141
4387
  type: "button",
4142
- children: isMinimized ? /* @__PURE__ */ jsx(ExpandIcon, { size: 16, color: COLOR.slate200 }) : /* @__PURE__ */ jsx(MinimizeIcon, { size: 16, color: COLOR.slate200 })
4388
+ children: onMinimize || !isMinimized ? /* @__PURE__ */ jsx(MinimizeIcon, { size: 18, color: COLOR.neutral700 }) : /* @__PURE__ */ jsx(ExpandIcon, { size: 18, color: COLOR.neutral700 })
4143
4389
  }
4144
4390
  ),
4145
4391
  /* @__PURE__ */ jsx(
4146
4392
  "button",
4147
4393
  {
4148
4394
  onClick: onClose,
4149
- style: styles13.titleButton,
4395
+ style: styles13.titleSquare,
4150
4396
  "aria-label": "Close chat",
4151
4397
  title: "Close",
4152
4398
  type: "button",
4153
- children: /* @__PURE__ */ jsx(CloseIcon, { size: 16, color: COLOR.slate200 })
4399
+ children: /* @__PURE__ */ jsx(CloseIcon, { size: 18, color: COLOR.neutral700 })
4154
4400
  }
4155
4401
  )
4156
4402
  ] })
@@ -4173,51 +4419,78 @@ var styles13 = {
4173
4419
  container: {
4174
4420
  position: "fixed",
4175
4421
  zIndex: Z_INDEX.dialog,
4176
- borderRadius: RADIUS.lg,
4177
- boxShadow: SHADOW.dialog,
4422
+ borderRadius: "20px",
4423
+ boxShadow: "0 20px 48px rgba(20, 21, 28, 0.16), 0 6px 16px rgba(20, 21, 28, 0.08)",
4178
4424
  overflow: "hidden",
4179
4425
  display: "flex",
4180
4426
  flexDirection: "column",
4181
4427
  backgroundColor: COLOR.white,
4182
- border: `1px solid ${COLOR.neutral200}`,
4428
+ border: `1px solid ${COLOR.neutral300}`,
4183
4429
  transition: "height 0.2s ease",
4184
4430
  fontFamily: FONT_STACK
4185
4431
  },
4186
4432
  titleBar: {
4187
4433
  display: "flex",
4188
- justifyContent: "space-between",
4189
4434
  alignItems: "center",
4190
- padding: `0 ${SPACE.S2} 0 ${SPACE.S4}`,
4191
- height: "52px",
4192
- backgroundColor: COLOR.slate800,
4435
+ gap: SPACE.S2,
4436
+ padding: `10px ${SPACE.S3}`,
4437
+ backgroundColor: COLOR.white,
4438
+ borderBottom: `1px solid ${COLOR.neutral200}`,
4193
4439
  cursor: "grab",
4194
4440
  userSelect: "none",
4195
4441
  flexShrink: 0
4196
4442
  },
4197
- titleText: {
4198
- fontSize: FONT_SIZE.md,
4199
- fontWeight: FONT_WEIGHT.medium,
4200
- color: COLOR.white,
4443
+ titleCenter: {
4444
+ flex: 1,
4445
+ minWidth: 0,
4446
+ display: "flex",
4447
+ flexDirection: "column",
4448
+ gap: "2px"
4449
+ },
4450
+ patientName: {
4451
+ margin: 0,
4452
+ fontSize: FONT_SIZE.lg,
4453
+ fontWeight: FONT_WEIGHT.bold,
4454
+ letterSpacing: "-0.01em",
4455
+ color: COLOR.neutral900,
4201
4456
  overflow: "hidden",
4202
4457
  textOverflow: "ellipsis",
4203
4458
  whiteSpace: "nowrap"
4204
4459
  },
4460
+ subRow: {
4461
+ display: "flex",
4462
+ alignItems: "center",
4463
+ gap: SPACE.S2,
4464
+ fontSize: FONT_SIZE.sm,
4465
+ color: COLOR.neutral500,
4466
+ overflow: "hidden",
4467
+ textOverflow: "ellipsis",
4468
+ whiteSpace: "nowrap"
4469
+ },
4470
+ subDot: {
4471
+ color: COLOR.neutral400
4472
+ },
4473
+ mono: {
4474
+ fontFamily: "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
4475
+ fontVariantNumeric: "tabular-nums"
4476
+ },
4205
4477
  titleActions: {
4206
4478
  display: "flex",
4207
4479
  gap: SPACE.S1,
4208
4480
  flexShrink: 0
4209
4481
  },
4210
- titleButton: {
4211
- width: SIZE.control,
4212
- height: SIZE.control,
4482
+ titleSquare: {
4483
+ width: "36px",
4484
+ height: "36px",
4213
4485
  display: "flex",
4214
4486
  alignItems: "center",
4215
4487
  justifyContent: "center",
4216
- backgroundColor: "transparent",
4217
- border: "none",
4488
+ backgroundColor: COLOR.neutral100,
4489
+ border: `1px solid ${COLOR.neutral200}`,
4218
4490
  borderRadius: RADIUS.md,
4219
4491
  cursor: "pointer",
4220
- color: COLOR.slate200
4492
+ color: COLOR.neutral700,
4493
+ flexShrink: 0
4221
4494
  },
4222
4495
  panelWrapper: {
4223
4496
  flex: 1,
@@ -4521,10 +4794,14 @@ function InlineMessageRow({ message }) {
4521
4794
  }
4522
4795
  function RoleDot({ role }) {
4523
4796
  const colors = {
4524
- radiologist: "#7c3aed",
4797
+ radiologist: "#4f46e5",
4798
+ // indigo
4525
4799
  lab: "#2563eb",
4526
- physician: "#059669",
4527
- admin: "#dc2626"
4800
+ // brand blue
4801
+ physician: "#0284c7",
4802
+ // sky
4803
+ admin: "#64748b"
4804
+ // slate
4528
4805
  };
4529
4806
  return /* @__PURE__ */ jsx("span", { style: { ...styles14.roleDot, backgroundColor: colors[role] } });
4530
4807
  }
@@ -4946,6 +5223,10 @@ function ConversationListItem({
4946
5223
  }) {
4947
5224
  const hasUnread = item.unreadCount > 0;
4948
5225
  const displayName = item.name || item.patientSnapshot?.patientName || "Unknown";
5226
+ const studyType = item.patientSnapshot?.studyType;
5227
+ const patientId = item.patientSnapshot?.patientId;
5228
+ const showStudyRow = !!(studyType || patientId);
5229
+ const initials = computeInitials2(item.patientSnapshot?.patientName || displayName);
4949
5230
  return /* @__PURE__ */ jsxs(
4950
5231
  "button",
4951
5232
  {
@@ -4953,15 +5234,11 @@ function ConversationListItem({
4953
5234
  onClick,
4954
5235
  style: {
4955
5236
  ...styles15.container,
4956
- ...isSelected ? styles15.selected : {},
4957
- ...hasUnread ? styles15.unread : {}
5237
+ ...isSelected ? styles15.selected : {}
4958
5238
  },
4959
5239
  type: "button",
4960
5240
  children: [
4961
- /* @__PURE__ */ jsxs("div", { style: styles15.avatarWrapper, children: [
4962
- item.picture ? /* @__PURE__ */ jsx("img", { src: item.picture, alt: "", style: styles15.avatar }) : /* @__PURE__ */ jsx("div", { style: styles15.avatarFallback, children: (displayName).charAt(0).toUpperCase() }),
4963
- hasUnread && /* @__PURE__ */ jsx("span", { style: styles15.unreadDot })
4964
- ] }),
5241
+ /* @__PURE__ */ jsx("div", { style: styles15.avatarWrapper, children: item.picture ? /* @__PURE__ */ jsx("img", { src: item.picture, alt: "", style: styles15.avatarImg }) : /* @__PURE__ */ jsx("div", { style: styles15.avatarTile, "aria-hidden": "true", children: initials }) }),
4965
5242
  /* @__PURE__ */ jsxs("div", { style: styles15.content, children: [
4966
5243
  /* @__PURE__ */ jsxs("div", { style: styles15.topRow, children: [
4967
5244
  /* @__PURE__ */ jsx(
@@ -4985,101 +5262,120 @@ function ConversationListItem({
4985
5262
  }
4986
5263
  )
4987
5264
  ] }),
5265
+ showStudyRow && /* @__PURE__ */ jsxs("div", { style: styles15.studyRow, children: [
5266
+ studyType && /* @__PURE__ */ jsx("span", { style: styles15.studyType, children: studyType }),
5267
+ studyType && patientId && /* @__PURE__ */ jsx("span", { style: styles15.studyDivider, children: "\xB7" }),
5268
+ patientId && /* @__PURE__ */ jsxs("span", { style: styles15.studyId, children: [
5269
+ "MRN ",
5270
+ patientId
5271
+ ] })
5272
+ ] }),
4988
5273
  /* @__PURE__ */ jsxs("div", { style: styles15.bottomRow, children: [
4989
- /* @__PURE__ */ jsx(
4990
- "span",
4991
- {
4992
- style: {
4993
- ...styles15.preview,
4994
- ...hasUnread ? styles15.previewUnread : {}
4995
- },
4996
- children: renderLastMessagePreview(item.lastMessage)
4997
- }
4998
- ),
4999
- hasUnread && /* @__PURE__ */ jsx("span", { style: styles15.badge, children: item.unreadCount > 99 ? "99+" : item.unreadCount })
5274
+ /* @__PURE__ */ jsx(PreviewLine, { message: item.lastMessage, hasUnread }),
5275
+ hasUnread && /* @__PURE__ */ jsx("span", { style: styles15.badge, "aria-label": `${item.unreadCount} unread`, children: item.unreadCount > 99 ? "99+" : item.unreadCount })
5000
5276
  ] })
5001
5277
  ] })
5002
5278
  ]
5003
5279
  }
5004
5280
  );
5005
5281
  }
5006
- function renderLastMessagePreview(message) {
5007
- if (!message) return "No messages yet";
5008
- const prefix = message.senderName ? `${message.senderName}: ` : "";
5282
+ function PreviewLine({ message, hasUnread }) {
5283
+ if (!message) {
5284
+ return /* @__PURE__ */ jsx("span", { style: styles15.preview, children: "No messages yet" });
5285
+ }
5286
+ if (message.type === "system") {
5287
+ return /* @__PURE__ */ jsx("span", { style: styles15.preview, children: message.body });
5288
+ }
5289
+ const text = previewText(message);
5290
+ const sender = message.senderName ? `${message.senderName.split(" ")[0]}:` : "";
5291
+ return /* @__PURE__ */ jsxs(
5292
+ "span",
5293
+ {
5294
+ style: {
5295
+ ...styles15.preview,
5296
+ ...hasUnread ? styles15.previewUnread : {}
5297
+ },
5298
+ children: [
5299
+ sender && /* @__PURE__ */ jsxs("span", { style: styles15.previewSender, children: [
5300
+ sender,
5301
+ " "
5302
+ ] }),
5303
+ text
5304
+ ]
5305
+ }
5306
+ );
5307
+ }
5308
+ function previewText(message) {
5009
5309
  switch (message.type) {
5010
5310
  case "audio":
5011
- return `${prefix}\u{1F3A4} Voice message`;
5311
+ return "Voice message";
5012
5312
  case "image":
5013
- return `${prefix}\u{1F4F7} ${message.fileName || "Image"}`;
5313
+ return message.fileName || "Image";
5014
5314
  case "file":
5015
- return `${prefix}\u{1F4CE} ${message.fileName || "File"}`;
5315
+ return message.fileName || "File";
5016
5316
  case "deep_link":
5017
- return `${prefix}\u{1F517} ${message.body}`;
5018
- case "system":
5019
- return message.body;
5317
+ return message.body || "Shared link";
5020
5318
  default: {
5021
5319
  const body = message.body || "";
5022
- const preview = body.length > 60 ? `${body.slice(0, 60)}\u2026` : body;
5023
- return `${prefix}${preview}`;
5320
+ return body.length > 60 ? `${body.slice(0, 60)}\u2026` : body;
5024
5321
  }
5025
5322
  }
5026
5323
  }
5324
+ function computeInitials2(name) {
5325
+ return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase() ?? "").join("") || "?";
5326
+ }
5027
5327
  var styles15 = {
5028
5328
  container: {
5029
5329
  display: "flex",
5030
- alignItems: "center",
5031
- gap: SPACE.S3,
5032
- padding: `${SPACE.S3} ${SPACE.S3}`,
5330
+ alignItems: "flex-start",
5331
+ gap: "14px",
5033
5332
  width: "100%",
5333
+ padding: `14px ${SPACE.S5}`,
5334
+ minHeight: "84px",
5034
5335
  backgroundColor: "transparent",
5035
5336
  border: "none",
5036
- borderBottom: `1px solid ${COLOR.neutral100}`,
5337
+ borderLeft: "4px solid transparent",
5037
5338
  cursor: "pointer",
5038
- textAlign: "left"
5339
+ textAlign: "left",
5340
+ color: "inherit",
5341
+ fontFamily: "inherit",
5342
+ transition: "background-color 120ms ease"
5039
5343
  },
5040
5344
  selected: {
5041
- backgroundColor: COLOR.primaryBg
5345
+ backgroundColor: COLOR.primaryBg,
5346
+ borderLeftColor: COLOR.primary
5042
5347
  },
5043
- unread: {},
5044
5348
  avatarWrapper: {
5045
- position: "relative",
5046
- width: SIZE.avatar,
5047
- height: SIZE.avatar,
5349
+ width: "52px",
5350
+ height: "52px",
5048
5351
  flexShrink: 0
5049
5352
  },
5050
- avatar: {
5051
- width: SIZE.avatar,
5052
- height: SIZE.avatar,
5053
- borderRadius: RADIUS.full,
5054
- objectFit: "cover"
5353
+ avatarImg: {
5354
+ width: "52px",
5355
+ height: "52px",
5356
+ borderRadius: RADIUS.xl,
5357
+ objectFit: "cover",
5358
+ boxShadow: "inset 0 -2px 0 rgba(0, 0, 0, 0.08)"
5055
5359
  },
5056
- avatarFallback: {
5057
- width: SIZE.avatar,
5058
- height: SIZE.avatar,
5059
- borderRadius: RADIUS.full,
5060
- backgroundColor: COLOR.neutral200,
5360
+ avatarTile: {
5361
+ width: "52px",
5362
+ height: "52px",
5363
+ borderRadius: RADIUS.xl,
5364
+ backgroundColor: COLOR.primary,
5365
+ color: COLOR.white,
5061
5366
  display: "flex",
5062
5367
  alignItems: "center",
5063
5368
  justifyContent: "center",
5064
- fontSize: FONT_SIZE.md,
5065
- fontWeight: FONT_WEIGHT.semibold,
5066
- color: COLOR.neutral500
5067
- },
5068
- unreadDot: {
5069
- position: "absolute",
5070
- top: "0",
5071
- left: "0",
5072
- width: SIZE.dot,
5073
- height: SIZE.dot,
5074
- borderRadius: RADIUS.full,
5075
- backgroundColor: COLOR.primary,
5076
- border: `2px solid ${COLOR.white}`
5369
+ fontSize: FONT_SIZE.lg,
5370
+ fontWeight: FONT_WEIGHT.bold,
5371
+ letterSpacing: "0.02em",
5372
+ boxShadow: "inset 0 -2px 0 rgba(0, 0, 0, 0.08)"
5077
5373
  },
5078
5374
  content: {
5079
5375
  flex: 1,
5080
5376
  display: "flex",
5081
5377
  flexDirection: "column",
5082
- gap: "2px",
5378
+ gap: "3px",
5083
5379
  minWidth: 0
5084
5380
  },
5085
5381
  topRow: {
@@ -5104,123 +5400,191 @@ var styles15 = {
5104
5400
  time: {
5105
5401
  fontSize: FONT_SIZE.xs,
5106
5402
  color: COLOR.neutral500,
5403
+ fontVariantNumeric: "tabular-nums",
5107
5404
  flexShrink: 0
5108
5405
  },
5109
5406
  timeUnread: {
5110
5407
  color: COLOR.primary,
5111
5408
  fontWeight: FONT_WEIGHT.semibold
5112
5409
  },
5410
+ studyRow: {
5411
+ display: "flex",
5412
+ alignItems: "center",
5413
+ gap: SPACE.S2,
5414
+ minWidth: 0
5415
+ },
5416
+ studyType: {
5417
+ fontSize: FONT_SIZE.sm,
5418
+ color: COLOR.neutral500,
5419
+ overflow: "hidden",
5420
+ textOverflow: "ellipsis",
5421
+ whiteSpace: "nowrap"
5422
+ },
5423
+ studyDivider: {
5424
+ fontSize: FONT_SIZE.sm,
5425
+ color: COLOR.neutral400,
5426
+ flexShrink: 0
5427
+ },
5428
+ studyId: {
5429
+ fontSize: FONT_SIZE.sm,
5430
+ color: COLOR.neutral500,
5431
+ fontFamily: "'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
5432
+ fontVariantNumeric: "tabular-nums",
5433
+ flexShrink: 0
5434
+ },
5113
5435
  bottomRow: {
5114
5436
  display: "flex",
5115
5437
  justifyContent: "space-between",
5116
5438
  alignItems: "center",
5117
- gap: SPACE.S2
5439
+ gap: SPACE.S2,
5440
+ marginTop: "2px"
5118
5441
  },
5119
5442
  preview: {
5120
5443
  fontSize: FONT_SIZE.sm,
5121
- color: COLOR.neutral600,
5444
+ color: COLOR.neutral500,
5122
5445
  overflow: "hidden",
5123
5446
  textOverflow: "ellipsis",
5124
5447
  whiteSpace: "nowrap",
5125
5448
  flex: 1,
5126
5449
  minWidth: 0
5127
5450
  },
5451
+ previewSender: {
5452
+ color: COLOR.neutral700,
5453
+ fontWeight: FONT_WEIGHT.semibold
5454
+ },
5128
5455
  previewUnread: {
5129
5456
  color: COLOR.neutral900,
5130
5457
  fontWeight: FONT_WEIGHT.medium
5131
5458
  },
5132
5459
  badge: {
5133
- minWidth: "20px",
5134
- height: "20px",
5135
- padding: `0 ${SPACE.S2}`,
5460
+ minWidth: "22px",
5461
+ height: "22px",
5462
+ padding: "0 9px",
5136
5463
  fontSize: FONT_SIZE.xs,
5137
- fontWeight: FONT_WEIGHT.semibold,
5464
+ fontWeight: FONT_WEIGHT.bold,
5138
5465
  color: COLOR.white,
5139
- backgroundColor: COLOR.primary,
5466
+ backgroundColor: COLOR.danger,
5140
5467
  borderRadius: RADIUS.pill,
5141
5468
  display: "flex",
5142
5469
  alignItems: "center",
5143
5470
  justifyContent: "center",
5471
+ fontVariantNumeric: "tabular-nums",
5144
5472
  flexShrink: 0
5145
5473
  }
5146
5474
  };
5475
+ function SearchIcon({ size = 20, color = "currentColor" }) {
5476
+ return /* @__PURE__ */ jsxs(
5477
+ "svg",
5478
+ {
5479
+ xmlns: "http://www.w3.org/2000/svg",
5480
+ width: size,
5481
+ height: size,
5482
+ viewBox: "0 0 24 24",
5483
+ fill: "none",
5484
+ stroke: color,
5485
+ strokeWidth: "2",
5486
+ strokeLinecap: "round",
5487
+ strokeLinejoin: "round",
5488
+ "aria-hidden": "true",
5489
+ children: [
5490
+ /* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "7" }),
5491
+ /* @__PURE__ */ jsx("path", { d: "M20 20l-4-4" })
5492
+ ]
5493
+ }
5494
+ );
5495
+ }
5147
5496
  function ConversationList({
5148
5497
  conversations,
5149
5498
  selectedId,
5150
5499
  isLoading,
5151
5500
  error,
5152
5501
  onSelect,
5153
- title = "Conversations",
5154
5502
  className
5155
5503
  }) {
5156
5504
  const [query, setQuery] = useState("");
5157
- const [showUnreadOnly, setShowUnreadOnly] = useState(false);
5505
+ const [filter, setFilter] = useState("all");
5506
+ const [searchFocused, setSearchFocused] = useState(false);
5507
+ const unreadCount = useMemo(
5508
+ () => conversations.filter((c) => c.unreadCount > 0).length,
5509
+ [conversations]
5510
+ );
5511
+ const autoDefaultedRef = useRef(false);
5512
+ useEffect(() => {
5513
+ if (autoDefaultedRef.current) return;
5514
+ if (conversations.length === 0) return;
5515
+ autoDefaultedRef.current = true;
5516
+ if (unreadCount > 0) setFilter("unread");
5517
+ }, [conversations.length, unreadCount]);
5158
5518
  const filtered = useMemo(() => {
5159
5519
  let result = conversations;
5160
- if (showUnreadOnly) {
5520
+ if (filter === "unread") {
5161
5521
  result = result.filter((c) => c.unreadCount > 0);
5162
5522
  }
5163
5523
  const q = query.trim().toLowerCase();
5164
5524
  if (q) {
5165
5525
  result = result.filter((c) => {
5166
- const patient = c.patientSnapshot?.patientName?.toLowerCase() || "";
5167
5526
  const name = c.name.toLowerCase();
5168
- return name.includes(q) || patient.includes(q);
5527
+ const patient = c.patientSnapshot?.patientName?.toLowerCase() || "";
5528
+ const studyType = c.patientSnapshot?.studyType?.toLowerCase() || "";
5529
+ const patientId = c.patientSnapshot?.patientId?.toLowerCase() || "";
5530
+ return name.includes(q) || patient.includes(q) || studyType.includes(q) || patientId.includes(q);
5169
5531
  });
5170
5532
  }
5171
5533
  return result;
5172
- }, [conversations, query, showUnreadOnly]);
5173
- const totalUnread = conversations.reduce((sum, c) => sum + c.unreadCount, 0);
5534
+ }, [conversations, query, filter]);
5174
5535
  return /* @__PURE__ */ jsxs("div", { className, style: styles16.container, children: [
5175
- /* @__PURE__ */ jsxs("div", { style: styles16.header, children: [
5176
- /* @__PURE__ */ jsxs("div", { style: styles16.titleRow, children: [
5177
- /* @__PURE__ */ jsx("h3", { style: styles16.title, children: title }),
5178
- totalUnread > 0 && /* @__PURE__ */ jsx("span", { style: styles16.totalBadge, children: totalUnread })
5179
- ] }),
5180
- /* @__PURE__ */ jsx("div", { style: styles16.searchRow, children: /* @__PURE__ */ jsx(
5181
- "input",
5536
+ /* @__PURE__ */ jsxs("div", { style: styles16.tabs, children: [
5537
+ /* @__PURE__ */ jsx(
5538
+ TabButton,
5182
5539
  {
5183
- type: "text",
5184
- value: query,
5185
- onChange: (e) => setQuery(e.target.value),
5186
- placeholder: "Search patients or channels...",
5187
- style: styles16.searchInput
5540
+ label: "Unread",
5541
+ count: unreadCount,
5542
+ isActive: filter === "unread",
5543
+ onClick: () => setFilter("unread")
5188
5544
  }
5189
- ) }),
5190
- /* @__PURE__ */ jsxs("div", { style: styles16.filterRow, children: [
5191
- /* @__PURE__ */ jsx(
5192
- "button",
5193
- {
5194
- onClick: () => setShowUnreadOnly(false),
5195
- style: {
5196
- ...styles16.filterButton,
5197
- ...!showUnreadOnly ? styles16.filterButtonActive : {}
5198
- },
5199
- type: "button",
5200
- children: "All"
5201
- }
5202
- ),
5203
- /* @__PURE__ */ jsxs(
5204
- "button",
5205
- {
5206
- onClick: () => setShowUnreadOnly(true),
5207
- style: {
5208
- ...styles16.filterButton,
5209
- ...showUnreadOnly ? styles16.filterButtonActive : {}
5210
- },
5211
- type: "button",
5212
- children: [
5213
- "Unread ",
5214
- totalUnread > 0 && `(${totalUnread})`
5215
- ]
5216
- }
5217
- )
5218
- ] })
5545
+ ),
5546
+ /* @__PURE__ */ jsx(
5547
+ TabButton,
5548
+ {
5549
+ label: "All",
5550
+ count: conversations.length,
5551
+ isActive: filter === "all",
5552
+ onClick: () => setFilter("all")
5553
+ }
5554
+ )
5219
5555
  ] }),
5556
+ /* @__PURE__ */ jsxs(
5557
+ "div",
5558
+ {
5559
+ style: {
5560
+ ...styles16.search,
5561
+ ...searchFocused ? styles16.searchFocused : {}
5562
+ },
5563
+ children: [
5564
+ /* @__PURE__ */ jsx("span", { style: styles16.searchIcon, "aria-hidden": "true", children: /* @__PURE__ */ jsx(SearchIcon, { size: 20, color: COLOR.neutral500 }) }),
5565
+ /* @__PURE__ */ jsx(
5566
+ "input",
5567
+ {
5568
+ type: "text",
5569
+ value: query,
5570
+ onChange: (e) => setQuery(e.target.value),
5571
+ onFocus: () => setSearchFocused(true),
5572
+ onBlur: () => setSearchFocused(false),
5573
+ placeholder: "Search by patient name or MRN",
5574
+ "aria-label": "Search conversations",
5575
+ style: styles16.searchInput
5576
+ }
5577
+ )
5578
+ ]
5579
+ }
5580
+ ),
5220
5581
  /* @__PURE__ */ jsxs("div", { style: styles16.list, children: [
5221
- isLoading && /* @__PURE__ */ jsx("div", { style: styles16.state, children: "Loading conversations..." }),
5222
- error && /* @__PURE__ */ jsx("div", { style: styles16.errorState, children: error }),
5223
- !isLoading && !error && filtered.length === 0 && /* @__PURE__ */ jsx("div", { style: styles16.state, children: query ? "No matching conversations" : "No conversations yet" }),
5582
+ isLoading && /* @__PURE__ */ jsxs("div", { style: styles16.loading, children: [
5583
+ /* @__PURE__ */ jsx(Spinner, { size: 16 }),
5584
+ /* @__PURE__ */ jsx("span", { children: "Loading conversations\u2026" })
5585
+ ] }),
5586
+ error && !isLoading && /* @__PURE__ */ jsx("div", { style: styles16.errorState, children: error }),
5587
+ !isLoading && !error && filtered.length === 0 && /* @__PURE__ */ jsx(EmptyState, { filter, hasQuery: query.trim().length > 0 }),
5224
5588
  filtered.map((item) => /* @__PURE__ */ jsx(
5225
5589
  ConversationListItem,
5226
5590
  {
@@ -5233,95 +5597,178 @@ function ConversationList({
5233
5597
  ] })
5234
5598
  ] });
5235
5599
  }
5600
+ function TabButton({
5601
+ label,
5602
+ count,
5603
+ isActive,
5604
+ onClick
5605
+ }) {
5606
+ return /* @__PURE__ */ jsxs(
5607
+ "button",
5608
+ {
5609
+ type: "button",
5610
+ onClick,
5611
+ style: {
5612
+ ...styles16.tab,
5613
+ ...isActive ? styles16.tabActive : {}
5614
+ },
5615
+ children: [
5616
+ /* @__PURE__ */ jsx("span", { children: label }),
5617
+ /* @__PURE__ */ jsx(
5618
+ "span",
5619
+ {
5620
+ style: {
5621
+ ...styles16.tabCount,
5622
+ ...isActive ? styles16.tabCountActive : {}
5623
+ },
5624
+ children: count
5625
+ }
5626
+ )
5627
+ ]
5628
+ }
5629
+ );
5630
+ }
5631
+ function EmptyState({ filter, hasQuery }) {
5632
+ let title = "No conversations yet";
5633
+ let subtitle = "New chats will show up here as cases come in.";
5634
+ if (filter === "unread") {
5635
+ title = "No unread messages";
5636
+ subtitle = "You're all caught up.";
5637
+ } else if (hasQuery) {
5638
+ title = "No conversations match";
5639
+ subtitle = "Try a different search.";
5640
+ }
5641
+ return /* @__PURE__ */ jsxs("div", { style: styles16.empty, children: [
5642
+ /* @__PURE__ */ jsx(ChatIllustration, { size: 88 }),
5643
+ /* @__PURE__ */ jsx("p", { style: styles16.emptyTitle, children: title }),
5644
+ /* @__PURE__ */ jsx("p", { style: styles16.emptySubtitle, children: subtitle })
5645
+ ] });
5646
+ }
5236
5647
  var styles16 = {
5237
5648
  container: {
5238
5649
  display: "flex",
5239
5650
  flexDirection: "column",
5240
5651
  height: "100%",
5241
- backgroundColor: COLOR.white,
5242
- borderRight: `1px solid ${COLOR.neutral200}`
5652
+ backgroundColor: COLOR.white
5243
5653
  },
5244
- header: {
5245
- padding: SPACE.S3,
5654
+ // Tabs sit at the top of the panel now (header section removed).
5655
+ // A bit more top padding so they don't crowd the top edge.
5656
+ tabs: {
5657
+ display: "flex",
5658
+ gap: SPACE.S2,
5659
+ padding: `${SPACE.S5} ${SPACE.S5} ${SPACE.S2}`,
5246
5660
  borderBottom: `1px solid ${COLOR.neutral200}`,
5247
- backgroundColor: COLOR.neutral50,
5248
5661
  flexShrink: 0
5249
5662
  },
5250
- titleRow: {
5251
- display: "flex",
5663
+ tab: {
5664
+ display: "inline-flex",
5252
5665
  alignItems: "center",
5253
5666
  gap: SPACE.S2,
5254
- marginBottom: SPACE.S2
5255
- },
5256
- title: {
5257
- margin: 0,
5258
- fontSize: FONT_SIZE.md,
5667
+ padding: `${SPACE.S2} ${SPACE.S4}`,
5668
+ backgroundColor: "transparent",
5669
+ border: "none",
5670
+ borderRadius: RADIUS.lg,
5671
+ cursor: "pointer",
5672
+ color: COLOR.neutral500,
5673
+ fontFamily: "inherit",
5674
+ fontSize: FONT_SIZE.sm,
5259
5675
  fontWeight: FONT_WEIGHT.semibold,
5260
- color: COLOR.neutral900
5676
+ transition: "background-color 120ms ease, color 120ms ease"
5261
5677
  },
5262
- totalBadge: {
5263
- minWidth: "20px",
5264
- height: "20px",
5265
- padding: `0 ${SPACE.S2}`,
5678
+ tabActive: {
5679
+ backgroundColor: COLOR.primaryBg,
5680
+ color: COLOR.primary
5681
+ },
5682
+ tabCount: {
5266
5683
  fontSize: FONT_SIZE.xs,
5267
- fontWeight: FONT_WEIGHT.semibold,
5268
- color: COLOR.white,
5269
- backgroundColor: COLOR.primary,
5684
+ fontVariantNumeric: "tabular-nums",
5685
+ padding: "1px 8px",
5270
5686
  borderRadius: RADIUS.pill,
5687
+ backgroundColor: COLOR.neutral200,
5688
+ color: COLOR.neutral500,
5689
+ fontWeight: FONT_WEIGHT.semibold
5690
+ },
5691
+ tabCountActive: {
5692
+ backgroundColor: COLOR.primary,
5693
+ color: COLOR.white
5694
+ },
5695
+ search: {
5271
5696
  display: "flex",
5272
5697
  alignItems: "center",
5273
- justifyContent: "center"
5274
- },
5275
- searchRow: {
5276
- marginBottom: SPACE.S2
5698
+ gap: SPACE.S2,
5699
+ margin: `14px ${SPACE.S5} ${SPACE.S3}`,
5700
+ padding: `${SPACE.S3} 14px`,
5701
+ backgroundColor: COLOR.neutral100,
5702
+ border: `1.5px solid ${COLOR.neutral200}`,
5703
+ borderRadius: "14px",
5704
+ transition: "background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease"
5277
5705
  },
5278
- searchInput: {
5279
- width: "100%",
5280
- padding: `${SPACE.S2} ${SPACE.S3}`,
5281
- fontSize: FONT_SIZE.sm,
5282
- border: `1px solid ${COLOR.neutral200}`,
5283
- borderRadius: RADIUS.md,
5284
- outline: "none",
5285
- fontFamily: "inherit"
5706
+ searchFocused: {
5707
+ backgroundColor: COLOR.white,
5708
+ borderColor: COLOR.primary,
5709
+ boxShadow: `0 0 0 3px ${COLOR.primaryBg}`
5286
5710
  },
5287
- filterRow: {
5711
+ searchIcon: {
5288
5712
  display: "flex",
5289
- gap: SPACE.S1
5713
+ alignItems: "center",
5714
+ justifyContent: "center",
5715
+ flexShrink: 0
5290
5716
  },
5291
- filterButton: {
5717
+ searchInput: {
5292
5718
  flex: 1,
5293
- padding: `${SPACE.S1} ${SPACE.S2}`,
5294
- fontSize: FONT_SIZE.xs,
5295
- color: COLOR.neutral500,
5296
- backgroundColor: COLOR.white,
5297
- border: `1px solid ${COLOR.neutral200}`,
5298
- borderRadius: RADIUS.md,
5299
- cursor: "pointer"
5300
- },
5301
- filterButtonActive: {
5302
- color: COLOR.white,
5303
- backgroundColor: COLOR.primary,
5304
- borderColor: COLOR.primary,
5305
- fontWeight: FONT_WEIGHT.medium
5719
+ minWidth: 0,
5720
+ border: "none",
5721
+ background: "transparent",
5722
+ outline: "none",
5723
+ fontFamily: "inherit",
5724
+ fontSize: FONT_SIZE.md,
5725
+ color: COLOR.neutral900
5306
5726
  },
5307
5727
  list: {
5308
5728
  flex: 1,
5309
- overflowY: "auto"
5729
+ overflowY: "auto",
5730
+ padding: "4px 0 12px"
5310
5731
  },
5311
- state: {
5312
- padding: `${SPACE.S6} ${SPACE.S4}`,
5732
+ loading: {
5733
+ display: "flex",
5734
+ alignItems: "center",
5735
+ justifyContent: "center",
5736
+ gap: SPACE.S2,
5737
+ padding: `44px ${SPACE.S5}`,
5313
5738
  fontSize: FONT_SIZE.sm,
5314
- color: COLOR.neutral500,
5315
- textAlign: "center"
5739
+ color: COLOR.neutral500
5316
5740
  },
5317
5741
  errorState: {
5318
5742
  padding: SPACE.S4,
5743
+ margin: SPACE.S3,
5319
5744
  fontSize: FONT_SIZE.sm,
5320
5745
  color: COLOR.danger,
5321
5746
  backgroundColor: COLOR.dangerBg,
5322
- margin: SPACE.S3,
5747
+ border: `1px solid ${COLOR.dangerBorder}`,
5323
5748
  borderRadius: RADIUS.md,
5324
5749
  textAlign: "center"
5750
+ },
5751
+ empty: {
5752
+ display: "flex",
5753
+ flexDirection: "column",
5754
+ alignItems: "center",
5755
+ justifyContent: "center",
5756
+ padding: "44px 24px",
5757
+ textAlign: "center",
5758
+ color: COLOR.neutral500
5759
+ },
5760
+ emptyTitle: {
5761
+ margin: "12px 0 4px",
5762
+ fontSize: FONT_SIZE.md,
5763
+ fontWeight: FONT_WEIGHT.semibold,
5764
+ color: COLOR.neutral700
5765
+ },
5766
+ emptySubtitle: {
5767
+ margin: 0,
5768
+ fontSize: FONT_SIZE.sm,
5769
+ color: COLOR.neutral500,
5770
+ maxWidth: "280px",
5771
+ lineHeight: LINE_HEIGHT.normal
5325
5772
  }
5326
5773
  };
5327
5774
  ensureGlobalStyles();
@@ -5329,7 +5776,6 @@ var COMPACT_BREAKPOINT = 720;
5329
5776
  function CollabInbox({
5330
5777
  initialConversationId,
5331
5778
  onSelectConversation,
5332
- title,
5333
5779
  className,
5334
5780
  style
5335
5781
  }) {
@@ -5366,8 +5812,7 @@ function CollabInbox({
5366
5812
  selectedId,
5367
5813
  isLoading,
5368
5814
  error,
5369
- onSelect: handleSelect,
5370
- title
5815
+ onSelect: handleSelect
5371
5816
  }
5372
5817
  ) }),
5373
5818
  showDetail && /* @__PURE__ */ jsx("div", { style: isCompact ? styles17.mainCompact : styles17.main, children: selected ? /* @__PURE__ */ jsx(
@@ -5380,7 +5825,7 @@ function CollabInbox({
5380
5825
  onBack: isCompact ? () => setSelectedId(null) : void 0
5381
5826
  },
5382
5827
  selected.id
5383
- ) : /* @__PURE__ */ jsx(EmptyState, { hasAny: conversations.length > 0 }) })
5828
+ ) : /* @__PURE__ */ jsx(EmptyState2, { hasAny: conversations.length > 0 }) })
5384
5829
  ]
5385
5830
  }
5386
5831
  );
@@ -5414,7 +5859,7 @@ function parseChannelName(name, orderId) {
5414
5859
  orderId
5415
5860
  };
5416
5861
  }
5417
- function EmptyState({ hasAny }) {
5862
+ function EmptyState2({ hasAny }) {
5418
5863
  return /* @__PURE__ */ jsxs("div", { style: styles17.empty, children: [
5419
5864
  /* @__PURE__ */ jsx("div", { style: styles17.emptyIcon, children: "\u{1F4AC}" }),
5420
5865
  /* @__PURE__ */ jsx("h3", { style: styles17.emptyTitle, children: hasAny ? "Select a conversation" : "No conversations yet" }),
@@ -5793,6 +6238,6 @@ function usePinnedMessages({
5793
6238
  };
5794
6239
  }
5795
6240
 
5796
- export { AUDIO_MIME_TYPE, ChannelSettings, CollabInbox, CollabInline, CollabPanel, CollabPopup, CollabProvider, CollabSocket, ConversationList, ConversationListItem, DEEP_LINK_PREFIX, EVENTS, MAX_FILE_SIZE, MAX_PINNED_MESSAGES, MESSAGES_PAGE_SIZE, MESSAGE_TYPES, MessageActionsMenu, MessageBubble, MessageInput, MessageList, ParticipantsList, PatientHeader, PinnedMessagesBar, ReplyPreview, ReplyQuoteBlock, SUPPORTED_IMAGE_TYPES, SeenByIndicator, TYPING_DEBOUNCE_MS, useAudioRecorder, useChannelSettings, useCollab, useConversation, useConversationList, useDeepLinks, useInlineCollab, useMessages, usePinnedMessages, useUnreadCount };
6241
+ export { AUDIO_MIME_TYPE, ChannelSettings, CollabInbox, CollabInline, CollabPanel, CollabPopup, CollabProvider, CollabSocket, ConversationList, ConversationListItem, DEEP_LINK_PREFIX, EVENTS, MAX_FILE_SIZE, MAX_PINNED_MESSAGES, MESSAGES_PAGE_SIZE, MESSAGE_TYPES, MessageActionsMenu, MessageBubble, MessageInput, MessageList, ParticipantsList, PatientHeader, PinnedMessagesBar, ReplyPreview, ReplyQuoteBlock, SUPPORTED_IMAGE_TYPES, SeenByIndicator, THEME_DEFAULTS, THEME_VAR, TYPING_DEBOUNCE_MS, applyThemeOverrides, useAudioRecorder, useChannelSettings, useCollab, useConversation, useConversationList, useDeepLinks, useInlineCollab, useMessages, usePinnedMessages, useUnreadCount };
5797
6242
  //# sourceMappingURL=index.mjs.map
5798
6243
  //# sourceMappingURL=index.mjs.map