vg-x07df 1.8.3 → 1.9.0
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/channel/index.cjs +256 -20
- package/dist/channel/index.cjs.map +1 -1
- package/dist/channel/index.d.cts +67 -8
- package/dist/channel/index.d.ts +67 -8
- package/dist/channel/index.mjs +254 -21
- package/dist/channel/index.mjs.map +1 -1
- package/dist/index.cjs +165 -71
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +30 -29
- package/dist/index.d.ts +30 -29
- package/dist/index.mjs +165 -71
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/channel/index.cjs
CHANGED
|
@@ -508,10 +508,8 @@ var ChatService = class {
|
|
|
508
508
|
});
|
|
509
509
|
return;
|
|
510
510
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
}
|
|
514
|
-
const validation = validateContent(content);
|
|
511
|
+
const messageContent = content.length === 0 && typeof file !== "undefined" ? file.name : content;
|
|
512
|
+
const validation = validateContent(messageContent);
|
|
515
513
|
if (!validation.valid) {
|
|
516
514
|
useRtcStore.getState().addError({
|
|
517
515
|
code: "CHAT_INVALID_CONTENT",
|
|
@@ -530,7 +528,7 @@ var ChatService = class {
|
|
|
530
528
|
}
|
|
531
529
|
const entry = {
|
|
532
530
|
id: entryId,
|
|
533
|
-
content,
|
|
531
|
+
content: messageContent,
|
|
534
532
|
sender: {
|
|
535
533
|
id: senderInfo.id
|
|
536
534
|
},
|
|
@@ -1426,6 +1424,236 @@ function useReactions() {
|
|
|
1426
1424
|
isReady: !!service
|
|
1427
1425
|
};
|
|
1428
1426
|
}
|
|
1427
|
+
var defaultState4 = {
|
|
1428
|
+
spotlightedUser: null
|
|
1429
|
+
};
|
|
1430
|
+
var useSpotlightStore = zustand.create()(
|
|
1431
|
+
immer.immer((set, get) => ({
|
|
1432
|
+
...defaultState4,
|
|
1433
|
+
spotlight: (participantId, info) => set((state) => {
|
|
1434
|
+
state.spotlightedUser = {
|
|
1435
|
+
participantId,
|
|
1436
|
+
ts: Date.now(),
|
|
1437
|
+
...info && { info }
|
|
1438
|
+
};
|
|
1439
|
+
}),
|
|
1440
|
+
unspotlight: () => set((state) => {
|
|
1441
|
+
state.spotlightedUser = null;
|
|
1442
|
+
}),
|
|
1443
|
+
getSpotlightedUser: () => get().spotlightedUser,
|
|
1444
|
+
isSpotlighted: (participantId) => {
|
|
1445
|
+
const user = get().spotlightedUser;
|
|
1446
|
+
return user?.participantId === participantId;
|
|
1447
|
+
},
|
|
1448
|
+
clear: () => set(() => ({
|
|
1449
|
+
spotlightedUser: null
|
|
1450
|
+
}))
|
|
1451
|
+
}))
|
|
1452
|
+
);
|
|
1453
|
+
function applyIncomingSpotlight(envelope) {
|
|
1454
|
+
const { spotlight, unspotlight } = useSpotlightStore.getState();
|
|
1455
|
+
switch (envelope.payload.action) {
|
|
1456
|
+
case "spotlight": {
|
|
1457
|
+
spotlight(envelope.payload.targetId);
|
|
1458
|
+
break;
|
|
1459
|
+
}
|
|
1460
|
+
case "unspotlight": {
|
|
1461
|
+
unspotlight();
|
|
1462
|
+
break;
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
// src/channel/spotlight/service.ts
|
|
1468
|
+
var logger7 = createLogger("spotlight");
|
|
1469
|
+
var SpotlightService = class {
|
|
1470
|
+
constructor(room) {
|
|
1471
|
+
this.isSubscribed = false;
|
|
1472
|
+
this.room = room;
|
|
1473
|
+
}
|
|
1474
|
+
isRoomReady() {
|
|
1475
|
+
if (!this.room) {
|
|
1476
|
+
logger7.warn("Room not initialized");
|
|
1477
|
+
return false;
|
|
1478
|
+
}
|
|
1479
|
+
if (this.room.state !== livekitClient.ConnectionState.Connected) {
|
|
1480
|
+
logger7.warn("Room not connected", { state: this.room.state });
|
|
1481
|
+
return false;
|
|
1482
|
+
}
|
|
1483
|
+
if (!this.room.localParticipant) {
|
|
1484
|
+
logger7.warn("Local participant not available");
|
|
1485
|
+
return false;
|
|
1486
|
+
}
|
|
1487
|
+
return true;
|
|
1488
|
+
}
|
|
1489
|
+
subscribe() {
|
|
1490
|
+
if (this.isSubscribed) return;
|
|
1491
|
+
this.room.registerTextStreamHandler("spotlight:v1", async (reader) => {
|
|
1492
|
+
try {
|
|
1493
|
+
const text = await reader.readAll();
|
|
1494
|
+
this.handleIncoming(text);
|
|
1495
|
+
} catch (err) {
|
|
1496
|
+
logger7.error("Error reading spotlight stream", err);
|
|
1497
|
+
}
|
|
1498
|
+
});
|
|
1499
|
+
this.isSubscribed = true;
|
|
1500
|
+
logger7.info("SpotlightService subscribed");
|
|
1501
|
+
}
|
|
1502
|
+
unsubscribe() {
|
|
1503
|
+
this.isSubscribed = false;
|
|
1504
|
+
logger7.info("SpotlightService unsubscribed");
|
|
1505
|
+
}
|
|
1506
|
+
getLocalParticipantId() {
|
|
1507
|
+
return this.room.localParticipant.identity;
|
|
1508
|
+
}
|
|
1509
|
+
async spotlight(targetId) {
|
|
1510
|
+
if (!this.isRoomReady()) {
|
|
1511
|
+
useRtcStore.getState().addError({
|
|
1512
|
+
code: "SPOTLIGHT_ROOM_NOT_READY",
|
|
1513
|
+
message: "Cannot spotlight: room not connected",
|
|
1514
|
+
timestamp: Date.now()
|
|
1515
|
+
});
|
|
1516
|
+
return;
|
|
1517
|
+
}
|
|
1518
|
+
const senderInfo = this.getSenderInfo();
|
|
1519
|
+
useSpotlightStore.getState().spotlight(targetId);
|
|
1520
|
+
try {
|
|
1521
|
+
const envelope = {
|
|
1522
|
+
v: 1,
|
|
1523
|
+
kind: "spotlight",
|
|
1524
|
+
roomId: this.room.name,
|
|
1525
|
+
ts: Date.now(),
|
|
1526
|
+
sender: senderInfo,
|
|
1527
|
+
payload: {
|
|
1528
|
+
action: "spotlight",
|
|
1529
|
+
targetId
|
|
1530
|
+
}
|
|
1531
|
+
};
|
|
1532
|
+
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
1533
|
+
topic: "spotlight:v1"
|
|
1534
|
+
});
|
|
1535
|
+
logger7.debug("User spotlighted", { targetId });
|
|
1536
|
+
} catch (error) {
|
|
1537
|
+
logger7.error("Failed to spotlight user", error);
|
|
1538
|
+
useRtcStore.getState().addError({
|
|
1539
|
+
code: "SPOTLIGHT_SEND_FAILED",
|
|
1540
|
+
message: error instanceof Error ? error.message : "Failed to spotlight user",
|
|
1541
|
+
timestamp: Date.now()
|
|
1542
|
+
});
|
|
1543
|
+
}
|
|
1544
|
+
}
|
|
1545
|
+
async unspotlight() {
|
|
1546
|
+
if (!this.isRoomReady()) {
|
|
1547
|
+
useRtcStore.getState().addError({
|
|
1548
|
+
code: "UNSPOTLIGHT_ROOM_NOT_READY",
|
|
1549
|
+
message: "Cannot unspotlight: room not connected",
|
|
1550
|
+
timestamp: Date.now()
|
|
1551
|
+
});
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
const senderInfo = this.getSenderInfo();
|
|
1555
|
+
const currentSpotlighted = useSpotlightStore.getState().getSpotlightedUser();
|
|
1556
|
+
if (!currentSpotlighted) {
|
|
1557
|
+
logger7.debug("No user is currently spotlighted");
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
useSpotlightStore.getState().unspotlight();
|
|
1561
|
+
try {
|
|
1562
|
+
const envelope = {
|
|
1563
|
+
v: 1,
|
|
1564
|
+
kind: "spotlight",
|
|
1565
|
+
roomId: this.room.name,
|
|
1566
|
+
ts: Date.now(),
|
|
1567
|
+
sender: senderInfo,
|
|
1568
|
+
payload: {
|
|
1569
|
+
action: "unspotlight",
|
|
1570
|
+
targetId: currentSpotlighted.participantId
|
|
1571
|
+
}
|
|
1572
|
+
};
|
|
1573
|
+
await this.room.localParticipant.sendText(JSON.stringify(envelope), {
|
|
1574
|
+
topic: "spotlight:v1"
|
|
1575
|
+
});
|
|
1576
|
+
logger7.debug("User unspotlighted");
|
|
1577
|
+
} catch (error) {
|
|
1578
|
+
logger7.error("Failed to unspotlight user", error);
|
|
1579
|
+
useRtcStore.getState().addError({
|
|
1580
|
+
code: "UNSPOTLIGHT_SEND_FAILED",
|
|
1581
|
+
message: error instanceof Error ? error.message : "Failed to unspotlight user",
|
|
1582
|
+
timestamp: Date.now()
|
|
1583
|
+
});
|
|
1584
|
+
}
|
|
1585
|
+
}
|
|
1586
|
+
handleIncoming(text) {
|
|
1587
|
+
try {
|
|
1588
|
+
const parsed = JSON.parse(text);
|
|
1589
|
+
if (!this.isValidEnvelope(parsed)) {
|
|
1590
|
+
logger7.warn("Invalid spotlight envelope received", parsed);
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
if (parsed.sender.id === this.getLocalParticipantId()) {
|
|
1594
|
+
logger7.debug("Ignoring self-echo spotlight message");
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
applyIncomingSpotlight(parsed);
|
|
1598
|
+
logger7.debug("Spotlight message received", {
|
|
1599
|
+
action: parsed.payload.action,
|
|
1600
|
+
targetId: parsed.payload.targetId
|
|
1601
|
+
});
|
|
1602
|
+
} catch (error) {
|
|
1603
|
+
logger7.error("Error parsing incoming spotlight message", error);
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
isValidEnvelope(e) {
|
|
1607
|
+
return e && e.v === 1 && e.kind === "spotlight" && typeof e.roomId === "string" && e.roomId === this.room.name && typeof e.ts === "number" && e.ts > 0 && typeof e.sender?.id === "string" && typeof e.payload?.action === "string" && ["spotlight", "unspotlight"].includes(e.payload.action) && typeof e.payload?.targetId === "string";
|
|
1608
|
+
}
|
|
1609
|
+
getSenderInfo() {
|
|
1610
|
+
const localParticipant = this.room.localParticipant;
|
|
1611
|
+
const sender = {
|
|
1612
|
+
id: localParticipant.identity
|
|
1613
|
+
};
|
|
1614
|
+
if (localParticipant.metadata) {
|
|
1615
|
+
try {
|
|
1616
|
+
sender.info = JSON.parse(
|
|
1617
|
+
localParticipant.metadata
|
|
1618
|
+
);
|
|
1619
|
+
} catch {
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
return sender;
|
|
1623
|
+
}
|
|
1624
|
+
};
|
|
1625
|
+
var logger8 = createLogger("spotlight:hook");
|
|
1626
|
+
function useSpotlight() {
|
|
1627
|
+
const service = useFeatureService("spotlight");
|
|
1628
|
+
const getSpotlightedUser = useSpotlightStore(
|
|
1629
|
+
(state) => state.getSpotlightedUser
|
|
1630
|
+
);
|
|
1631
|
+
const isSpotlighted = useSpotlightStore((state) => state.isSpotlighted);
|
|
1632
|
+
const spotlight = react.useCallback(
|
|
1633
|
+
async (targetId) => {
|
|
1634
|
+
if (!service) {
|
|
1635
|
+
logger8.error("Cannot spotlight: service not ready");
|
|
1636
|
+
return;
|
|
1637
|
+
}
|
|
1638
|
+
return service.spotlight(targetId);
|
|
1639
|
+
},
|
|
1640
|
+
[service]
|
|
1641
|
+
);
|
|
1642
|
+
const unspotlight = react.useCallback(async () => {
|
|
1643
|
+
if (!service) {
|
|
1644
|
+
logger8.error("Cannot unspotlight: service not ready");
|
|
1645
|
+
return;
|
|
1646
|
+
}
|
|
1647
|
+
return service.unspotlight();
|
|
1648
|
+
}, [service]);
|
|
1649
|
+
return {
|
|
1650
|
+
spotlight,
|
|
1651
|
+
unspotlight,
|
|
1652
|
+
getSpotlightedUser,
|
|
1653
|
+
isSpotlighted,
|
|
1654
|
+
isReady: !!service
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1429
1657
|
|
|
1430
1658
|
// src/channel/registry.ts
|
|
1431
1659
|
var FEATURES = {
|
|
@@ -1443,10 +1671,15 @@ var FEATURES = {
|
|
|
1443
1671
|
name: "raise-hand",
|
|
1444
1672
|
createService: (room) => new RaiseHandService(room),
|
|
1445
1673
|
cleanupStore: () => useRaiseHandStore.getState().clear()
|
|
1674
|
+
},
|
|
1675
|
+
spotlight: {
|
|
1676
|
+
name: "spotlight",
|
|
1677
|
+
createService: (room) => new SpotlightService(room),
|
|
1678
|
+
cleanupStore: () => useSpotlightStore.getState().clear()
|
|
1446
1679
|
}
|
|
1447
1680
|
};
|
|
1448
|
-
var DEFAULT_FEATURES = ["chat", "reactions", "raise-hand"];
|
|
1449
|
-
var
|
|
1681
|
+
var DEFAULT_FEATURES = ["chat", "reactions", "raise-hand", "spotlight"];
|
|
1682
|
+
var logger9 = createLogger("channels:provider");
|
|
1450
1683
|
function DataChannelProvider({
|
|
1451
1684
|
room,
|
|
1452
1685
|
features = DEFAULT_FEATURES,
|
|
@@ -1456,52 +1689,52 @@ function DataChannelProvider({
|
|
|
1456
1689
|
const [isReady, setIsReady] = react.useState(false);
|
|
1457
1690
|
react.useEffect(() => {
|
|
1458
1691
|
if (!room) {
|
|
1459
|
-
|
|
1692
|
+
logger9.warn("DataChannelProvider mounted without room");
|
|
1460
1693
|
return;
|
|
1461
1694
|
}
|
|
1462
|
-
|
|
1695
|
+
logger9.debug("Initializing features", { features });
|
|
1463
1696
|
for (const featureName of features) {
|
|
1464
1697
|
const feature = FEATURES[featureName];
|
|
1465
1698
|
if (!feature) {
|
|
1466
|
-
|
|
1699
|
+
logger9.warn(`Feature "${featureName}" not found in registry`);
|
|
1467
1700
|
continue;
|
|
1468
1701
|
}
|
|
1469
1702
|
try {
|
|
1470
|
-
|
|
1703
|
+
logger9.debug(`Initializing feature: ${featureName}`);
|
|
1471
1704
|
const service = feature.createService(room);
|
|
1472
1705
|
service.subscribe();
|
|
1473
1706
|
services.current.set(featureName, service);
|
|
1474
|
-
|
|
1707
|
+
logger9.info(`Feature "${featureName}" initialized`);
|
|
1475
1708
|
} catch (error) {
|
|
1476
|
-
|
|
1709
|
+
logger9.error(`Failed to initialize feature "${featureName}"`, error);
|
|
1477
1710
|
}
|
|
1478
1711
|
}
|
|
1479
1712
|
setIsReady(true);
|
|
1480
|
-
|
|
1713
|
+
logger9.info("All features initialized");
|
|
1481
1714
|
return () => {
|
|
1482
|
-
|
|
1715
|
+
logger9.debug("Cleaning up features");
|
|
1483
1716
|
services.current.forEach((service, name) => {
|
|
1484
1717
|
try {
|
|
1485
|
-
|
|
1718
|
+
logger9.debug(`Unsubscribing feature: ${name}`);
|
|
1486
1719
|
service.unsubscribe();
|
|
1487
1720
|
} catch (error) {
|
|
1488
|
-
|
|
1721
|
+
logger9.error(`Failed to unsubscribe feature "${name}"`, error);
|
|
1489
1722
|
}
|
|
1490
1723
|
});
|
|
1491
1724
|
for (const featureName of features) {
|
|
1492
1725
|
const feature = FEATURES[featureName];
|
|
1493
1726
|
if (feature?.cleanupStore) {
|
|
1494
1727
|
try {
|
|
1495
|
-
|
|
1728
|
+
logger9.debug(`Cleaning store for feature: ${featureName}`);
|
|
1496
1729
|
feature.cleanupStore();
|
|
1497
1730
|
} catch (error) {
|
|
1498
|
-
|
|
1731
|
+
logger9.error(`Failed to cleanup store for "${featureName}"`, error);
|
|
1499
1732
|
}
|
|
1500
1733
|
}
|
|
1501
1734
|
}
|
|
1502
1735
|
services.current.clear();
|
|
1503
1736
|
setIsReady(false);
|
|
1504
|
-
|
|
1737
|
+
logger9.info("Features cleanup complete");
|
|
1505
1738
|
};
|
|
1506
1739
|
}, [room, features]);
|
|
1507
1740
|
const contextValue = react.useMemo(
|
|
@@ -1518,6 +1751,7 @@ exports.ChatService = ChatService;
|
|
|
1518
1751
|
exports.DataChannelProvider = DataChannelProvider;
|
|
1519
1752
|
exports.RaiseHandService = RaiseHandService;
|
|
1520
1753
|
exports.ReactionsService = ReactionsService;
|
|
1754
|
+
exports.SpotlightService = SpotlightService;
|
|
1521
1755
|
exports.applyIncomingReaction = applyIncomingReaction;
|
|
1522
1756
|
exports.compareEntries = compareEntries;
|
|
1523
1757
|
exports.generateEntryId = generateEntryId;
|
|
@@ -1532,6 +1766,8 @@ exports.useRaiseHand = useRaiseHand;
|
|
|
1532
1766
|
exports.useRaiseHandStore = useRaiseHandStore;
|
|
1533
1767
|
exports.useReactions = useReactions;
|
|
1534
1768
|
exports.useReactionsStore = useReactionsStore;
|
|
1769
|
+
exports.useSpotlight = useSpotlight;
|
|
1770
|
+
exports.useSpotlightStore = useSpotlightStore;
|
|
1535
1771
|
exports.validateContent = validateContent;
|
|
1536
1772
|
exports.validateEmoji = validateEmoji;
|
|
1537
1773
|
//# sourceMappingURL=index.cjs.map
|