@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.d.mts +16 -9
- package/dist/index.d.ts +16 -9
- package/dist/index.js +310 -115
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +311 -116
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -376,7 +376,9 @@ function CollabProvider({ config, apiBaseUrl, children }) {
|
|
|
376
376
|
const data = snakeToCamel(await response.json());
|
|
377
377
|
orderIds.forEach((orderId) => {
|
|
378
378
|
const preview = data.previews[orderId] ?? null;
|
|
379
|
-
|
|
379
|
+
if (preview !== null) {
|
|
380
|
+
previewCache.current.set(orderId, preview);
|
|
381
|
+
}
|
|
380
382
|
resolvers.get(orderId)?.forEach((resolve) => resolve(preview));
|
|
381
383
|
});
|
|
382
384
|
} catch (error) {
|
|
@@ -423,7 +425,8 @@ function CollabProvider({ config, apiBaseUrl, children }) {
|
|
|
423
425
|
if (!response.ok) {
|
|
424
426
|
throw { code: "FETCH_ERROR", message: "Failed to fetch messages", status: response.status };
|
|
425
427
|
}
|
|
426
|
-
|
|
428
|
+
const msgs = snakeToCamel(await response.json());
|
|
429
|
+
return msgs.sort((a, b) => a.insertedAt.localeCompare(b.insertedAt));
|
|
427
430
|
},
|
|
428
431
|
[apiBaseUrl, authHeaders]
|
|
429
432
|
);
|
|
@@ -897,8 +900,8 @@ function useConversation({
|
|
|
897
900
|
const payload = {
|
|
898
901
|
body: "Voice message",
|
|
899
902
|
type: "audio",
|
|
900
|
-
|
|
901
|
-
|
|
903
|
+
media_url: url,
|
|
904
|
+
media_duration: duration
|
|
902
905
|
};
|
|
903
906
|
const { conv, persistedByCreate } = await ensureConversation(payload);
|
|
904
907
|
if (persistedByCreate) return;
|
|
@@ -914,8 +917,8 @@ function useConversation({
|
|
|
914
917
|
const payload = {
|
|
915
918
|
body: file.name,
|
|
916
919
|
type: isImage ? "image" : "file",
|
|
917
|
-
|
|
918
|
-
|
|
920
|
+
media_url: url,
|
|
921
|
+
file_name: file.name
|
|
919
922
|
};
|
|
920
923
|
const { conv, persistedByCreate } = await ensureConversation(payload);
|
|
921
924
|
if (persistedByCreate) return;
|
|
@@ -1379,91 +1382,247 @@ var styles3 = {
|
|
|
1379
1382
|
textDecoration: "underline"
|
|
1380
1383
|
}
|
|
1381
1384
|
};
|
|
1385
|
+
function ReplyIcon({ size = 18, color = "currentColor" }) {
|
|
1386
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1387
|
+
"svg",
|
|
1388
|
+
{
|
|
1389
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1390
|
+
width: size,
|
|
1391
|
+
height: size,
|
|
1392
|
+
viewBox: "0 0 24 24",
|
|
1393
|
+
fill: color,
|
|
1394
|
+
"aria-hidden": "true",
|
|
1395
|
+
children: /* @__PURE__ */ jsxRuntime.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" })
|
|
1396
|
+
}
|
|
1397
|
+
);
|
|
1398
|
+
}
|
|
1399
|
+
function PinIcon({ size = 14, color = "currentColor" }) {
|
|
1400
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1401
|
+
"svg",
|
|
1402
|
+
{
|
|
1403
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1404
|
+
width: size,
|
|
1405
|
+
height: size,
|
|
1406
|
+
viewBox: "0 0 24 24",
|
|
1407
|
+
fill: color,
|
|
1408
|
+
"aria-hidden": "true",
|
|
1409
|
+
children: /* @__PURE__ */ jsxRuntime.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" })
|
|
1410
|
+
}
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
function CopyIcon({ size = 18, color = "currentColor" }) {
|
|
1414
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1415
|
+
"svg",
|
|
1416
|
+
{
|
|
1417
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1418
|
+
width: size,
|
|
1419
|
+
height: size,
|
|
1420
|
+
viewBox: "0 0 24 24",
|
|
1421
|
+
fill: color,
|
|
1422
|
+
"aria-hidden": "true",
|
|
1423
|
+
children: /* @__PURE__ */ jsxRuntime.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" })
|
|
1424
|
+
}
|
|
1425
|
+
);
|
|
1426
|
+
}
|
|
1427
|
+
function MoreIcon({ size = 18, color = "currentColor" }) {
|
|
1428
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1429
|
+
"svg",
|
|
1430
|
+
{
|
|
1431
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1432
|
+
width: size,
|
|
1433
|
+
height: size,
|
|
1434
|
+
viewBox: "0 0 24 24",
|
|
1435
|
+
fill: color,
|
|
1436
|
+
"aria-hidden": "true",
|
|
1437
|
+
children: [
|
|
1438
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "5", r: "2" }),
|
|
1439
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "2" }),
|
|
1440
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "12", cy: "19", r: "2" })
|
|
1441
|
+
]
|
|
1442
|
+
}
|
|
1443
|
+
);
|
|
1444
|
+
}
|
|
1382
1445
|
function MessageActionsMenu({
|
|
1383
1446
|
message,
|
|
1384
1447
|
onReply,
|
|
1385
1448
|
onPin,
|
|
1386
1449
|
onUnpin,
|
|
1387
1450
|
onCopy,
|
|
1388
|
-
visible,
|
|
1389
1451
|
pinDisabled = false,
|
|
1452
|
+
anchorRight = false,
|
|
1390
1453
|
className
|
|
1391
1454
|
}) {
|
|
1392
|
-
|
|
1455
|
+
const [isOpen, setIsOpen] = React4.useState(false);
|
|
1456
|
+
const wrapperRef = React4.useRef(null);
|
|
1457
|
+
const triggerRef = React4.useRef(null);
|
|
1393
1458
|
const isPinned = !!message.isPinned;
|
|
1394
1459
|
const canCopy = message.type === "text" && !!message.body;
|
|
1395
|
-
|
|
1396
|
-
if (!
|
|
1397
|
-
|
|
1460
|
+
React4.useEffect(() => {
|
|
1461
|
+
if (!isOpen) return;
|
|
1462
|
+
const handleClick = (e) => {
|
|
1463
|
+
if (!wrapperRef.current?.contains(e.target)) {
|
|
1464
|
+
setIsOpen(false);
|
|
1465
|
+
}
|
|
1466
|
+
};
|
|
1467
|
+
const handleKey = (e) => {
|
|
1468
|
+
if (e.key === "Escape") {
|
|
1469
|
+
setIsOpen(false);
|
|
1470
|
+
triggerRef.current?.focus();
|
|
1471
|
+
}
|
|
1472
|
+
};
|
|
1473
|
+
document.addEventListener("mousedown", handleClick);
|
|
1474
|
+
document.addEventListener("keydown", handleKey);
|
|
1475
|
+
return () => {
|
|
1476
|
+
document.removeEventListener("mousedown", handleClick);
|
|
1477
|
+
document.removeEventListener("keydown", handleKey);
|
|
1478
|
+
};
|
|
1479
|
+
}, [isOpen]);
|
|
1480
|
+
const run = (fn) => {
|
|
1481
|
+
setIsOpen(false);
|
|
1482
|
+
fn();
|
|
1398
1483
|
};
|
|
1399
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: styles4.
|
|
1484
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: wrapperRef, className, style: styles4.wrapper, children: [
|
|
1400
1485
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1401
1486
|
"button",
|
|
1402
1487
|
{
|
|
1403
|
-
|
|
1404
|
-
style: styles4.button,
|
|
1405
|
-
title: "Reply",
|
|
1488
|
+
ref: triggerRef,
|
|
1406
1489
|
type: "button",
|
|
1407
|
-
|
|
1490
|
+
onClick: (e) => {
|
|
1491
|
+
e.stopPropagation();
|
|
1492
|
+
setIsOpen((v) => !v);
|
|
1493
|
+
},
|
|
1494
|
+
"aria-label": "Message actions",
|
|
1495
|
+
"aria-haspopup": "menu",
|
|
1496
|
+
"aria-expanded": isOpen ? "true" : "false",
|
|
1497
|
+
title: "Message actions",
|
|
1498
|
+
style: styles4.trigger,
|
|
1499
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(MoreIcon, { size: 18, color: "#4b5563" })
|
|
1408
1500
|
}
|
|
1409
1501
|
),
|
|
1410
|
-
|
|
1411
|
-
"
|
|
1502
|
+
isOpen && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1503
|
+
"div",
|
|
1412
1504
|
{
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
title: "Unpin",
|
|
1416
|
-
type: "button",
|
|
1417
|
-
children: "\u{1F4CC}"
|
|
1418
|
-
}
|
|
1419
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
1420
|
-
"button",
|
|
1421
|
-
{
|
|
1422
|
-
onClick: () => onPin(message.id),
|
|
1505
|
+
role: "menu",
|
|
1506
|
+
"aria-label": "Message actions",
|
|
1423
1507
|
style: {
|
|
1424
|
-
...styles4.
|
|
1425
|
-
|
|
1426
|
-
cursor: pinDisabled ? "not-allowed" : "pointer"
|
|
1508
|
+
...styles4.menu,
|
|
1509
|
+
...anchorRight ? styles4.menuRight : styles4.menuLeft
|
|
1427
1510
|
},
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1511
|
+
children: [
|
|
1512
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1513
|
+
"button",
|
|
1514
|
+
{
|
|
1515
|
+
type: "button",
|
|
1516
|
+
role: "menuitem",
|
|
1517
|
+
onClick: () => run(() => onReply(message)),
|
|
1518
|
+
style: styles4.item,
|
|
1519
|
+
children: [
|
|
1520
|
+
/* @__PURE__ */ jsxRuntime.jsx(ReplyIcon, { size: 18, color: "#374151" }),
|
|
1521
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Reply" })
|
|
1522
|
+
]
|
|
1523
|
+
}
|
|
1524
|
+
),
|
|
1525
|
+
isPinned ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1526
|
+
"button",
|
|
1527
|
+
{
|
|
1528
|
+
type: "button",
|
|
1529
|
+
role: "menuitem",
|
|
1530
|
+
onClick: () => run(() => onUnpin(message.id)),
|
|
1531
|
+
style: styles4.item,
|
|
1532
|
+
children: [
|
|
1533
|
+
/* @__PURE__ */ jsxRuntime.jsx(PinIcon, { size: 16, color: "#374151" }),
|
|
1534
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Unpin" })
|
|
1535
|
+
]
|
|
1536
|
+
}
|
|
1537
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1538
|
+
"button",
|
|
1539
|
+
{
|
|
1540
|
+
type: "button",
|
|
1541
|
+
role: "menuitem",
|
|
1542
|
+
onClick: () => run(() => onPin(message.id)),
|
|
1543
|
+
disabled: pinDisabled,
|
|
1544
|
+
style: {
|
|
1545
|
+
...styles4.item,
|
|
1546
|
+
...pinDisabled ? styles4.itemDisabled : {}
|
|
1547
|
+
},
|
|
1548
|
+
title: pinDisabled ? "Pin limit reached (max 3)" : void 0,
|
|
1549
|
+
children: [
|
|
1550
|
+
/* @__PURE__ */ jsxRuntime.jsx(PinIcon, { size: 16, color: "#374151" }),
|
|
1551
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: pinDisabled ? "Pin (limit reached)" : "Pin" })
|
|
1552
|
+
]
|
|
1553
|
+
}
|
|
1554
|
+
),
|
|
1555
|
+
canCopy && onCopy && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1556
|
+
"button",
|
|
1557
|
+
{
|
|
1558
|
+
type: "button",
|
|
1559
|
+
role: "menuitem",
|
|
1560
|
+
onClick: () => run(() => onCopy(message.body)),
|
|
1561
|
+
style: styles4.item,
|
|
1562
|
+
children: [
|
|
1563
|
+
/* @__PURE__ */ jsxRuntime.jsx(CopyIcon, { size: 16, color: "#374151" }),
|
|
1564
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Copy text" })
|
|
1565
|
+
]
|
|
1566
|
+
}
|
|
1567
|
+
)
|
|
1568
|
+
]
|
|
1442
1569
|
}
|
|
1443
1570
|
)
|
|
1444
1571
|
] });
|
|
1445
1572
|
}
|
|
1446
1573
|
var styles4 = {
|
|
1447
|
-
|
|
1574
|
+
wrapper: {
|
|
1575
|
+
position: "relative",
|
|
1576
|
+
display: "inline-block"
|
|
1577
|
+
},
|
|
1578
|
+
trigger: {
|
|
1579
|
+
width: "36px",
|
|
1580
|
+
height: "36px",
|
|
1448
1581
|
display: "flex",
|
|
1449
|
-
|
|
1450
|
-
|
|
1582
|
+
alignItems: "center",
|
|
1583
|
+
justifyContent: "center",
|
|
1584
|
+
backgroundColor: "transparent",
|
|
1585
|
+
border: "none",
|
|
1586
|
+
borderRadius: "50%",
|
|
1587
|
+
cursor: "pointer",
|
|
1588
|
+
color: "#4b5563"
|
|
1589
|
+
},
|
|
1590
|
+
menu: {
|
|
1591
|
+
position: "absolute",
|
|
1592
|
+
top: "100%",
|
|
1593
|
+
marginTop: "4px",
|
|
1594
|
+
minWidth: "160px",
|
|
1451
1595
|
backgroundColor: "#ffffff",
|
|
1452
1596
|
border: "1px solid #e5e7eb",
|
|
1453
|
-
borderRadius: "
|
|
1454
|
-
boxShadow: "0
|
|
1597
|
+
borderRadius: "10px",
|
|
1598
|
+
boxShadow: "0 6px 18px rgba(0, 0, 0, 0.12)",
|
|
1599
|
+
padding: "4px",
|
|
1600
|
+
zIndex: 20
|
|
1455
1601
|
},
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1602
|
+
menuLeft: {
|
|
1603
|
+
left: 0
|
|
1604
|
+
},
|
|
1605
|
+
menuRight: {
|
|
1606
|
+
right: 0
|
|
1607
|
+
},
|
|
1608
|
+
item: {
|
|
1459
1609
|
display: "flex",
|
|
1460
1610
|
alignItems: "center",
|
|
1461
|
-
|
|
1462
|
-
|
|
1611
|
+
gap: "10px",
|
|
1612
|
+
width: "100%",
|
|
1613
|
+
minHeight: "40px",
|
|
1614
|
+
padding: "8px 12px",
|
|
1615
|
+
fontSize: "14px",
|
|
1616
|
+
color: "#111827",
|
|
1463
1617
|
backgroundColor: "transparent",
|
|
1464
1618
|
border: "none",
|
|
1465
|
-
borderRadius: "
|
|
1466
|
-
cursor: "pointer"
|
|
1619
|
+
borderRadius: "6px",
|
|
1620
|
+
cursor: "pointer",
|
|
1621
|
+
textAlign: "left"
|
|
1622
|
+
},
|
|
1623
|
+
itemDisabled: {
|
|
1624
|
+
color: "#9ca3af",
|
|
1625
|
+
cursor: "not-allowed"
|
|
1467
1626
|
}
|
|
1468
1627
|
};
|
|
1469
1628
|
function FileIcon({ size = 18, color = "currentColor" }) {
|
|
@@ -1480,20 +1639,6 @@ function FileIcon({ size = 18, color = "currentColor" }) {
|
|
|
1480
1639
|
}
|
|
1481
1640
|
);
|
|
1482
1641
|
}
|
|
1483
|
-
function PinIcon({ size = 14, color = "currentColor" }) {
|
|
1484
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1485
|
-
"svg",
|
|
1486
|
-
{
|
|
1487
|
-
xmlns: "http://www.w3.org/2000/svg",
|
|
1488
|
-
width: size,
|
|
1489
|
-
height: size,
|
|
1490
|
-
viewBox: "0 0 24 24",
|
|
1491
|
-
fill: color,
|
|
1492
|
-
"aria-hidden": "true",
|
|
1493
|
-
children: /* @__PURE__ */ jsxRuntime.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" })
|
|
1494
|
-
}
|
|
1495
|
-
);
|
|
1496
|
-
}
|
|
1497
1642
|
function AlertIcon({ size = 14, color = "currentColor" }) {
|
|
1498
1643
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1499
1644
|
"svg",
|
|
@@ -1558,12 +1703,26 @@ function MessageBubble({
|
|
|
1558
1703
|
pinDisabled = false,
|
|
1559
1704
|
className
|
|
1560
1705
|
}) {
|
|
1561
|
-
const [isHovered, setIsHovered] = React4.useState(false);
|
|
1562
1706
|
if (message.type === "system") {
|
|
1563
1707
|
return /* @__PURE__ */ jsxRuntime.jsx(SystemBubble, { message });
|
|
1564
1708
|
}
|
|
1565
1709
|
const hasActions = !!(onReply || onPin || onUnpin || onCopy);
|
|
1566
|
-
|
|
1710
|
+
const actionsSlot = hasActions ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.actionsSlot, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1711
|
+
MessageActionsMenu,
|
|
1712
|
+
{
|
|
1713
|
+
message,
|
|
1714
|
+
onReply: onReply ?? (() => {
|
|
1715
|
+
}),
|
|
1716
|
+
onPin: onPin ?? (() => {
|
|
1717
|
+
}),
|
|
1718
|
+
onUnpin: onUnpin ?? (() => {
|
|
1719
|
+
}),
|
|
1720
|
+
onCopy,
|
|
1721
|
+
pinDisabled,
|
|
1722
|
+
anchorRight: !isOwn
|
|
1723
|
+
}
|
|
1724
|
+
) }) : null;
|
|
1725
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1567
1726
|
"div",
|
|
1568
1727
|
{
|
|
1569
1728
|
className,
|
|
@@ -1571,10 +1730,9 @@ function MessageBubble({
|
|
|
1571
1730
|
...styles5.wrapper,
|
|
1572
1731
|
justifyContent: isOwn ? "flex-end" : "flex-start"
|
|
1573
1732
|
},
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1733
|
+
children: [
|
|
1734
|
+
isOwn && actionsSlot,
|
|
1735
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: styles5.bubbleWrapper, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1578
1736
|
"div",
|
|
1579
1737
|
{
|
|
1580
1738
|
style: {
|
|
@@ -1633,33 +1791,9 @@ function MessageBubble({
|
|
|
1633
1791
|
)
|
|
1634
1792
|
]
|
|
1635
1793
|
}
|
|
1636
|
-
),
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
{
|
|
1640
|
-
style: {
|
|
1641
|
-
...styles5.actionsWrapper,
|
|
1642
|
-
[isOwn ? "right" : "left"]: "100%",
|
|
1643
|
-
[isOwn ? "marginRight" : "marginLeft"]: "6px"
|
|
1644
|
-
},
|
|
1645
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1646
|
-
MessageActionsMenu,
|
|
1647
|
-
{
|
|
1648
|
-
message,
|
|
1649
|
-
onReply: onReply ?? (() => {
|
|
1650
|
-
}),
|
|
1651
|
-
onPin: onPin ?? (() => {
|
|
1652
|
-
}),
|
|
1653
|
-
onUnpin: onUnpin ?? (() => {
|
|
1654
|
-
}),
|
|
1655
|
-
onCopy,
|
|
1656
|
-
visible: isHovered,
|
|
1657
|
-
pinDisabled
|
|
1658
|
-
}
|
|
1659
|
-
)
|
|
1660
|
-
}
|
|
1661
|
-
)
|
|
1662
|
-
] })
|
|
1794
|
+
) }),
|
|
1795
|
+
!isOwn && actionsSlot
|
|
1796
|
+
]
|
|
1663
1797
|
}
|
|
1664
1798
|
);
|
|
1665
1799
|
}
|
|
@@ -1756,6 +1890,8 @@ function escapeRegex2(string) {
|
|
|
1756
1890
|
var styles5 = {
|
|
1757
1891
|
wrapper: {
|
|
1758
1892
|
display: "flex",
|
|
1893
|
+
alignItems: "flex-end",
|
|
1894
|
+
gap: "4px",
|
|
1759
1895
|
marginBottom: "8px",
|
|
1760
1896
|
paddingLeft: "16px",
|
|
1761
1897
|
paddingRight: "16px"
|
|
@@ -1764,6 +1900,10 @@ var styles5 = {
|
|
|
1764
1900
|
position: "relative",
|
|
1765
1901
|
maxWidth: "75%"
|
|
1766
1902
|
},
|
|
1903
|
+
actionsSlot: {
|
|
1904
|
+
flexShrink: 0,
|
|
1905
|
+
alignSelf: "flex-end"
|
|
1906
|
+
},
|
|
1767
1907
|
bubble: {
|
|
1768
1908
|
padding: "10px 14px",
|
|
1769
1909
|
borderRadius: "12px",
|
|
@@ -1839,12 +1979,6 @@ var styles5 = {
|
|
|
1839
1979
|
cursor: "pointer",
|
|
1840
1980
|
padding: "0 2px"
|
|
1841
1981
|
},
|
|
1842
|
-
actionsWrapper: {
|
|
1843
|
-
position: "absolute",
|
|
1844
|
-
top: "50%",
|
|
1845
|
-
transform: "translateY(-50%)",
|
|
1846
|
-
zIndex: 10
|
|
1847
|
-
},
|
|
1848
1982
|
audioContainer: {
|
|
1849
1983
|
display: "flex",
|
|
1850
1984
|
alignItems: "center",
|
|
@@ -2494,7 +2628,18 @@ function MessageInput({
|
|
|
2494
2628
|
type: "file",
|
|
2495
2629
|
onChange: handleFileSelect,
|
|
2496
2630
|
style: { display: "none" },
|
|
2497
|
-
accept: [
|
|
2631
|
+
accept: [
|
|
2632
|
+
...SUPPORTED_IMAGE_TYPES,
|
|
2633
|
+
"application/pdf",
|
|
2634
|
+
".doc",
|
|
2635
|
+
".docx",
|
|
2636
|
+
".xls",
|
|
2637
|
+
".xlsx",
|
|
2638
|
+
".csv",
|
|
2639
|
+
".txt",
|
|
2640
|
+
".heic",
|
|
2641
|
+
".heif"
|
|
2642
|
+
].join(",")
|
|
2498
2643
|
}
|
|
2499
2644
|
),
|
|
2500
2645
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -3506,12 +3651,14 @@ function ExpandIcon({ size = 18, color = "currentColor" }) {
|
|
|
3506
3651
|
}
|
|
3507
3652
|
);
|
|
3508
3653
|
}
|
|
3654
|
+
var FOCUSABLE_SELECTOR = 'a[href], button:not([disabled]), textarea:not([disabled]), input:not([disabled]), select:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
3509
3655
|
function CollabPopup({
|
|
3510
3656
|
orderId,
|
|
3511
3657
|
patientData,
|
|
3512
3658
|
participantIds,
|
|
3513
3659
|
isOpen,
|
|
3514
3660
|
onClose,
|
|
3661
|
+
onBack,
|
|
3515
3662
|
initialPosition,
|
|
3516
3663
|
width = 380,
|
|
3517
3664
|
height = 520,
|
|
@@ -3527,6 +3674,8 @@ function CollabPopup({
|
|
|
3527
3674
|
const [isMinimized, setIsMinimized] = React4.useState(false);
|
|
3528
3675
|
const dragOffset = React4.useRef({ x: 0, y: 0 });
|
|
3529
3676
|
const containerRef = React4.useRef(null);
|
|
3677
|
+
const previouslyFocused = React4.useRef(null);
|
|
3678
|
+
const titleId = React4.useId();
|
|
3530
3679
|
const handleMouseDown = React4.useCallback(
|
|
3531
3680
|
(e) => {
|
|
3532
3681
|
if (!e.target.closest("[data-drag-handle]")) return;
|
|
@@ -3541,13 +3690,44 @@ function CollabPopup({
|
|
|
3541
3690
|
);
|
|
3542
3691
|
React4.useEffect(() => {
|
|
3543
3692
|
if (!isOpen) return;
|
|
3693
|
+
previouslyFocused.current = document.activeElement;
|
|
3694
|
+
const focusFirst = () => {
|
|
3695
|
+
const root = containerRef.current;
|
|
3696
|
+
if (!root) return;
|
|
3697
|
+
const first = root.querySelector(FOCUSABLE_SELECTOR);
|
|
3698
|
+
(first ?? root).focus();
|
|
3699
|
+
};
|
|
3700
|
+
const raf = requestAnimationFrame(focusFirst);
|
|
3544
3701
|
const handleKey = (e) => {
|
|
3545
3702
|
if (e.key === "Escape") {
|
|
3546
3703
|
onClose();
|
|
3704
|
+
return;
|
|
3705
|
+
}
|
|
3706
|
+
if (e.key !== "Tab") return;
|
|
3707
|
+
const root = containerRef.current;
|
|
3708
|
+
if (!root) return;
|
|
3709
|
+
const focusables = Array.from(root.querySelectorAll(FOCUSABLE_SELECTOR));
|
|
3710
|
+
if (focusables.length === 0) {
|
|
3711
|
+
e.preventDefault();
|
|
3712
|
+
return;
|
|
3713
|
+
}
|
|
3714
|
+
const first = focusables[0];
|
|
3715
|
+
const last = focusables[focusables.length - 1];
|
|
3716
|
+
const active = document.activeElement;
|
|
3717
|
+
if (e.shiftKey && active === first) {
|
|
3718
|
+
e.preventDefault();
|
|
3719
|
+
last.focus();
|
|
3720
|
+
} else if (!e.shiftKey && active === last) {
|
|
3721
|
+
e.preventDefault();
|
|
3722
|
+
first.focus();
|
|
3547
3723
|
}
|
|
3548
3724
|
};
|
|
3549
3725
|
window.addEventListener("keydown", handleKey);
|
|
3550
|
-
return () =>
|
|
3726
|
+
return () => {
|
|
3727
|
+
cancelAnimationFrame(raf);
|
|
3728
|
+
window.removeEventListener("keydown", handleKey);
|
|
3729
|
+
previouslyFocused.current?.focus?.();
|
|
3730
|
+
};
|
|
3551
3731
|
}, [isOpen, onClose]);
|
|
3552
3732
|
React4.useEffect(() => {
|
|
3553
3733
|
if (!isDragging) return;
|
|
@@ -3573,6 +3753,10 @@ function CollabPopup({
|
|
|
3573
3753
|
{
|
|
3574
3754
|
ref: containerRef,
|
|
3575
3755
|
className,
|
|
3756
|
+
role: "dialog",
|
|
3757
|
+
"aria-modal": "true",
|
|
3758
|
+
"aria-labelledby": titleId,
|
|
3759
|
+
tabIndex: -1,
|
|
3576
3760
|
style: {
|
|
3577
3761
|
...styles13.container,
|
|
3578
3762
|
left: position.x,
|
|
@@ -3584,7 +3768,18 @@ function CollabPopup({
|
|
|
3584
3768
|
onMouseDown: handleMouseDown,
|
|
3585
3769
|
children: [
|
|
3586
3770
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { "data-drag-handle": true, style: styles13.titleBar, children: [
|
|
3587
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3771
|
+
onBack && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3772
|
+
"button",
|
|
3773
|
+
{
|
|
3774
|
+
onClick: onBack,
|
|
3775
|
+
style: styles13.titleButton,
|
|
3776
|
+
"aria-label": "Go back",
|
|
3777
|
+
title: "Back",
|
|
3778
|
+
type: "button",
|
|
3779
|
+
children: "\u2190"
|
|
3780
|
+
}
|
|
3781
|
+
),
|
|
3782
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { id: titleId, style: { ...styles13.titleText, flex: 1 }, children: buildChannelName(patientData) }),
|
|
3588
3783
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: styles13.titleActions, children: [
|
|
3589
3784
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3590
3785
|
"button",
|
|
@@ -3871,8 +4066,8 @@ function useInlineCollab({
|
|
|
3871
4066
|
await dispatchSend({
|
|
3872
4067
|
body: "Voice message",
|
|
3873
4068
|
type: "audio",
|
|
3874
|
-
|
|
3875
|
-
|
|
4069
|
+
media_url: url,
|
|
4070
|
+
media_duration: duration
|
|
3876
4071
|
});
|
|
3877
4072
|
} catch (err) {
|
|
3878
4073
|
setError(err instanceof Error ? err.message : "Failed to send voice message");
|