@natoe/colab 0.1.4 → 0.1.6

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, useContext, useMemo } from 'react';
1
+ import React4, { createContext, forwardRef, useRef, useImperativeHandle, 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
 
@@ -370,7 +370,9 @@ function CollabProvider({ config, apiBaseUrl, children }) {
370
370
  const data = snakeToCamel(await response.json());
371
371
  orderIds.forEach((orderId) => {
372
372
  const preview = data.previews[orderId] ?? null;
373
- previewCache.current.set(orderId, preview);
373
+ if (preview !== null) {
374
+ previewCache.current.set(orderId, preview);
375
+ }
374
376
  resolvers.get(orderId)?.forEach((resolve) => resolve(preview));
375
377
  });
376
378
  } catch (error) {
@@ -417,7 +419,8 @@ function CollabProvider({ config, apiBaseUrl, children }) {
417
419
  if (!response.ok) {
418
420
  throw { code: "FETCH_ERROR", message: "Failed to fetch messages", status: response.status };
419
421
  }
420
- return snakeToCamel(await response.json());
422
+ const msgs = snakeToCamel(await response.json());
423
+ return msgs.sort((a, b) => a.insertedAt.localeCompare(b.insertedAt));
421
424
  },
422
425
  [apiBaseUrl, authHeaders]
423
426
  );
@@ -891,8 +894,8 @@ function useConversation({
891
894
  const payload = {
892
895
  body: "Voice message",
893
896
  type: "audio",
894
- mediaUrl: url,
895
- mediaDuration: duration
897
+ media_url: url,
898
+ media_duration: duration
896
899
  };
897
900
  const { conv, persistedByCreate } = await ensureConversation(payload);
898
901
  if (persistedByCreate) return;
@@ -908,8 +911,8 @@ function useConversation({
908
911
  const payload = {
909
912
  body: file.name,
910
913
  type: isImage ? "image" : "file",
911
- mediaUrl: url,
912
- fileName: file.name
914
+ media_url: url,
915
+ file_name: file.name
913
916
  };
914
917
  const { conv, persistedByCreate } = await ensureConversation(payload);
915
918
  if (persistedByCreate) return;
@@ -1373,91 +1376,247 @@ var styles3 = {
1373
1376
  textDecoration: "underline"
1374
1377
  }
1375
1378
  };
1379
+ function ReplyIcon({ size = 18, color = "currentColor" }) {
1380
+ return /* @__PURE__ */ jsx(
1381
+ "svg",
1382
+ {
1383
+ xmlns: "http://www.w3.org/2000/svg",
1384
+ width: size,
1385
+ height: size,
1386
+ viewBox: "0 0 24 24",
1387
+ fill: color,
1388
+ "aria-hidden": "true",
1389
+ children: /* @__PURE__ */ jsx("path", { d: "M10 9V5l-7 7 7 7v-4.1c5 0 8.5 1.6 11 5.1-1-5-4-10-11-11z" })
1390
+ }
1391
+ );
1392
+ }
1393
+ function PinIcon({ size = 14, color = "currentColor" }) {
1394
+ return /* @__PURE__ */ jsx(
1395
+ "svg",
1396
+ {
1397
+ xmlns: "http://www.w3.org/2000/svg",
1398
+ width: size,
1399
+ height: size,
1400
+ viewBox: "0 0 24 24",
1401
+ fill: color,
1402
+ "aria-hidden": "true",
1403
+ children: /* @__PURE__ */ jsx("path", { d: "M16 9V4l1 0c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1l1 0v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z" })
1404
+ }
1405
+ );
1406
+ }
1407
+ function CopyIcon({ size = 18, color = "currentColor" }) {
1408
+ return /* @__PURE__ */ jsx(
1409
+ "svg",
1410
+ {
1411
+ xmlns: "http://www.w3.org/2000/svg",
1412
+ width: size,
1413
+ height: size,
1414
+ viewBox: "0 0 24 24",
1415
+ fill: color,
1416
+ "aria-hidden": "true",
1417
+ children: /* @__PURE__ */ jsx("path", { d: "M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z" })
1418
+ }
1419
+ );
1420
+ }
1421
+ function MoreIcon({ size = 18, color = "currentColor" }) {
1422
+ return /* @__PURE__ */ jsxs(
1423
+ "svg",
1424
+ {
1425
+ xmlns: "http://www.w3.org/2000/svg",
1426
+ width: size,
1427
+ height: size,
1428
+ viewBox: "0 0 24 24",
1429
+ fill: color,
1430
+ "aria-hidden": "true",
1431
+ children: [
1432
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "2" }),
1433
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "2" }),
1434
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "2" })
1435
+ ]
1436
+ }
1437
+ );
1438
+ }
1376
1439
  function MessageActionsMenu({
1377
1440
  message,
1378
1441
  onReply,
1379
1442
  onPin,
1380
1443
  onUnpin,
1381
1444
  onCopy,
1382
- visible,
1383
1445
  pinDisabled = false,
1446
+ anchorRight = false,
1384
1447
  className
1385
1448
  }) {
1386
- if (!visible) return null;
1449
+ const [isOpen, setIsOpen] = useState(false);
1450
+ const wrapperRef = useRef(null);
1451
+ const triggerRef = useRef(null);
1387
1452
  const isPinned = !!message.isPinned;
1388
1453
  const canCopy = message.type === "text" && !!message.body;
1389
- const handleCopy = () => {
1390
- if (!onCopy) return;
1391
- onCopy(message.body);
1454
+ useEffect(() => {
1455
+ if (!isOpen) return;
1456
+ const handleClick = (e) => {
1457
+ if (!wrapperRef.current?.contains(e.target)) {
1458
+ setIsOpen(false);
1459
+ }
1460
+ };
1461
+ const handleKey = (e) => {
1462
+ if (e.key === "Escape") {
1463
+ setIsOpen(false);
1464
+ triggerRef.current?.focus();
1465
+ }
1466
+ };
1467
+ document.addEventListener("mousedown", handleClick);
1468
+ document.addEventListener("keydown", handleKey);
1469
+ return () => {
1470
+ document.removeEventListener("mousedown", handleClick);
1471
+ document.removeEventListener("keydown", handleKey);
1472
+ };
1473
+ }, [isOpen]);
1474
+ const run = (fn) => {
1475
+ setIsOpen(false);
1476
+ fn();
1392
1477
  };
1393
- return /* @__PURE__ */ jsxs("div", { className, style: styles4.container, children: [
1478
+ return /* @__PURE__ */ jsxs("div", { ref: wrapperRef, className, style: styles4.wrapper, children: [
1394
1479
  /* @__PURE__ */ jsx(
1395
1480
  "button",
1396
1481
  {
1397
- onClick: () => onReply(message),
1398
- style: styles4.button,
1399
- title: "Reply",
1482
+ ref: triggerRef,
1400
1483
  type: "button",
1401
- children: "\u21A9"
1484
+ onClick: (e) => {
1485
+ e.stopPropagation();
1486
+ setIsOpen((v) => !v);
1487
+ },
1488
+ "aria-label": "Message actions",
1489
+ "aria-haspopup": "menu",
1490
+ "aria-expanded": isOpen ? "true" : "false",
1491
+ title: "Message actions",
1492
+ style: styles4.trigger,
1493
+ children: /* @__PURE__ */ jsx(MoreIcon, { size: 18, color: "#4b5563" })
1402
1494
  }
1403
1495
  ),
1404
- isPinned ? /* @__PURE__ */ jsx(
1405
- "button",
1496
+ isOpen && /* @__PURE__ */ jsxs(
1497
+ "div",
1406
1498
  {
1407
- onClick: () => onUnpin(message.id),
1408
- style: styles4.button,
1409
- title: "Unpin",
1410
- type: "button",
1411
- children: "\u{1F4CC}"
1412
- }
1413
- ) : /* @__PURE__ */ jsx(
1414
- "button",
1415
- {
1416
- onClick: () => onPin(message.id),
1499
+ role: "menu",
1500
+ "aria-label": "Message actions",
1417
1501
  style: {
1418
- ...styles4.button,
1419
- opacity: pinDisabled ? 0.4 : 1,
1420
- cursor: pinDisabled ? "not-allowed" : "pointer"
1502
+ ...styles4.menu,
1503
+ ...anchorRight ? styles4.menuRight : styles4.menuLeft
1421
1504
  },
1422
- disabled: pinDisabled,
1423
- title: pinDisabled ? "Pin limit reached (max 3)" : "Pin",
1424
- type: "button",
1425
- children: "\u{1F4CC}"
1426
- }
1427
- ),
1428
- canCopy && onCopy && /* @__PURE__ */ jsx(
1429
- "button",
1430
- {
1431
- onClick: handleCopy,
1432
- style: styles4.button,
1433
- title: "Copy text",
1434
- type: "button",
1435
- children: "\u2398"
1505
+ children: [
1506
+ /* @__PURE__ */ jsxs(
1507
+ "button",
1508
+ {
1509
+ type: "button",
1510
+ role: "menuitem",
1511
+ onClick: () => run(() => onReply(message)),
1512
+ style: styles4.item,
1513
+ children: [
1514
+ /* @__PURE__ */ jsx(ReplyIcon, { size: 18, color: "#374151" }),
1515
+ /* @__PURE__ */ jsx("span", { children: "Reply" })
1516
+ ]
1517
+ }
1518
+ ),
1519
+ isPinned ? /* @__PURE__ */ jsxs(
1520
+ "button",
1521
+ {
1522
+ type: "button",
1523
+ role: "menuitem",
1524
+ onClick: () => run(() => onUnpin(message.id)),
1525
+ style: styles4.item,
1526
+ children: [
1527
+ /* @__PURE__ */ jsx(PinIcon, { size: 16, color: "#374151" }),
1528
+ /* @__PURE__ */ jsx("span", { children: "Unpin" })
1529
+ ]
1530
+ }
1531
+ ) : /* @__PURE__ */ jsxs(
1532
+ "button",
1533
+ {
1534
+ type: "button",
1535
+ role: "menuitem",
1536
+ onClick: () => run(() => onPin(message.id)),
1537
+ disabled: pinDisabled,
1538
+ style: {
1539
+ ...styles4.item,
1540
+ ...pinDisabled ? styles4.itemDisabled : {}
1541
+ },
1542
+ title: pinDisabled ? "Pin limit reached (max 3)" : void 0,
1543
+ children: [
1544
+ /* @__PURE__ */ jsx(PinIcon, { size: 16, color: "#374151" }),
1545
+ /* @__PURE__ */ jsx("span", { children: pinDisabled ? "Pin (limit reached)" : "Pin" })
1546
+ ]
1547
+ }
1548
+ ),
1549
+ canCopy && onCopy && /* @__PURE__ */ jsxs(
1550
+ "button",
1551
+ {
1552
+ type: "button",
1553
+ role: "menuitem",
1554
+ onClick: () => run(() => onCopy(message.body)),
1555
+ style: styles4.item,
1556
+ children: [
1557
+ /* @__PURE__ */ jsx(CopyIcon, { size: 16, color: "#374151" }),
1558
+ /* @__PURE__ */ jsx("span", { children: "Copy text" })
1559
+ ]
1560
+ }
1561
+ )
1562
+ ]
1436
1563
  }
1437
1564
  )
1438
1565
  ] });
1439
1566
  }
1440
1567
  var styles4 = {
1441
- container: {
1568
+ wrapper: {
1569
+ position: "relative",
1570
+ display: "inline-block"
1571
+ },
1572
+ trigger: {
1573
+ width: "36px",
1574
+ height: "36px",
1442
1575
  display: "flex",
1443
- gap: "2px",
1444
- padding: "2px",
1576
+ alignItems: "center",
1577
+ justifyContent: "center",
1578
+ backgroundColor: "transparent",
1579
+ border: "none",
1580
+ borderRadius: "50%",
1581
+ cursor: "pointer",
1582
+ color: "#4b5563"
1583
+ },
1584
+ menu: {
1585
+ position: "absolute",
1586
+ top: "100%",
1587
+ marginTop: "4px",
1588
+ minWidth: "160px",
1445
1589
  backgroundColor: "#ffffff",
1446
1590
  border: "1px solid #e5e7eb",
1447
- borderRadius: "20px",
1448
- boxShadow: "0 2px 8px rgba(0, 0, 0, 0.08)"
1591
+ borderRadius: "10px",
1592
+ boxShadow: "0 6px 18px rgba(0, 0, 0, 0.12)",
1593
+ padding: "4px",
1594
+ zIndex: 20
1449
1595
  },
1450
- button: {
1451
- width: "24px",
1452
- height: "24px",
1596
+ menuLeft: {
1597
+ left: 0
1598
+ },
1599
+ menuRight: {
1600
+ right: 0
1601
+ },
1602
+ item: {
1453
1603
  display: "flex",
1454
1604
  alignItems: "center",
1455
- justifyContent: "center",
1456
- fontSize: "12px",
1605
+ gap: "10px",
1606
+ width: "100%",
1607
+ minHeight: "40px",
1608
+ padding: "8px 12px",
1609
+ fontSize: "14px",
1610
+ color: "#111827",
1457
1611
  backgroundColor: "transparent",
1458
1612
  border: "none",
1459
- borderRadius: "50%",
1460
- cursor: "pointer"
1613
+ borderRadius: "6px",
1614
+ cursor: "pointer",
1615
+ textAlign: "left"
1616
+ },
1617
+ itemDisabled: {
1618
+ color: "#9ca3af",
1619
+ cursor: "not-allowed"
1461
1620
  }
1462
1621
  };
1463
1622
  function FileIcon({ size = 18, color = "currentColor" }) {
@@ -1474,20 +1633,6 @@ function FileIcon({ size = 18, color = "currentColor" }) {
1474
1633
  }
1475
1634
  );
1476
1635
  }
1477
- function PinIcon({ size = 14, color = "currentColor" }) {
1478
- return /* @__PURE__ */ jsx(
1479
- "svg",
1480
- {
1481
- xmlns: "http://www.w3.org/2000/svg",
1482
- width: size,
1483
- height: size,
1484
- viewBox: "0 0 24 24",
1485
- fill: color,
1486
- "aria-hidden": "true",
1487
- children: /* @__PURE__ */ jsx("path", { d: "M16 9V4l1 0c.55 0 1-.45 1-1s-.45-1-1-1H7c-.55 0-1 .45-1 1s.45 1 1 1l1 0v5c0 1.66-1.34 3-3 3v2h5.97v7l1 1 1-1v-7H19v-2c-1.66 0-3-1.34-3-3z" })
1488
- }
1489
- );
1490
- }
1491
1636
  function AlertIcon({ size = 14, color = "currentColor" }) {
1492
1637
  return /* @__PURE__ */ jsx(
1493
1638
  "svg",
@@ -1552,12 +1697,26 @@ function MessageBubble({
1552
1697
  pinDisabled = false,
1553
1698
  className
1554
1699
  }) {
1555
- const [isHovered, setIsHovered] = useState(false);
1556
1700
  if (message.type === "system") {
1557
1701
  return /* @__PURE__ */ jsx(SystemBubble, { message });
1558
1702
  }
1559
1703
  const hasActions = !!(onReply || onPin || onUnpin || onCopy);
1560
- return /* @__PURE__ */ jsx(
1704
+ const actionsSlot = hasActions ? /* @__PURE__ */ jsx("div", { style: styles5.actionsSlot, children: /* @__PURE__ */ jsx(
1705
+ MessageActionsMenu,
1706
+ {
1707
+ message,
1708
+ onReply: onReply ?? (() => {
1709
+ }),
1710
+ onPin: onPin ?? (() => {
1711
+ }),
1712
+ onUnpin: onUnpin ?? (() => {
1713
+ }),
1714
+ onCopy,
1715
+ pinDisabled,
1716
+ anchorRight: !isOwn
1717
+ }
1718
+ ) }) : null;
1719
+ return /* @__PURE__ */ jsxs(
1561
1720
  "div",
1562
1721
  {
1563
1722
  className,
@@ -1565,10 +1724,9 @@ function MessageBubble({
1565
1724
  ...styles5.wrapper,
1566
1725
  justifyContent: isOwn ? "flex-end" : "flex-start"
1567
1726
  },
1568
- onMouseEnter: () => setIsHovered(true),
1569
- onMouseLeave: () => setIsHovered(false),
1570
- children: /* @__PURE__ */ jsxs("div", { style: styles5.bubbleWrapper, children: [
1571
- /* @__PURE__ */ jsxs(
1727
+ children: [
1728
+ isOwn && actionsSlot,
1729
+ /* @__PURE__ */ jsx("div", { style: styles5.bubbleWrapper, children: /* @__PURE__ */ jsxs(
1572
1730
  "div",
1573
1731
  {
1574
1732
  style: {
@@ -1627,33 +1785,9 @@ function MessageBubble({
1627
1785
  )
1628
1786
  ]
1629
1787
  }
1630
- ),
1631
- hasActions && /* @__PURE__ */ jsx(
1632
- "div",
1633
- {
1634
- style: {
1635
- ...styles5.actionsWrapper,
1636
- [isOwn ? "right" : "left"]: "100%",
1637
- [isOwn ? "marginRight" : "marginLeft"]: "6px"
1638
- },
1639
- children: /* @__PURE__ */ jsx(
1640
- MessageActionsMenu,
1641
- {
1642
- message,
1643
- onReply: onReply ?? (() => {
1644
- }),
1645
- onPin: onPin ?? (() => {
1646
- }),
1647
- onUnpin: onUnpin ?? (() => {
1648
- }),
1649
- onCopy,
1650
- visible: isHovered,
1651
- pinDisabled
1652
- }
1653
- )
1654
- }
1655
- )
1656
- ] })
1788
+ ) }),
1789
+ !isOwn && actionsSlot
1790
+ ]
1657
1791
  }
1658
1792
  );
1659
1793
  }
@@ -1750,6 +1884,8 @@ function escapeRegex2(string) {
1750
1884
  var styles5 = {
1751
1885
  wrapper: {
1752
1886
  display: "flex",
1887
+ alignItems: "flex-end",
1888
+ gap: "4px",
1753
1889
  marginBottom: "8px",
1754
1890
  paddingLeft: "16px",
1755
1891
  paddingRight: "16px"
@@ -1758,6 +1894,10 @@ var styles5 = {
1758
1894
  position: "relative",
1759
1895
  maxWidth: "75%"
1760
1896
  },
1897
+ actionsSlot: {
1898
+ flexShrink: 0,
1899
+ alignSelf: "flex-end"
1900
+ },
1761
1901
  bubble: {
1762
1902
  padding: "10px 14px",
1763
1903
  borderRadius: "12px",
@@ -1833,12 +1973,6 @@ var styles5 = {
1833
1973
  cursor: "pointer",
1834
1974
  padding: "0 2px"
1835
1975
  },
1836
- actionsWrapper: {
1837
- position: "absolute",
1838
- top: "50%",
1839
- transform: "translateY(-50%)",
1840
- zIndex: 10
1841
- },
1842
1976
  audioContainer: {
1843
1977
  display: "flex",
1844
1978
  alignItems: "center",
@@ -2488,7 +2622,18 @@ function MessageInput({
2488
2622
  type: "file",
2489
2623
  onChange: handleFileSelect,
2490
2624
  style: { display: "none" },
2491
- accept: [...SUPPORTED_IMAGE_TYPES, "application/pdf", ".doc", ".docx"].join(",")
2625
+ accept: [
2626
+ ...SUPPORTED_IMAGE_TYPES,
2627
+ "application/pdf",
2628
+ ".doc",
2629
+ ".docx",
2630
+ ".xls",
2631
+ ".xlsx",
2632
+ ".csv",
2633
+ ".txt",
2634
+ ".heic",
2635
+ ".heif"
2636
+ ].join(",")
2492
2637
  }
2493
2638
  ),
2494
2639
  /* @__PURE__ */ jsx(
@@ -3500,12 +3645,14 @@ function ExpandIcon({ size = 18, color = "currentColor" }) {
3500
3645
  }
3501
3646
  );
3502
3647
  }
3648
+ var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
3503
3649
  function CollabPopup({
3504
3650
  orderId,
3505
3651
  patientData,
3506
3652
  participantIds,
3507
3653
  isOpen,
3508
3654
  onClose,
3655
+ onBack,
3509
3656
  initialPosition,
3510
3657
  width = 380,
3511
3658
  height = 520,
@@ -3521,6 +3668,8 @@ function CollabPopup({
3521
3668
  const [isMinimized, setIsMinimized] = useState(false);
3522
3669
  const dragOffset = useRef({ x: 0, y: 0 });
3523
3670
  const containerRef = useRef(null);
3671
+ const previouslyFocused = useRef(null);
3672
+ const titleId = useId();
3524
3673
  const handleMouseDown = useCallback(
3525
3674
  (e) => {
3526
3675
  if (!e.target.closest("[data-drag-handle]")) return;
@@ -3535,13 +3684,44 @@ function CollabPopup({
3535
3684
  );
3536
3685
  useEffect(() => {
3537
3686
  if (!isOpen) return;
3687
+ previouslyFocused.current = document.activeElement;
3688
+ const focusFirst = () => {
3689
+ const root = containerRef.current;
3690
+ if (!root) return;
3691
+ const first = root.querySelector(FOCUSABLE_SELECTOR);
3692
+ (first ?? root).focus();
3693
+ };
3694
+ const raf = requestAnimationFrame(focusFirst);
3538
3695
  const handleKey = (e) => {
3539
3696
  if (e.key === "Escape") {
3540
3697
  onClose();
3698
+ return;
3699
+ }
3700
+ if (e.key !== "Tab") return;
3701
+ const root = containerRef.current;
3702
+ if (!root) return;
3703
+ const focusables = Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR));
3704
+ if (focusables.length === 0) {
3705
+ e.preventDefault();
3706
+ return;
3707
+ }
3708
+ const first = focusables[0];
3709
+ const last = focusables[focusables.length - 1];
3710
+ const active = document.activeElement;
3711
+ if (e.shiftKey && active === first) {
3712
+ e.preventDefault();
3713
+ last.focus();
3714
+ } else if (!e.shiftKey && active === last) {
3715
+ e.preventDefault();
3716
+ first.focus();
3541
3717
  }
3542
3718
  };
3543
3719
  window.addEventListener("keydown", handleKey);
3544
- return () => window.removeEventListener("keydown", handleKey);
3720
+ return () => {
3721
+ cancelAnimationFrame(raf);
3722
+ window.removeEventListener("keydown", handleKey);
3723
+ previouslyFocused.current?.focus?.();
3724
+ };
3545
3725
  }, [isOpen, onClose]);
3546
3726
  useEffect(() => {
3547
3727
  if (!isDragging) return;
@@ -3567,6 +3747,10 @@ function CollabPopup({
3567
3747
  {
3568
3748
  ref: containerRef,
3569
3749
  className,
3750
+ role: "dialog",
3751
+ "aria-modal": "true",
3752
+ "aria-labelledby": titleId,
3753
+ tabIndex: -1,
3570
3754
  style: {
3571
3755
  ...styles13.container,
3572
3756
  left: position.x,
@@ -3578,7 +3762,18 @@ function CollabPopup({
3578
3762
  onMouseDown: handleMouseDown,
3579
3763
  children: [
3580
3764
  /* @__PURE__ */ jsxs("div", { "data-drag-handle": true, style: styles13.titleBar, children: [
3581
- /* @__PURE__ */ jsx("span", { style: styles13.titleText, children: buildChannelName(patientData) }),
3765
+ onBack && /* @__PURE__ */ jsx(
3766
+ "button",
3767
+ {
3768
+ onClick: onBack,
3769
+ style: styles13.titleButton,
3770
+ "aria-label": "Go back",
3771
+ title: "Back",
3772
+ type: "button",
3773
+ children: "\u2190"
3774
+ }
3775
+ ),
3776
+ /* @__PURE__ */ jsx("span", { id: titleId, style: { ...styles13.titleText, flex: 1 }, children: buildChannelName(patientData) }),
3582
3777
  /* @__PURE__ */ jsxs("div", { style: styles13.titleActions, children: [
3583
3778
  /* @__PURE__ */ jsx(
3584
3779
  "button",
@@ -3865,8 +4060,8 @@ function useInlineCollab({
3865
4060
  await dispatchSend({
3866
4061
  body: "Voice message",
3867
4062
  type: "audio",
3868
- mediaUrl: url,
3869
- mediaDuration: duration
4063
+ media_url: url,
4064
+ media_duration: duration
3870
4065
  });
3871
4066
  } catch (err) {
3872
4067
  setError(err instanceof Error ? err.message : "Failed to send voice message");