vg-x07df 1.8.3 → 1.9.1

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.
@@ -508,10 +508,8 @@ var ChatService = class {
508
508
  });
509
509
  return;
510
510
  }
511
- if (content.length === 0 && typeof file !== "undefined") {
512
- content = file.name;
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 logger7 = createLogger("channels:provider");
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
- logger7.warn("DataChannelProvider mounted without room");
1692
+ logger9.warn("DataChannelProvider mounted without room");
1460
1693
  return;
1461
1694
  }
1462
- logger7.debug("Initializing features", { features });
1695
+ logger9.debug("Initializing features", { features });
1463
1696
  for (const featureName of features) {
1464
1697
  const feature = FEATURES[featureName];
1465
1698
  if (!feature) {
1466
- logger7.warn(`Feature "${featureName}" not found in registry`);
1699
+ logger9.warn(`Feature "${featureName}" not found in registry`);
1467
1700
  continue;
1468
1701
  }
1469
1702
  try {
1470
- logger7.debug(`Initializing feature: ${featureName}`);
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
- logger7.info(`Feature "${featureName}" initialized`);
1707
+ logger9.info(`Feature "${featureName}" initialized`);
1475
1708
  } catch (error) {
1476
- logger7.error(`Failed to initialize feature "${featureName}"`, error);
1709
+ logger9.error(`Failed to initialize feature "${featureName}"`, error);
1477
1710
  }
1478
1711
  }
1479
1712
  setIsReady(true);
1480
- logger7.info("All features initialized");
1713
+ logger9.info("All features initialized");
1481
1714
  return () => {
1482
- logger7.debug("Cleaning up features");
1715
+ logger9.debug("Cleaning up features");
1483
1716
  services.current.forEach((service, name) => {
1484
1717
  try {
1485
- logger7.debug(`Unsubscribing feature: ${name}`);
1718
+ logger9.debug(`Unsubscribing feature: ${name}`);
1486
1719
  service.unsubscribe();
1487
1720
  } catch (error) {
1488
- logger7.error(`Failed to unsubscribe feature "${name}"`, error);
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
- logger7.debug(`Cleaning store for feature: ${featureName}`);
1728
+ logger9.debug(`Cleaning store for feature: ${featureName}`);
1496
1729
  feature.cleanupStore();
1497
1730
  } catch (error) {
1498
- logger7.error(`Failed to cleanup store for "${featureName}"`, error);
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
- logger7.info("Features cleanup complete");
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