@natoe/colab 0.1.16 → 0.1.18
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 +45 -14
- package/dist/index.d.ts +45 -14
- package/dist/index.js +303 -177
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +303 -177
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -513,8 +513,6 @@ var SHADOW = {
|
|
|
513
513
|
toast: "0 4px 12px rgba(0, 0, 0, 0.18)"
|
|
514
514
|
};
|
|
515
515
|
var Z_INDEX = {
|
|
516
|
-
/** Sticky day divider — above bubbles, below interactive popovers. */
|
|
517
|
-
sticky: 1,
|
|
518
516
|
/** Toasts inside a panel. */
|
|
519
517
|
toast: 20,
|
|
520
518
|
/** Floating CollabPopup dialog. */
|
|
@@ -1446,6 +1444,31 @@ function DicomIcon({ size = 18, color = "currentColor" }) {
|
|
|
1446
1444
|
}
|
|
1447
1445
|
);
|
|
1448
1446
|
}
|
|
1447
|
+
function OpenCaseIcon({
|
|
1448
|
+
size = 18,
|
|
1449
|
+
color = "currentColor"
|
|
1450
|
+
}) {
|
|
1451
|
+
return /* @__PURE__ */ jsxs(
|
|
1452
|
+
"svg",
|
|
1453
|
+
{
|
|
1454
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1455
|
+
width: size,
|
|
1456
|
+
height: size,
|
|
1457
|
+
viewBox: "0 0 24 24",
|
|
1458
|
+
fill: "none",
|
|
1459
|
+
stroke: color,
|
|
1460
|
+
strokeWidth: "2",
|
|
1461
|
+
strokeLinecap: "round",
|
|
1462
|
+
strokeLinejoin: "round",
|
|
1463
|
+
"aria-hidden": "true",
|
|
1464
|
+
children: [
|
|
1465
|
+
/* @__PURE__ */ jsx("path", { d: "M14 4h6v6" }),
|
|
1466
|
+
/* @__PURE__ */ jsx("path", { d: "M20 4L11 13" }),
|
|
1467
|
+
/* @__PURE__ */ jsx("path", { d: "M20 14v5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V5a1 1 0 0 1 1-1h5" })
|
|
1468
|
+
]
|
|
1469
|
+
}
|
|
1470
|
+
);
|
|
1471
|
+
}
|
|
1449
1472
|
function PeopleIcon({ size = 18, color = "currentColor" }) {
|
|
1450
1473
|
return /* @__PURE__ */ jsxs(
|
|
1451
1474
|
"svg",
|
|
@@ -1483,15 +1506,19 @@ function BackIcon({ size = 22, color = "currentColor" }) {
|
|
|
1483
1506
|
}
|
|
1484
1507
|
);
|
|
1485
1508
|
}
|
|
1509
|
+
function stripOrderIdSuffix(name) {
|
|
1510
|
+
return name.replace(/\s*-\s*\d{1,6}\s*$/, "").trim();
|
|
1511
|
+
}
|
|
1486
1512
|
function resolveDisplayName(patientData, displayName) {
|
|
1487
1513
|
const localComplete = !!patientData.labName && !!patientData.displayOrderId;
|
|
1488
|
-
|
|
1489
|
-
return
|
|
1514
|
+
const raw = localComplete ? buildChannelName(patientData) : cleanChannelName(displayName) || buildChannelName(patientData);
|
|
1515
|
+
return stripOrderIdSuffix(raw);
|
|
1490
1516
|
}
|
|
1491
1517
|
function PatientHeader({
|
|
1492
1518
|
patientData,
|
|
1493
1519
|
participants,
|
|
1494
1520
|
onOpenDicom,
|
|
1521
|
+
onOpenCase,
|
|
1495
1522
|
onOpenSettings,
|
|
1496
1523
|
onBack,
|
|
1497
1524
|
hideName = false,
|
|
@@ -1499,29 +1526,58 @@ function PatientHeader({
|
|
|
1499
1526
|
className
|
|
1500
1527
|
}) {
|
|
1501
1528
|
const hasDicom = !!(patientData.studyId && patientData.storageId);
|
|
1502
|
-
const
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
value: `${patientData.patientAge} \xB7 ${patientData.patientSex}`
|
|
1508
|
-
});
|
|
1509
|
-
} else if (patientData.patientAge) {
|
|
1510
|
-
meta.push({ key: "age", label: "Age", value: String(patientData.patientAge) });
|
|
1511
|
-
} else if (patientData.patientSex) {
|
|
1512
|
-
meta.push({ key: "sex", label: "Sex", value: String(patientData.patientSex) });
|
|
1513
|
-
}
|
|
1514
|
-
if (patientData.studyType) {
|
|
1515
|
-
meta.push({ key: "modality", label: "Modality", value: patientData.studyType });
|
|
1516
|
-
}
|
|
1529
|
+
const resolvedName = resolveDisplayName(patientData, displayName);
|
|
1530
|
+
const metaParts = [];
|
|
1531
|
+
if (patientData.patientAge) metaParts.push(String(patientData.patientAge));
|
|
1532
|
+
if (patientData.patientSex) metaParts.push(String(patientData.patientSex));
|
|
1533
|
+
if (patientData.studyType) metaParts.push(patientData.studyType);
|
|
1517
1534
|
if (patientData.bodyParts && patientData.bodyParts.length > 0) {
|
|
1518
|
-
|
|
1519
|
-
key: "body",
|
|
1520
|
-
label: "Body part",
|
|
1521
|
-
value: patientData.bodyParts.join(", ")
|
|
1522
|
-
});
|
|
1535
|
+
metaParts.push(patientData.bodyParts.join(", "));
|
|
1523
1536
|
}
|
|
1524
|
-
const
|
|
1537
|
+
const hasPrimaryActions = hasDicom && onOpenDicom || onOpenCase;
|
|
1538
|
+
const primaryActions = hasPrimaryActions ? /* @__PURE__ */ jsxs("div", { style: styles.actions, children: [
|
|
1539
|
+
hasDicom && onOpenDicom && /* @__PURE__ */ jsxs(
|
|
1540
|
+
"button",
|
|
1541
|
+
{
|
|
1542
|
+
onClick: onOpenDicom,
|
|
1543
|
+
style: styles.dicomButton,
|
|
1544
|
+
type: "button",
|
|
1545
|
+
"aria-label": "View DICOM study",
|
|
1546
|
+
children: [
|
|
1547
|
+
/* @__PURE__ */ jsx(DicomIcon, { size: 18, color: COLOR.primary }),
|
|
1548
|
+
/* @__PURE__ */ jsx("span", { children: "View DICOM" })
|
|
1549
|
+
]
|
|
1550
|
+
}
|
|
1551
|
+
),
|
|
1552
|
+
onOpenCase && /* @__PURE__ */ jsxs(
|
|
1553
|
+
"button",
|
|
1554
|
+
{
|
|
1555
|
+
onClick: onOpenCase,
|
|
1556
|
+
style: styles.openCaseButton,
|
|
1557
|
+
type: "button",
|
|
1558
|
+
"aria-label": "Open case",
|
|
1559
|
+
title: "Open case",
|
|
1560
|
+
children: [
|
|
1561
|
+
/* @__PURE__ */ jsx(OpenCaseIcon, { size: 18, color: COLOR.primary }),
|
|
1562
|
+
/* @__PURE__ */ jsx("span", { children: "Open" })
|
|
1563
|
+
]
|
|
1564
|
+
}
|
|
1565
|
+
)
|
|
1566
|
+
] }) : null;
|
|
1567
|
+
const settingsButton = onOpenSettings ? /* @__PURE__ */ jsxs(
|
|
1568
|
+
"button",
|
|
1569
|
+
{
|
|
1570
|
+
onClick: onOpenSettings,
|
|
1571
|
+
style: styles.settingsIconButton,
|
|
1572
|
+
type: "button",
|
|
1573
|
+
"aria-label": `Open channel settings (${participants.length} participants)`,
|
|
1574
|
+
title: "Channel participants",
|
|
1575
|
+
children: [
|
|
1576
|
+
/* @__PURE__ */ jsx(PeopleIcon, { size: 18, color: COLOR.neutral600 }),
|
|
1577
|
+
/* @__PURE__ */ jsx("span", { style: styles.participantCount, children: participants.length })
|
|
1578
|
+
]
|
|
1579
|
+
}
|
|
1580
|
+
) : null;
|
|
1525
1581
|
return /* @__PURE__ */ jsxs("div", { className, style: styles.container, children: [
|
|
1526
1582
|
!hideName && /* @__PURE__ */ jsxs("div", { style: styles.nameRow, children: [
|
|
1527
1583
|
onBack && /* @__PURE__ */ jsx(
|
|
@@ -1532,45 +1588,19 @@ function PatientHeader({
|
|
|
1532
1588
|
style: styles.backButton,
|
|
1533
1589
|
"aria-label": "Back to conversations",
|
|
1534
1590
|
title: "Back to conversations",
|
|
1535
|
-
children: /* @__PURE__ */ jsx(BackIcon, { size:
|
|
1591
|
+
children: /* @__PURE__ */ jsx(BackIcon, { size: 20, color: COLOR.neutral800 })
|
|
1536
1592
|
}
|
|
1537
1593
|
),
|
|
1538
|
-
/* @__PURE__ */ jsx("span", { style: styles.nameText, children:
|
|
1594
|
+
/* @__PURE__ */ jsx("span", { style: styles.nameText, children: resolvedName }),
|
|
1595
|
+
primaryActions
|
|
1539
1596
|
] }),
|
|
1540
|
-
/* @__PURE__ */ jsxs("div", { style: styles.
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
"button",
|
|
1548
|
-
{
|
|
1549
|
-
onClick: onOpenDicom,
|
|
1550
|
-
style: styles.dicomButton,
|
|
1551
|
-
type: "button",
|
|
1552
|
-
"aria-label": "View DICOM study",
|
|
1553
|
-
children: [
|
|
1554
|
-
/* @__PURE__ */ jsx(DicomIcon, { size: 18, color: COLOR.primary }),
|
|
1555
|
-
/* @__PURE__ */ jsx("span", { children: "View DICOM" })
|
|
1556
|
-
]
|
|
1557
|
-
}
|
|
1558
|
-
),
|
|
1559
|
-
onOpenSettings && /* @__PURE__ */ jsxs(
|
|
1560
|
-
"button",
|
|
1561
|
-
{
|
|
1562
|
-
onClick: onOpenSettings,
|
|
1563
|
-
style: styles.settingsIconButton,
|
|
1564
|
-
type: "button",
|
|
1565
|
-
"aria-label": `Open channel settings (${participants.length} participants)`,
|
|
1566
|
-
title: "Channel participants",
|
|
1567
|
-
children: [
|
|
1568
|
-
/* @__PURE__ */ jsx(PeopleIcon, { size: 18, color: COLOR.neutral600 }),
|
|
1569
|
-
/* @__PURE__ */ jsx("span", { style: styles.participantCount, children: participants.length })
|
|
1570
|
-
]
|
|
1571
|
-
}
|
|
1572
|
-
)
|
|
1573
|
-
] })
|
|
1597
|
+
!hideName && (metaParts.length > 0 || settingsButton) && /* @__PURE__ */ jsxs("div", { style: styles.metaRow, children: [
|
|
1598
|
+
metaParts.length > 0 && /* @__PURE__ */ jsx("span", { style: styles.metaText, children: metaParts.join(" \xB7 ") }),
|
|
1599
|
+
settingsButton && /* @__PURE__ */ jsx("div", { style: styles.metaRowEnd, children: settingsButton })
|
|
1600
|
+
] }),
|
|
1601
|
+
hideName && (primaryActions || settingsButton) && /* @__PURE__ */ jsxs("div", { style: styles.actionsOnlyRow, children: [
|
|
1602
|
+
primaryActions,
|
|
1603
|
+
settingsButton
|
|
1574
1604
|
] })
|
|
1575
1605
|
] });
|
|
1576
1606
|
}
|
|
@@ -1579,16 +1609,16 @@ var styles = {
|
|
|
1579
1609
|
display: "flex",
|
|
1580
1610
|
flexDirection: "column",
|
|
1581
1611
|
backgroundColor: COLOR.primaryBg,
|
|
1582
|
-
borderBottom: `1px solid ${COLOR.neutral200}
|
|
1612
|
+
borderBottom: `1px solid ${COLOR.neutral200}`,
|
|
1613
|
+
padding: `${SPACE.S2} ${SPACE.S4}`,
|
|
1614
|
+
gap: "2px"
|
|
1583
1615
|
},
|
|
1584
|
-
//
|
|
1616
|
+
// Title row — back · name · actions in one horizontal line.
|
|
1585
1617
|
nameRow: {
|
|
1586
1618
|
display: "flex",
|
|
1587
1619
|
alignItems: "center",
|
|
1588
1620
|
gap: SPACE.S2,
|
|
1589
|
-
|
|
1590
|
-
borderBottom: `1px solid ${COLOR.neutral200}`,
|
|
1591
|
-
backgroundColor: COLOR.white
|
|
1621
|
+
minWidth: 0
|
|
1592
1622
|
},
|
|
1593
1623
|
backButton: {
|
|
1594
1624
|
width: SIZE.controlIcon,
|
|
@@ -1604,57 +1634,51 @@ var styles = {
|
|
|
1604
1634
|
flexShrink: 0
|
|
1605
1635
|
},
|
|
1606
1636
|
nameText: {
|
|
1637
|
+
flex: 1,
|
|
1638
|
+
minWidth: 0,
|
|
1607
1639
|
fontSize: FONT_SIZE.lg,
|
|
1608
1640
|
fontWeight: FONT_WEIGHT.bold,
|
|
1609
1641
|
color: COLOR.neutral900,
|
|
1610
1642
|
lineHeight: LINE_HEIGHT.tight,
|
|
1611
1643
|
letterSpacing: "-0.01em",
|
|
1612
|
-
minWidth: 0,
|
|
1613
1644
|
overflow: "hidden",
|
|
1614
1645
|
textOverflow: "ellipsis",
|
|
1615
1646
|
whiteSpace: "nowrap"
|
|
1616
1647
|
},
|
|
1617
|
-
//
|
|
1618
|
-
//
|
|
1619
|
-
//
|
|
1620
|
-
|
|
1621
|
-
display: "flex",
|
|
1622
|
-
flexDirection: "column",
|
|
1623
|
-
gap: SPACE.S3,
|
|
1624
|
-
padding: `${SPACE.S3} ${SPACE.S5}`
|
|
1625
|
-
},
|
|
1626
|
-
// Wrap-row of stacked label/value columns. Compact column gap keeps
|
|
1627
|
-
// the row dense without crowding; row gap kicks in when columns
|
|
1628
|
-
// wrap to a second line on narrow popups.
|
|
1648
|
+
// Sub-row beneath the title — small muted meta on the left, the
|
|
1649
|
+
// channel-settings pill on the right when present. Meta text can
|
|
1650
|
+
// ellipsis on narrow widths; the settings pill stays its content
|
|
1651
|
+
// width and hugs the trailing edge via `metaRowEnd`.
|
|
1629
1652
|
metaRow: {
|
|
1630
1653
|
display: "flex",
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1654
|
+
alignItems: "center",
|
|
1655
|
+
gap: SPACE.S2,
|
|
1656
|
+
paddingLeft: `calc(${SIZE.controlIcon} + ${SPACE.S2} - ${SPACE.S2})`,
|
|
1634
1657
|
minWidth: 0
|
|
1635
1658
|
},
|
|
1636
|
-
|
|
1659
|
+
metaRowEnd: {
|
|
1660
|
+
marginLeft: "auto",
|
|
1661
|
+
flexShrink: 0
|
|
1662
|
+
},
|
|
1663
|
+
// When hideName=true (popup mode), only the actions render. The
|
|
1664
|
+
// popup's own title bar carries the name + meta above this row, so
|
|
1665
|
+
// the actions land flush-left here — pinning them to the right
|
|
1666
|
+
// edge would leave the whole left half of the strip awkwardly
|
|
1667
|
+
// empty (the meta that used to fill it now lives in the title bar).
|
|
1668
|
+
actionsOnlyRow: {
|
|
1637
1669
|
display: "flex",
|
|
1638
|
-
|
|
1639
|
-
gap: "2px",
|
|
1670
|
+
justifyContent: "flex-start",
|
|
1640
1671
|
minWidth: 0
|
|
1641
1672
|
},
|
|
1642
|
-
|
|
1643
|
-
fontSize: "11px",
|
|
1644
|
-
fontWeight: FONT_WEIGHT.bold,
|
|
1645
|
-
letterSpacing: "0.06em",
|
|
1646
|
-
textTransform: "uppercase",
|
|
1647
|
-
color: COLOR.neutral500,
|
|
1648
|
-
whiteSpace: "nowrap"
|
|
1649
|
-
},
|
|
1650
|
-
metaValue: {
|
|
1673
|
+
metaText: {
|
|
1651
1674
|
fontSize: FONT_SIZE.sm,
|
|
1652
|
-
fontWeight: FONT_WEIGHT.
|
|
1653
|
-
color: COLOR.
|
|
1654
|
-
|
|
1675
|
+
fontWeight: FONT_WEIGHT.medium,
|
|
1676
|
+
color: COLOR.neutral600,
|
|
1677
|
+
lineHeight: LINE_HEIGHT.tight,
|
|
1655
1678
|
overflow: "hidden",
|
|
1656
1679
|
textOverflow: "ellipsis",
|
|
1657
|
-
|
|
1680
|
+
whiteSpace: "nowrap",
|
|
1681
|
+
minWidth: 0
|
|
1658
1682
|
},
|
|
1659
1683
|
actions: {
|
|
1660
1684
|
display: "flex",
|
|
@@ -1666,7 +1690,24 @@ var styles = {
|
|
|
1666
1690
|
alignItems: "center",
|
|
1667
1691
|
gap: SPACE.S2,
|
|
1668
1692
|
minHeight: SIZE.control,
|
|
1669
|
-
padding: `${SPACE.
|
|
1693
|
+
padding: `${SPACE.S1} ${SPACE.S3}`,
|
|
1694
|
+
fontSize: FONT_SIZE.sm,
|
|
1695
|
+
fontWeight: FONT_WEIGHT.semibold,
|
|
1696
|
+
color: COLOR.primary,
|
|
1697
|
+
backgroundColor: COLOR.white,
|
|
1698
|
+
border: `1.5px solid ${COLOR.primary}`,
|
|
1699
|
+
borderRadius: RADIUS.lg,
|
|
1700
|
+
cursor: "pointer"
|
|
1701
|
+
},
|
|
1702
|
+
// Sibling of the DICOM button — same shape so the two read as a pair.
|
|
1703
|
+
// Click hands off to the host's `onOpenCase` callback (role-aware
|
|
1704
|
+
// routing lives on the host side).
|
|
1705
|
+
openCaseButton: {
|
|
1706
|
+
display: "inline-flex",
|
|
1707
|
+
alignItems: "center",
|
|
1708
|
+
gap: SPACE.S2,
|
|
1709
|
+
minHeight: SIZE.control,
|
|
1710
|
+
padding: `${SPACE.S1} ${SPACE.S3}`,
|
|
1670
1711
|
fontSize: FONT_SIZE.sm,
|
|
1671
1712
|
fontWeight: FONT_WEIGHT.semibold,
|
|
1672
1713
|
color: COLOR.primary,
|
|
@@ -1675,10 +1716,8 @@ var styles = {
|
|
|
1675
1716
|
borderRadius: RADIUS.lg,
|
|
1676
1717
|
cursor: "pointer"
|
|
1677
1718
|
},
|
|
1678
|
-
// Channel-info button: people icon + participant count.
|
|
1679
|
-
// channel settings overlay
|
|
1680
|
-
// don't have to re-wire — the affordance just looks like a "members"
|
|
1681
|
-
// pill now instead of a gear).
|
|
1719
|
+
// Channel-info button: people icon + participant count. Opens the
|
|
1720
|
+
// channel settings overlay.
|
|
1682
1721
|
settingsIconButton: {
|
|
1683
1722
|
minHeight: SIZE.control,
|
|
1684
1723
|
display: "inline-flex",
|
|
@@ -1821,7 +1860,6 @@ var styles3 = {
|
|
|
1821
1860
|
display: "flex",
|
|
1822
1861
|
alignItems: "center",
|
|
1823
1862
|
gap: SPACE.S1,
|
|
1824
|
-
marginTop: "2px",
|
|
1825
1863
|
fontSize: FONT_SIZE.xs,
|
|
1826
1864
|
opacity: 0.85
|
|
1827
1865
|
},
|
|
@@ -2158,6 +2196,62 @@ function AlertIcon({ size = 14, color = "currentColor" }) {
|
|
|
2158
2196
|
}
|
|
2159
2197
|
);
|
|
2160
2198
|
}
|
|
2199
|
+
function RoleAvatarIcon({
|
|
2200
|
+
role,
|
|
2201
|
+
size = 18,
|
|
2202
|
+
color = "currentColor",
|
|
2203
|
+
className
|
|
2204
|
+
}) {
|
|
2205
|
+
const common = {
|
|
2206
|
+
width: size,
|
|
2207
|
+
height: size,
|
|
2208
|
+
viewBox: "0 0 24 24",
|
|
2209
|
+
fill: "none",
|
|
2210
|
+
stroke: color,
|
|
2211
|
+
strokeWidth: 1.9,
|
|
2212
|
+
strokeLinecap: "round",
|
|
2213
|
+
strokeLinejoin: "round",
|
|
2214
|
+
"aria-hidden": true,
|
|
2215
|
+
className
|
|
2216
|
+
};
|
|
2217
|
+
switch (role) {
|
|
2218
|
+
case "radiologist":
|
|
2219
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2220
|
+
/* @__PURE__ */ jsx("path", { d: "M7.5 8.5h9" }),
|
|
2221
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "9.5", r: "4" }),
|
|
2222
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "7.5", r: "1", fill: color, stroke: "none" }),
|
|
2223
|
+
/* @__PURE__ */ jsx("path", { d: "M4 21v-1.5a5 5 0 0 1 5-5h6a5 5 0 0 1 5 5V21" })
|
|
2224
|
+
] });
|
|
2225
|
+
case "lab":
|
|
2226
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2227
|
+
/* @__PURE__ */ jsx("path", { d: "M3 21V9l9-6 9 6v12" }),
|
|
2228
|
+
/* @__PURE__ */ jsx("path", { d: "M12 11v6" }),
|
|
2229
|
+
/* @__PURE__ */ jsx("path", { d: "M9 14h6" }),
|
|
2230
|
+
/* @__PURE__ */ jsx("path", { d: "M11 21v-3h2v3" }),
|
|
2231
|
+
/* @__PURE__ */ jsx("path", { d: "M3 21h18" })
|
|
2232
|
+
] });
|
|
2233
|
+
case "physician":
|
|
2234
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2235
|
+
/* @__PURE__ */ jsx("path", { d: "M7 3v5a4 4 0 0 0 8 0V3" }),
|
|
2236
|
+
/* @__PURE__ */ jsx("path", { d: "M5 3h2" }),
|
|
2237
|
+
/* @__PURE__ */ jsx("path", { d: "M15 3h2" }),
|
|
2238
|
+
/* @__PURE__ */ jsx("path", { d: "M11 12v3a5 5 0 0 0 5 5h0a5 5 0 0 0 5-5v-2" }),
|
|
2239
|
+
/* @__PURE__ */ jsx("circle", { cx: "21", cy: "11", r: "2" })
|
|
2240
|
+
] });
|
|
2241
|
+
case "admin":
|
|
2242
|
+
return /* @__PURE__ */ jsxs("svg", { ...common, children: [
|
|
2243
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "3" }),
|
|
2244
|
+
/* @__PURE__ */ jsx("path", { d: "M12 2v3" }),
|
|
2245
|
+
/* @__PURE__ */ jsx("path", { d: "M12 19v3" }),
|
|
2246
|
+
/* @__PURE__ */ jsx("path", { d: "M5 12H2" }),
|
|
2247
|
+
/* @__PURE__ */ jsx("path", { d: "M22 12h-3" }),
|
|
2248
|
+
/* @__PURE__ */ jsx("path", { d: "M5.6 5.6l2 2" }),
|
|
2249
|
+
/* @__PURE__ */ jsx("path", { d: "M16.4 16.4l2 2" }),
|
|
2250
|
+
/* @__PURE__ */ jsx("path", { d: "M5.6 18.4l2-2" }),
|
|
2251
|
+
/* @__PURE__ */ jsx("path", { d: "M16.4 7.6l2-2" })
|
|
2252
|
+
] });
|
|
2253
|
+
}
|
|
2254
|
+
}
|
|
2161
2255
|
function Spinner({ size = 16, color = COLOR.neutral500, thickness = 2 }) {
|
|
2162
2256
|
return /* @__PURE__ */ jsx(
|
|
2163
2257
|
"span",
|
|
@@ -2246,12 +2340,11 @@ function MessageBubble({
|
|
|
2246
2340
|
...styles5.authorAvatar,
|
|
2247
2341
|
backgroundColor: roleColor(message.senderRole)
|
|
2248
2342
|
},
|
|
2249
|
-
"aria-
|
|
2250
|
-
children:
|
|
2343
|
+
"aria-label": ROLE_LABELS[message.senderRole] ?? message.senderRole,
|
|
2344
|
+
children: /* @__PURE__ */ jsx(RoleAvatarIcon, { role: message.senderRole, size: 20, color: COLOR.white })
|
|
2251
2345
|
}
|
|
2252
2346
|
),
|
|
2253
|
-
/* @__PURE__ */ jsx("span", { style: styles5.authorName, children: message.senderName })
|
|
2254
|
-
/* @__PURE__ */ jsx(RoleBadge, { role: message.senderRole })
|
|
2347
|
+
/* @__PURE__ */ jsx("span", { style: styles5.authorName, children: message.senderName })
|
|
2255
2348
|
] }),
|
|
2256
2349
|
/* @__PURE__ */ jsxs(
|
|
2257
2350
|
"div",
|
|
@@ -2326,9 +2419,6 @@ function MessageBubble({
|
|
|
2326
2419
|
}
|
|
2327
2420
|
);
|
|
2328
2421
|
}
|
|
2329
|
-
function computeInitials(name) {
|
|
2330
|
-
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((p) => p[0]?.toUpperCase() ?? "").join("") || "?";
|
|
2331
|
-
}
|
|
2332
2422
|
function roleColor(role) {
|
|
2333
2423
|
switch (role) {
|
|
2334
2424
|
case "radiologist":
|
|
@@ -2413,9 +2503,6 @@ function SystemBubble({ message }) {
|
|
|
2413
2503
|
/* @__PURE__ */ jsx("span", { style: styles5.systemTime, children: formatTime(message.insertedAt) })
|
|
2414
2504
|
] }) });
|
|
2415
2505
|
}
|
|
2416
|
-
function RoleBadge({ role }) {
|
|
2417
|
-
return /* @__PURE__ */ jsx("span", { style: { ...styles5.roleBadge, backgroundColor: roleColor(role) }, children: ROLE_LABELS[role] ?? role });
|
|
2418
|
-
}
|
|
2419
2506
|
function formatTime(iso) {
|
|
2420
2507
|
try {
|
|
2421
2508
|
return new Date(iso).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
@@ -2480,7 +2567,7 @@ var styles5 = {
|
|
|
2480
2567
|
minWidth: 0
|
|
2481
2568
|
},
|
|
2482
2569
|
bubble: {
|
|
2483
|
-
padding: `${SPACE.
|
|
2570
|
+
padding: `${SPACE.S2} ${SPACE.S4}`,
|
|
2484
2571
|
borderRadius: "18px",
|
|
2485
2572
|
wordBreak: "break-word",
|
|
2486
2573
|
border: "1px solid transparent"
|
|
@@ -2517,16 +2604,6 @@ var styles5 = {
|
|
|
2517
2604
|
color: "#fff4d6",
|
|
2518
2605
|
backgroundColor: "rgba(255, 255, 255, 0.2)"
|
|
2519
2606
|
},
|
|
2520
|
-
roleBadge: {
|
|
2521
|
-
fontSize: "10px",
|
|
2522
|
-
fontWeight: FONT_WEIGHT.bold,
|
|
2523
|
-
padding: "2px 7px",
|
|
2524
|
-
borderRadius: RADIUS.sm,
|
|
2525
|
-
textTransform: "uppercase",
|
|
2526
|
-
letterSpacing: "0.06em",
|
|
2527
|
-
color: COLOR.white,
|
|
2528
|
-
flexShrink: 0
|
|
2529
|
-
},
|
|
2530
2607
|
textBody: {
|
|
2531
2608
|
margin: 0,
|
|
2532
2609
|
fontSize: FONT_SIZE.md,
|
|
@@ -2537,7 +2614,7 @@ var styles5 = {
|
|
|
2537
2614
|
alignItems: "center",
|
|
2538
2615
|
justifyContent: "flex-end",
|
|
2539
2616
|
gap: SPACE.S2,
|
|
2540
|
-
marginTop: SPACE.
|
|
2617
|
+
marginTop: SPACE.S1,
|
|
2541
2618
|
fontSize: "11px",
|
|
2542
2619
|
opacity: 0.85
|
|
2543
2620
|
},
|
|
@@ -2990,9 +3067,6 @@ var styles6 = {
|
|
|
2990
3067
|
alignItems: "center",
|
|
2991
3068
|
gap: SPACE.S3,
|
|
2992
3069
|
margin: `${SPACE.S4} 0 ${SPACE.S3}`,
|
|
2993
|
-
position: "sticky",
|
|
2994
|
-
top: SPACE.S1,
|
|
2995
|
-
zIndex: Z_INDEX.sticky,
|
|
2996
3070
|
pointerEvents: "none"
|
|
2997
3071
|
},
|
|
2998
3072
|
dayHr: {
|
|
@@ -3007,11 +3081,7 @@ var styles6 = {
|
|
|
3007
3081
|
color: COLOR.neutral500,
|
|
3008
3082
|
textTransform: "uppercase",
|
|
3009
3083
|
letterSpacing: "0.06em",
|
|
3010
|
-
// Subtle pill background so the label is readable when it overlays
|
|
3011
|
-
// bubbles passing under it during sticky scroll. Without this the
|
|
3012
|
-
// hairlines visually run through the text on tinted backgrounds.
|
|
3013
3084
|
padding: `2px ${SPACE.S2}`,
|
|
3014
|
-
backgroundColor: COLOR.white,
|
|
3015
3085
|
borderRadius: RADIUS.sm
|
|
3016
3086
|
},
|
|
3017
3087
|
bubbleSlot: {
|
|
@@ -3309,7 +3379,7 @@ function MessageInput({
|
|
|
3309
3379
|
style: styles8.textarea
|
|
3310
3380
|
}
|
|
3311
3381
|
),
|
|
3312
|
-
/* @__PURE__ */
|
|
3382
|
+
/* @__PURE__ */ jsx(
|
|
3313
3383
|
"button",
|
|
3314
3384
|
{
|
|
3315
3385
|
onClick: handleSendText,
|
|
@@ -3321,10 +3391,7 @@ function MessageInput({
|
|
|
3321
3391
|
opacity: text.trim() ? 1 : 0.5
|
|
3322
3392
|
},
|
|
3323
3393
|
type: "button",
|
|
3324
|
-
children:
|
|
3325
|
-
/* @__PURE__ */ jsx(SendIcon, { size: 20, color: COLOR.white }),
|
|
3326
|
-
/* @__PURE__ */ jsx("span", { style: styles8.sendLabel, children: "Send" })
|
|
3327
|
-
]
|
|
3394
|
+
children: /* @__PURE__ */ jsx(SendIcon, { size: 20, color: COLOR.white })
|
|
3328
3395
|
}
|
|
3329
3396
|
)
|
|
3330
3397
|
] })
|
|
@@ -3422,29 +3489,33 @@ var styles8 = {
|
|
|
3422
3489
|
fontFamily: "inherit",
|
|
3423
3490
|
transition: "background-color 120ms ease, border-color 120ms ease, box-shadow 120ms ease"
|
|
3424
3491
|
},
|
|
3425
|
-
//
|
|
3426
|
-
//
|
|
3492
|
+
// Square icon-only send button. The arrow inside an obvious input
|
|
3493
|
+
// context is affordance enough; the word "Send" used to sit next to
|
|
3494
|
+
// it but added clutter without disambiguating anything.
|
|
3427
3495
|
sendButton: {
|
|
3428
3496
|
display: "inline-flex",
|
|
3429
3497
|
alignItems: "center",
|
|
3430
|
-
|
|
3431
|
-
|
|
3498
|
+
justifyContent: "center",
|
|
3499
|
+
width: "48px",
|
|
3432
3500
|
height: "48px",
|
|
3433
|
-
padding: `0 ${SPACE.S4}`,
|
|
3434
3501
|
backgroundColor: COLOR.primary,
|
|
3435
3502
|
color: COLOR.white,
|
|
3436
3503
|
border: "none",
|
|
3437
3504
|
borderRadius: "14px",
|
|
3438
3505
|
cursor: "pointer",
|
|
3439
3506
|
flexShrink: 0,
|
|
3440
|
-
fontFamily: "inherit",
|
|
3441
|
-
fontSize: "15px",
|
|
3442
|
-
fontWeight: FONT_WEIGHT.semibold,
|
|
3443
3507
|
transition: "opacity 120ms ease, background-color 120ms ease"
|
|
3444
|
-
},
|
|
3445
|
-
sendLabel: {
|
|
3446
|
-
lineHeight: 1
|
|
3447
3508
|
}};
|
|
3509
|
+
var ROLE_AVATAR_BG = {
|
|
3510
|
+
radiologist: "#4f46e5",
|
|
3511
|
+
// indigo
|
|
3512
|
+
lab: "#2563eb",
|
|
3513
|
+
// brand blue
|
|
3514
|
+
physician: "#0284c7",
|
|
3515
|
+
// sky
|
|
3516
|
+
admin: "#64748b"
|
|
3517
|
+
// slate
|
|
3518
|
+
};
|
|
3448
3519
|
function ParticipantsList({
|
|
3449
3520
|
participants,
|
|
3450
3521
|
currentUserId,
|
|
@@ -3454,7 +3525,17 @@ function ParticipantsList({
|
|
|
3454
3525
|
}) {
|
|
3455
3526
|
return /* @__PURE__ */ jsx("div", { className, style: styles9.list, children: participants.map((participant) => /* @__PURE__ */ jsxs("div", { style: styles9.item, children: [
|
|
3456
3527
|
/* @__PURE__ */ jsxs("div", { style: styles9.avatar, children: [
|
|
3457
|
-
participant.avatar ? /* @__PURE__ */ jsx("img", { src: participant.avatar, alt: "", style: styles9.avatarImg }) : /* @__PURE__ */ jsx(
|
|
3528
|
+
participant.avatar ? /* @__PURE__ */ jsx("img", { src: participant.avatar, alt: "", style: styles9.avatarImg }) : /* @__PURE__ */ jsx(
|
|
3529
|
+
"span",
|
|
3530
|
+
{
|
|
3531
|
+
style: {
|
|
3532
|
+
...styles9.avatarFallback,
|
|
3533
|
+
backgroundColor: ROLE_AVATAR_BG[participant.userRole]
|
|
3534
|
+
},
|
|
3535
|
+
"aria-hidden": "true",
|
|
3536
|
+
children: /* @__PURE__ */ jsx(RoleAvatarIcon, { role: participant.userRole, size: 18, color: COLOR.white })
|
|
3537
|
+
}
|
|
3538
|
+
),
|
|
3458
3539
|
/* @__PURE__ */ jsx(
|
|
3459
3540
|
"span",
|
|
3460
3541
|
{
|
|
@@ -3470,7 +3551,7 @@ function ParticipantsList({
|
|
|
3470
3551
|
participant.userName ?? fallbackName(participant.userRole),
|
|
3471
3552
|
participant.userId === currentUserId && /* @__PURE__ */ jsx("span", { style: styles9.youBadge, children: " (you)" })
|
|
3472
3553
|
] }),
|
|
3473
|
-
/* @__PURE__ */ jsx(
|
|
3554
|
+
/* @__PURE__ */ jsx(RoleBadge, { role: participant.userRole })
|
|
3474
3555
|
] }),
|
|
3475
3556
|
isAdmin && participant.userId !== currentUserId && /* @__PURE__ */ jsx(
|
|
3476
3557
|
"button",
|
|
@@ -3498,7 +3579,7 @@ function fallbackName(role) {
|
|
|
3498
3579
|
return "User";
|
|
3499
3580
|
}
|
|
3500
3581
|
}
|
|
3501
|
-
function
|
|
3582
|
+
function RoleBadge({ role }) {
|
|
3502
3583
|
const colors = {
|
|
3503
3584
|
radiologist: { bg: "#eef2ff", text: "#4f46e5" },
|
|
3504
3585
|
// indigo
|
|
@@ -3540,13 +3621,10 @@ var styles9 = {
|
|
|
3540
3621
|
width: "32px",
|
|
3541
3622
|
height: "32px",
|
|
3542
3623
|
borderRadius: RADIUS.full,
|
|
3543
|
-
backgroundColor: COLOR.neutral200,
|
|
3544
3624
|
display: "flex",
|
|
3545
3625
|
alignItems: "center",
|
|
3546
3626
|
justifyContent: "center",
|
|
3547
|
-
|
|
3548
|
-
fontWeight: FONT_WEIGHT.semibold,
|
|
3549
|
-
color: COLOR.neutral500
|
|
3627
|
+
color: COLOR.white
|
|
3550
3628
|
},
|
|
3551
3629
|
statusDot: {
|
|
3552
3630
|
position: "absolute",
|
|
@@ -4138,13 +4216,19 @@ function CollabPanel({
|
|
|
4138
4216
|
showSeenBy = true,
|
|
4139
4217
|
onBack,
|
|
4140
4218
|
hidePatientName = false,
|
|
4219
|
+
hideOpenCase = false,
|
|
4141
4220
|
onConversationChange,
|
|
4142
4221
|
themeMode = "light",
|
|
4222
|
+
showSettings: controlledShowSettings,
|
|
4223
|
+
onSettingsChange,
|
|
4143
4224
|
className,
|
|
4144
4225
|
style
|
|
4145
4226
|
}) {
|
|
4146
4227
|
const { config } = useCollab();
|
|
4147
|
-
const
|
|
4228
|
+
const isSettingsControlled = controlledShowSettings !== void 0 && onSettingsChange !== void 0;
|
|
4229
|
+
const [internalShowSettings, setInternalShowSettings] = useState(false);
|
|
4230
|
+
const showSettings = isSettingsControlled ? controlledShowSettings : internalShowSettings;
|
|
4231
|
+
const updateShowSettings = isSettingsControlled ? onSettingsChange : setInternalShowSettings;
|
|
4148
4232
|
const [toast, setToast] = useState(null);
|
|
4149
4233
|
const messageListRef = useRef(null);
|
|
4150
4234
|
const {
|
|
@@ -4180,6 +4264,7 @@ function CollabPanel({
|
|
|
4180
4264
|
config.onOpenDicom(patientData.studyId, patientData.storageId);
|
|
4181
4265
|
}
|
|
4182
4266
|
};
|
|
4267
|
+
const handleOpenCase = !hideOpenCase && config.onOpenCase ? () => config.onOpenCase?.(patientData.orderId, patientData.displayOrderId) : void 0;
|
|
4183
4268
|
const handleJumpToMessage = (messageId) => {
|
|
4184
4269
|
messageListRef.current?.scrollToMessage(messageId);
|
|
4185
4270
|
};
|
|
@@ -4246,7 +4331,7 @@ function CollabPanel({
|
|
|
4246
4331
|
onRemoveUser: channelSettings.removeUser,
|
|
4247
4332
|
onLeaveChannel: channelSettings.leaveChannel,
|
|
4248
4333
|
onDeleteChannel: channelSettings.deleteChannel,
|
|
4249
|
-
onClose: () =>
|
|
4334
|
+
onClose: () => updateShowSettings(false)
|
|
4250
4335
|
}
|
|
4251
4336
|
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4252
4337
|
/* @__PURE__ */ jsx(
|
|
@@ -4255,7 +4340,8 @@ function CollabPanel({
|
|
|
4255
4340
|
patientData,
|
|
4256
4341
|
participants,
|
|
4257
4342
|
onOpenDicom: handleOpenDicom,
|
|
4258
|
-
|
|
4343
|
+
onOpenCase: handleOpenCase,
|
|
4344
|
+
onOpenSettings: hidePatientName ? void 0 : () => updateShowSettings(true),
|
|
4259
4345
|
onBack,
|
|
4260
4346
|
hideName: hidePatientName,
|
|
4261
4347
|
displayName: conversation?.name
|
|
@@ -4398,6 +4484,7 @@ function CollabPopup({
|
|
|
4398
4484
|
);
|
|
4399
4485
|
const [isDragging, setIsDragging] = useState(false);
|
|
4400
4486
|
const [isMinimized, setIsMinimized] = useState(false);
|
|
4487
|
+
const [showSettings, setShowSettings] = useState(false);
|
|
4401
4488
|
const [loadedConversation, setLoadedConversation] = useState(null);
|
|
4402
4489
|
const dragOffset = useRef({ x: 0, y: 0 });
|
|
4403
4490
|
const containerRef = useRef(null);
|
|
@@ -4509,16 +4596,32 @@ function CollabPopup({
|
|
|
4509
4596
|
),
|
|
4510
4597
|
/* @__PURE__ */ jsxs("div", { style: styles13.titleCenter, children: [
|
|
4511
4598
|
/* @__PURE__ */ jsx("h2", { id: titleId, style: styles13.patientName, children: patientData.patientName || titleText }),
|
|
4512
|
-
(
|
|
4513
|
-
|
|
4514
|
-
patientData.
|
|
4515
|
-
patientData.
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4599
|
+
(() => {
|
|
4600
|
+
const parts = [];
|
|
4601
|
+
if (patientData.patientAge) parts.push(String(patientData.patientAge));
|
|
4602
|
+
if (patientData.patientSex) parts.push(String(patientData.patientSex));
|
|
4603
|
+
if (patientData.studyType) parts.push(patientData.studyType);
|
|
4604
|
+
if (patientData.bodyParts && patientData.bodyParts.length > 0) {
|
|
4605
|
+
parts.push(patientData.bodyParts.join(", "));
|
|
4606
|
+
}
|
|
4607
|
+
return parts.length > 0 ? /* @__PURE__ */ jsx("div", { style: styles13.subRow, children: /* @__PURE__ */ jsx("span", { style: styles13.metaText, children: parts.join(" \xB7 ") }) }) : null;
|
|
4608
|
+
})()
|
|
4520
4609
|
] }),
|
|
4521
4610
|
/* @__PURE__ */ jsxs("div", { style: styles13.titleActions, children: [
|
|
4611
|
+
loadedConversation && /* @__PURE__ */ jsxs(
|
|
4612
|
+
"button",
|
|
4613
|
+
{
|
|
4614
|
+
type: "button",
|
|
4615
|
+
onClick: () => setShowSettings(true),
|
|
4616
|
+
style: styles13.titleSettingsPill,
|
|
4617
|
+
"aria-label": `Open channel settings (${loadedConversation.participants.length} participants)`,
|
|
4618
|
+
title: "Channel participants",
|
|
4619
|
+
children: [
|
|
4620
|
+
/* @__PURE__ */ jsx(PeopleIcon, { size: 18, color: COLOR.neutral600 }),
|
|
4621
|
+
/* @__PURE__ */ jsx("span", { style: styles13.titleParticipantCount, children: loadedConversation.participants.length })
|
|
4622
|
+
]
|
|
4623
|
+
}
|
|
4624
|
+
),
|
|
4522
4625
|
/* @__PURE__ */ jsx(
|
|
4523
4626
|
"button",
|
|
4524
4627
|
{
|
|
@@ -4553,7 +4656,9 @@ function CollabPopup({
|
|
|
4553
4656
|
patientData,
|
|
4554
4657
|
participantIds,
|
|
4555
4658
|
hidePatientName: true,
|
|
4556
|
-
onConversationChange: setLoadedConversation
|
|
4659
|
+
onConversationChange: setLoadedConversation,
|
|
4660
|
+
showSettings,
|
|
4661
|
+
onSettingsChange: setShowSettings
|
|
4557
4662
|
}
|
|
4558
4663
|
) })
|
|
4559
4664
|
]
|
|
@@ -4605,19 +4710,17 @@ var styles13 = {
|
|
|
4605
4710
|
subRow: {
|
|
4606
4711
|
display: "flex",
|
|
4607
4712
|
alignItems: "center",
|
|
4608
|
-
gap: SPACE.S2,
|
|
4609
4713
|
fontSize: FONT_SIZE.sm,
|
|
4610
4714
|
color: COLOR.neutral500,
|
|
4611
4715
|
overflow: "hidden",
|
|
4612
4716
|
textOverflow: "ellipsis",
|
|
4613
4717
|
whiteSpace: "nowrap"
|
|
4614
4718
|
},
|
|
4615
|
-
|
|
4616
|
-
|
|
4617
|
-
|
|
4618
|
-
|
|
4619
|
-
|
|
4620
|
-
fontVariantNumeric: "tabular-nums"
|
|
4719
|
+
metaText: {
|
|
4720
|
+
overflow: "hidden",
|
|
4721
|
+
textOverflow: "ellipsis",
|
|
4722
|
+
whiteSpace: "nowrap",
|
|
4723
|
+
minWidth: 0
|
|
4621
4724
|
},
|
|
4622
4725
|
titleActions: {
|
|
4623
4726
|
display: "flex",
|
|
@@ -4637,6 +4740,29 @@ var styles13 = {
|
|
|
4637
4740
|
color: COLOR.neutral700,
|
|
4638
4741
|
flexShrink: 0
|
|
4639
4742
|
},
|
|
4743
|
+
// People-icon + participant-count pill in the title-actions row.
|
|
4744
|
+
// Matches the in-panel settings button's shape (icon + count) so a
|
|
4745
|
+
// user moving between popup mode and standalone mode (CollabInbox
|
|
4746
|
+
// compact) sees the same control language.
|
|
4747
|
+
titleSettingsPill: {
|
|
4748
|
+
height: "36px",
|
|
4749
|
+
display: "inline-flex",
|
|
4750
|
+
alignItems: "center",
|
|
4751
|
+
gap: SPACE.S2,
|
|
4752
|
+
padding: `0 ${SPACE.S3}`,
|
|
4753
|
+
backgroundColor: COLOR.neutral100,
|
|
4754
|
+
border: `1px solid ${COLOR.neutral200}`,
|
|
4755
|
+
borderRadius: RADIUS.md,
|
|
4756
|
+
color: COLOR.neutral600,
|
|
4757
|
+
cursor: "pointer",
|
|
4758
|
+
flexShrink: 0
|
|
4759
|
+
},
|
|
4760
|
+
titleParticipantCount: {
|
|
4761
|
+
fontSize: FONT_SIZE.sm,
|
|
4762
|
+
fontWeight: FONT_WEIGHT.semibold,
|
|
4763
|
+
color: COLOR.neutral700,
|
|
4764
|
+
fontVariantNumeric: "tabular-nums"
|
|
4765
|
+
},
|
|
4640
4766
|
panelWrapper: {
|
|
4641
4767
|
flex: 1,
|
|
4642
4768
|
overflow: "hidden"
|
|
@@ -5430,7 +5556,7 @@ function ConversationListItem({
|
|
|
5430
5556
|
const studyType = item.patientSnapshot?.studyType;
|
|
5431
5557
|
const patientId = item.patientSnapshot?.patientId;
|
|
5432
5558
|
const showStudyRow = !!(studyType || patientId);
|
|
5433
|
-
const initials =
|
|
5559
|
+
const initials = computeInitials(item.patientSnapshot?.patientName || displayName);
|
|
5434
5560
|
return /* @__PURE__ */ jsxs(
|
|
5435
5561
|
"button",
|
|
5436
5562
|
{
|
|
@@ -5525,7 +5651,7 @@ function previewText(message) {
|
|
|
5525
5651
|
}
|
|
5526
5652
|
}
|
|
5527
5653
|
}
|
|
5528
|
-
function
|
|
5654
|
+
function computeInitials(name) {
|
|
5529
5655
|
return name.split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]?.toUpperCase() ?? "").join("") || "?";
|
|
5530
5656
|
}
|
|
5531
5657
|
var styles15 = {
|