vg-x07df 1.10.6 → 1.11.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.
@@ -108,28 +108,6 @@ function createLogger(namespace, options) {
108
108
  }
109
109
  return new CallpadLoggerImpl(`callpad:${namespace}`, options);
110
110
  }
111
- var DataChannelContext = createContext(
112
- null
113
- );
114
- function useDataChannelContext() {
115
- const context = useContext(DataChannelContext);
116
- if (!context) {
117
- throw new Error(
118
- "useDataChannelContext must be used within DataChannelProvider"
119
- );
120
- }
121
- return context;
122
- }
123
- function useFeatureService(featureName) {
124
- const { services } = useDataChannelContext();
125
- const service = services.get(featureName);
126
- if (!service) {
127
- throw new Error(
128
- `Feature service "${featureName}" not found. Make sure it's enabled in DataChannelProvider.`
129
- );
130
- }
131
- return service;
132
- }
133
111
 
134
112
  // src/state/types.ts
135
113
  var defaultState = {
@@ -818,6 +796,30 @@ var ChatService = class {
818
796
  return sender;
819
797
  }
820
798
  };
799
+ var DataChannelContext = createContext(
800
+ null
801
+ );
802
+ function useDataChannelContext() {
803
+ const context = useContext(DataChannelContext);
804
+ if (!context) {
805
+ throw new Error(
806
+ "useDataChannelContext must be used within DataChannelProvider"
807
+ );
808
+ }
809
+ return context;
810
+ }
811
+ function useFeatureService(featureName) {
812
+ const { services } = useDataChannelContext();
813
+ const service = services.get(featureName);
814
+ if (!service) {
815
+ throw new Error(
816
+ `Feature service "${featureName}" not found. Make sure it's enabled in DataChannelProvider.`
817
+ );
818
+ }
819
+ return service;
820
+ }
821
+
822
+ // src/channel/chat/useChat.ts
821
823
  function useChat() {
822
824
  const service = useFeatureService("chat");
823
825
  const byId = useChatStore((state) => state.byId);
@@ -1169,13 +1171,255 @@ function useRaiseHand() {
1169
1171
  };
1170
1172
  }
1171
1173
  var defaultState3 = {
1174
+ spotlightedUser: null,
1175
+ isSpotlighted: false
1176
+ };
1177
+ var useSpotlightStore = create()(
1178
+ immer((set, get) => ({
1179
+ ...defaultState3,
1180
+ spotlight: (participantId, info) => set((state) => {
1181
+ state.spotlightedUser = {
1182
+ participantId,
1183
+ ts: Date.now(),
1184
+ ...info && { info }
1185
+ };
1186
+ state.isSpotlighted = true;
1187
+ }),
1188
+ unspotlight: () => set((state) => {
1189
+ state.spotlightedUser = null;
1190
+ state.isSpotlighted = false;
1191
+ }),
1192
+ getSpotlightedUser: () => get().spotlightedUser,
1193
+ clear: () => set(() => ({
1194
+ spotlightedUser: null,
1195
+ isSpotlighted: false
1196
+ }))
1197
+ }))
1198
+ );
1199
+ function applyIncomingSpotlight(envelope) {
1200
+ const { spotlight, unspotlight } = useSpotlightStore.getState();
1201
+ switch (envelope.payload.action) {
1202
+ case "spotlight": {
1203
+ spotlight(envelope.payload.targetId, envelope.payload.info);
1204
+ break;
1205
+ }
1206
+ case "unspotlight": {
1207
+ unspotlight();
1208
+ break;
1209
+ }
1210
+ }
1211
+ }
1212
+
1213
+ // src/channel/spotlight/service.ts
1214
+ var logger4 = createLogger("spotlight");
1215
+ var SpotlightService = class {
1216
+ constructor(room) {
1217
+ this.isSubscribed = false;
1218
+ this.room = room;
1219
+ }
1220
+ isRoomReady() {
1221
+ if (!this.room) {
1222
+ logger4.warn("Room not initialized");
1223
+ return false;
1224
+ }
1225
+ if (this.room.state !== ConnectionState.Connected) {
1226
+ logger4.warn("Room not connected", { state: this.room.state });
1227
+ return false;
1228
+ }
1229
+ if (!this.room.localParticipant) {
1230
+ logger4.warn("Local participant not available");
1231
+ return false;
1232
+ }
1233
+ return true;
1234
+ }
1235
+ subscribe() {
1236
+ if (this.isSubscribed) return;
1237
+ this.room.registerTextStreamHandler("spotlight:v1", async (reader) => {
1238
+ try {
1239
+ const text = await reader.readAll();
1240
+ this.handleIncoming(text);
1241
+ } catch (err) {
1242
+ logger4.error("Error reading spotlight stream", err);
1243
+ }
1244
+ });
1245
+ this.isSubscribed = true;
1246
+ logger4.info("SpotlightService subscribed");
1247
+ }
1248
+ unsubscribe() {
1249
+ this.isSubscribed = false;
1250
+ logger4.info("SpotlightService unsubscribed");
1251
+ }
1252
+ getLocalParticipantId() {
1253
+ return this.room.localParticipant.identity;
1254
+ }
1255
+ async spotlight(targetId, info) {
1256
+ if (!this.isRoomReady()) {
1257
+ useRtcStore.getState().addError({
1258
+ code: "SPOTLIGHT_ROOM_NOT_READY",
1259
+ message: "Cannot spotlight: room not connected",
1260
+ timestamp: Date.now()
1261
+ });
1262
+ return;
1263
+ }
1264
+ const senderInfo = this.getSenderInfo();
1265
+ const previousSpotlighted = useSpotlightStore.getState().getSpotlightedUser();
1266
+ useSpotlightStore.getState().spotlight(targetId, info);
1267
+ try {
1268
+ const envelope = {
1269
+ v: 1,
1270
+ kind: "spotlight",
1271
+ roomId: this.room.name,
1272
+ ts: Date.now(),
1273
+ sender: senderInfo,
1274
+ payload: {
1275
+ action: "spotlight",
1276
+ targetId,
1277
+ info
1278
+ }
1279
+ };
1280
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1281
+ topic: "spotlight:v1"
1282
+ });
1283
+ logger4.info("User spotlighted", { targetId });
1284
+ } catch (error) {
1285
+ logger4.error("Failed to spotlight user", error);
1286
+ if (previousSpotlighted) {
1287
+ useSpotlightStore.getState().spotlight(
1288
+ previousSpotlighted.participantId,
1289
+ previousSpotlighted.info
1290
+ );
1291
+ } else {
1292
+ useSpotlightStore.getState().unspotlight();
1293
+ }
1294
+ useRtcStore.getState().addError({
1295
+ code: "SPOTLIGHT_SEND_FAILED",
1296
+ message: error instanceof Error ? error.message : "Failed to spotlight user",
1297
+ timestamp: Date.now()
1298
+ });
1299
+ }
1300
+ }
1301
+ async unspotlight() {
1302
+ if (!this.isRoomReady()) {
1303
+ useRtcStore.getState().addError({
1304
+ code: "UNSPOTLIGHT_ROOM_NOT_READY",
1305
+ message: "Cannot unspotlight: room not connected",
1306
+ timestamp: Date.now()
1307
+ });
1308
+ return;
1309
+ }
1310
+ const senderInfo = this.getSenderInfo();
1311
+ const currentSpotlighted = useSpotlightStore.getState().getSpotlightedUser();
1312
+ if (!currentSpotlighted) {
1313
+ logger4.debug("No user is currently spotlighted");
1314
+ return;
1315
+ }
1316
+ const previousSpotlighted = { ...currentSpotlighted };
1317
+ useSpotlightStore.getState().unspotlight();
1318
+ try {
1319
+ const envelope = {
1320
+ v: 1,
1321
+ kind: "spotlight",
1322
+ roomId: this.room.name,
1323
+ ts: Date.now(),
1324
+ sender: senderInfo,
1325
+ payload: {
1326
+ action: "unspotlight",
1327
+ targetId: previousSpotlighted.participantId
1328
+ }
1329
+ };
1330
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1331
+ topic: "spotlight:v1"
1332
+ });
1333
+ logger4.info("User unspotlighted");
1334
+ } catch (error) {
1335
+ logger4.error("Failed to unspotlight user", error);
1336
+ useSpotlightStore.getState().spotlight(previousSpotlighted.participantId, previousSpotlighted.info);
1337
+ useRtcStore.getState().addError({
1338
+ code: "UNSPOTLIGHT_SEND_FAILED",
1339
+ message: error instanceof Error ? error.message : "Failed to unspotlight user",
1340
+ timestamp: Date.now()
1341
+ });
1342
+ }
1343
+ }
1344
+ handleIncoming(text) {
1345
+ try {
1346
+ const parsed = JSON.parse(text);
1347
+ if (!this.isValidEnvelope(parsed)) {
1348
+ logger4.warn("Invalid spotlight envelope received", parsed);
1349
+ return;
1350
+ }
1351
+ if (parsed.sender.id === this.getLocalParticipantId()) {
1352
+ logger4.debug("Ignoring self-echo spotlight message");
1353
+ return;
1354
+ }
1355
+ applyIncomingSpotlight(parsed);
1356
+ logger4.debug("Spotlight message received", {
1357
+ action: parsed.payload.action,
1358
+ targetId: parsed.payload.targetId
1359
+ });
1360
+ } catch (error) {
1361
+ logger4.error("Error parsing incoming spotlight message", error);
1362
+ }
1363
+ }
1364
+ isValidEnvelope(e) {
1365
+ 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";
1366
+ }
1367
+ getSenderInfo() {
1368
+ const localParticipant = this.room.localParticipant;
1369
+ const sender = {
1370
+ id: localParticipant.identity
1371
+ };
1372
+ if (localParticipant.metadata) {
1373
+ try {
1374
+ sender.info = JSON.parse(
1375
+ localParticipant.metadata
1376
+ );
1377
+ } catch {
1378
+ }
1379
+ }
1380
+ return sender;
1381
+ }
1382
+ };
1383
+ var logger5 = createLogger("spotlight:hook");
1384
+ function useSpotlight() {
1385
+ const service = useFeatureService("spotlight");
1386
+ const getSpotlightedUser = useSpotlightStore(
1387
+ (state) => state.getSpotlightedUser
1388
+ );
1389
+ const isSpotlighted = useSpotlightStore((state) => state.isSpotlighted);
1390
+ const spotlight = useCallback(
1391
+ async (targetId, info) => {
1392
+ if (!service) {
1393
+ logger5.error("Cannot spotlight: service not ready");
1394
+ return;
1395
+ }
1396
+ return service.spotlight(targetId, info);
1397
+ },
1398
+ [service]
1399
+ );
1400
+ const unspotlight = useCallback(async () => {
1401
+ if (!service) {
1402
+ logger5.error("Cannot unspotlight: service not ready");
1403
+ return;
1404
+ }
1405
+ return service.unspotlight();
1406
+ }, [service]);
1407
+ return {
1408
+ spotlight,
1409
+ unspotlight,
1410
+ getSpotlightedUser,
1411
+ isSpotlighted,
1412
+ isReady: !!service
1413
+ };
1414
+ }
1415
+ var defaultState4 = {
1172
1416
  reactions: /* @__PURE__ */ new Map(),
1173
1417
  participantCache: {},
1174
1418
  ttlMs: 4e3
1175
1419
  };
1176
1420
  var useReactionsStore = create()(
1177
1421
  immer((set) => ({
1178
- ...defaultState3,
1422
+ ...defaultState4,
1179
1423
  setReaction: (participantId, reaction) => set((state) => {
1180
1424
  state.reactions.set(participantId, reaction);
1181
1425
  }),
@@ -1196,7 +1440,7 @@ var useReactionsStore = create()(
1196
1440
  clear: () => set(() => ({
1197
1441
  reactions: /* @__PURE__ */ new Map(),
1198
1442
  participantCache: {},
1199
- ttlMs: defaultState3.ttlMs
1443
+ ttlMs: defaultState4.ttlMs
1200
1444
  }))
1201
1445
  }))
1202
1446
  );
@@ -1228,7 +1472,7 @@ function generateNonce() {
1228
1472
  }
1229
1473
 
1230
1474
  // src/channel/reactions/service.ts
1231
- var logger5 = createLogger("reactions");
1475
+ var logger7 = createLogger("reactions");
1232
1476
  var ReactionsService = class {
1233
1477
  constructor(room) {
1234
1478
  this.isSubscribed = false;
@@ -1239,15 +1483,15 @@ var ReactionsService = class {
1239
1483
  }
1240
1484
  isRoomReady() {
1241
1485
  if (!this.room) {
1242
- logger5.warn("Room not initialized");
1486
+ logger7.warn("Room not initialized");
1243
1487
  return false;
1244
1488
  }
1245
1489
  if (this.room.state !== ConnectionState.Connected) {
1246
- logger5.warn("Room not connected", { state: this.room.state });
1490
+ logger7.warn("Room not connected", { state: this.room.state });
1247
1491
  return false;
1248
1492
  }
1249
1493
  if (!this.room.localParticipant) {
1250
- logger5.warn("Local participant not available");
1494
+ logger7.warn("Local participant not available");
1251
1495
  return false;
1252
1496
  }
1253
1497
  return true;
@@ -1265,14 +1509,14 @@ var ReactionsService = class {
1265
1509
  const text = await reader.readAll();
1266
1510
  this.handleIncoming(text);
1267
1511
  } catch (err) {
1268
- logger5.error("Error reading reactions stream", err);
1512
+ logger7.error("Error reading reactions stream", err);
1269
1513
  }
1270
1514
  });
1271
1515
  this.pruneInterval = setInterval(() => {
1272
1516
  useReactionsStore.getState().pruneExpired();
1273
1517
  }, 1e3);
1274
1518
  this.isSubscribed = true;
1275
- logger5.info("ReactionsService subscribed");
1519
+ logger7.info("ReactionsService subscribed");
1276
1520
  }
1277
1521
  unsubscribe() {
1278
1522
  this.isSubscribed = false;
@@ -1280,7 +1524,7 @@ var ReactionsService = class {
1280
1524
  clearInterval(this.pruneInterval);
1281
1525
  this.pruneInterval = void 0;
1282
1526
  }
1283
- logger5.info("ReactionsService unsubscribed");
1527
+ logger7.info("ReactionsService unsubscribed");
1284
1528
  }
1285
1529
  getLocalParticipantId() {
1286
1530
  return this.room.localParticipant.identity;
@@ -1304,7 +1548,7 @@ var ReactionsService = class {
1304
1548
  return;
1305
1549
  }
1306
1550
  if (!this.canSendNow()) {
1307
- logger5.debug("Rate limited, skipping send");
1551
+ logger7.debug("Rate limited, skipping send");
1308
1552
  return;
1309
1553
  }
1310
1554
  const senderInfo = this.getSenderInfo();
@@ -1330,9 +1574,9 @@ var ReactionsService = class {
1330
1574
  await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1331
1575
  topic: "reactions:v1"
1332
1576
  });
1333
- logger5.debug("Reaction sent", { emoji });
1577
+ logger7.debug("Reaction sent", { emoji });
1334
1578
  } catch (error) {
1335
- logger5.error("Failed to send reaction", error);
1579
+ logger7.error("Failed to send reaction", error);
1336
1580
  useRtcStore.getState().addError({
1337
1581
  code: "REACTIONS_SEND_FAILED",
1338
1582
  message: error instanceof Error ? error.message : "Failed to send reaction",
@@ -1345,16 +1589,16 @@ var ReactionsService = class {
1345
1589
  try {
1346
1590
  const parsed = JSON.parse(text);
1347
1591
  if (!this.isValidEnvelope(parsed)) {
1348
- logger5.warn("Invalid reaction envelope received", parsed);
1592
+ logger7.warn("Invalid reaction envelope received", parsed);
1349
1593
  return;
1350
1594
  }
1351
1595
  if (parsed.sender.id === this.getLocalParticipantId()) {
1352
- logger5.debug("Ignoring self-echo reaction");
1596
+ logger7.debug("Ignoring self-echo reaction");
1353
1597
  return;
1354
1598
  }
1355
1599
  const lastTs = this.lastRemoteTs.get(parsed.sender.id) ?? Number.NEGATIVE_INFINITY;
1356
1600
  if (parsed.ts < lastTs) {
1357
- logger5.debug("Ignoring out-of-order reaction", {
1601
+ logger7.debug("Ignoring out-of-order reaction", {
1358
1602
  sender: parsed.sender.id,
1359
1603
  ts: parsed.ts,
1360
1604
  lastTs
@@ -1363,9 +1607,9 @@ var ReactionsService = class {
1363
1607
  }
1364
1608
  this.lastRemoteTs.set(parsed.sender.id, parsed.ts);
1365
1609
  applyIncomingReaction(parsed);
1366
- logger5.debug("Reaction received", { emoji: parsed.payload.emoji });
1610
+ logger7.debug("Reaction received", { emoji: parsed.payload.emoji });
1367
1611
  } catch (error) {
1368
- logger5.error("Error parsing incoming reaction", error);
1612
+ logger7.error("Error parsing incoming reaction", error);
1369
1613
  }
1370
1614
  }
1371
1615
  isValidEnvelope(e) {
@@ -1387,7 +1631,7 @@ var ReactionsService = class {
1387
1631
  return sender;
1388
1632
  }
1389
1633
  };
1390
- var logger6 = createLogger("reactions:hook");
1634
+ var logger8 = createLogger("reactions:hook");
1391
1635
  function useReactions() {
1392
1636
  const service = useFeatureService("reactions");
1393
1637
  const reactions = useReactionsStore((state) => state.reactions);
@@ -1406,7 +1650,7 @@ function useReactions() {
1406
1650
  const sendReaction = useCallback(
1407
1651
  async (emoji) => {
1408
1652
  if (!service) {
1409
- logger6.error("Cannot send reaction: service not ready");
1653
+ logger8.error("Cannot send reaction: service not ready");
1410
1654
  return;
1411
1655
  }
1412
1656
  return service.sendReaction(emoji);
@@ -1424,248 +1668,6 @@ function useReactions() {
1424
1668
  isReady: !!service
1425
1669
  };
1426
1670
  }
1427
- var defaultState4 = {
1428
- spotlightedUser: null,
1429
- isSpotlighted: false
1430
- };
1431
- var useSpotlightStore = create()(
1432
- immer((set, get) => ({
1433
- ...defaultState4,
1434
- spotlight: (participantId, info) => set((state) => {
1435
- state.spotlightedUser = {
1436
- participantId,
1437
- ts: Date.now(),
1438
- ...info && { info }
1439
- };
1440
- state.isSpotlighted = true;
1441
- }),
1442
- unspotlight: () => set((state) => {
1443
- state.spotlightedUser = null;
1444
- state.isSpotlighted = false;
1445
- }),
1446
- getSpotlightedUser: () => get().spotlightedUser,
1447
- clear: () => set(() => ({
1448
- spotlightedUser: null,
1449
- isSpotlighted: false
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, envelope.payload.info);
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 !== 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, info) {
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
- const previousSpotlighted = useSpotlightStore.getState().getSpotlightedUser();
1520
- useSpotlightStore.getState().spotlight(targetId, info);
1521
- try {
1522
- const envelope = {
1523
- v: 1,
1524
- kind: "spotlight",
1525
- roomId: this.room.name,
1526
- ts: Date.now(),
1527
- sender: senderInfo,
1528
- payload: {
1529
- action: "spotlight",
1530
- targetId,
1531
- info
1532
- }
1533
- };
1534
- await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1535
- topic: "spotlight:v1"
1536
- });
1537
- logger7.info("User spotlighted", { targetId });
1538
- } catch (error) {
1539
- logger7.error("Failed to spotlight user", error);
1540
- if (previousSpotlighted) {
1541
- useSpotlightStore.getState().spotlight(
1542
- previousSpotlighted.participantId,
1543
- previousSpotlighted.info
1544
- );
1545
- } else {
1546
- useSpotlightStore.getState().unspotlight();
1547
- }
1548
- useRtcStore.getState().addError({
1549
- code: "SPOTLIGHT_SEND_FAILED",
1550
- message: error instanceof Error ? error.message : "Failed to spotlight user",
1551
- timestamp: Date.now()
1552
- });
1553
- }
1554
- }
1555
- async unspotlight() {
1556
- if (!this.isRoomReady()) {
1557
- useRtcStore.getState().addError({
1558
- code: "UNSPOTLIGHT_ROOM_NOT_READY",
1559
- message: "Cannot unspotlight: room not connected",
1560
- timestamp: Date.now()
1561
- });
1562
- return;
1563
- }
1564
- const senderInfo = this.getSenderInfo();
1565
- const currentSpotlighted = useSpotlightStore.getState().getSpotlightedUser();
1566
- if (!currentSpotlighted) {
1567
- logger7.debug("No user is currently spotlighted");
1568
- return;
1569
- }
1570
- const previousSpotlighted = { ...currentSpotlighted };
1571
- useSpotlightStore.getState().unspotlight();
1572
- try {
1573
- const envelope = {
1574
- v: 1,
1575
- kind: "spotlight",
1576
- roomId: this.room.name,
1577
- ts: Date.now(),
1578
- sender: senderInfo,
1579
- payload: {
1580
- action: "unspotlight",
1581
- targetId: previousSpotlighted.participantId
1582
- }
1583
- };
1584
- await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1585
- topic: "spotlight:v1"
1586
- });
1587
- logger7.info("User unspotlighted");
1588
- } catch (error) {
1589
- logger7.error("Failed to unspotlight user", error);
1590
- useSpotlightStore.getState().spotlight(previousSpotlighted.participantId, previousSpotlighted.info);
1591
- useRtcStore.getState().addError({
1592
- code: "UNSPOTLIGHT_SEND_FAILED",
1593
- message: error instanceof Error ? error.message : "Failed to unspotlight user",
1594
- timestamp: Date.now()
1595
- });
1596
- }
1597
- }
1598
- handleIncoming(text) {
1599
- try {
1600
- const parsed = JSON.parse(text);
1601
- if (!this.isValidEnvelope(parsed)) {
1602
- logger7.warn("Invalid spotlight envelope received", parsed);
1603
- return;
1604
- }
1605
- if (parsed.sender.id === this.getLocalParticipantId()) {
1606
- logger7.debug("Ignoring self-echo spotlight message");
1607
- return;
1608
- }
1609
- applyIncomingSpotlight(parsed);
1610
- logger7.debug("Spotlight message received", {
1611
- action: parsed.payload.action,
1612
- targetId: parsed.payload.targetId
1613
- });
1614
- } catch (error) {
1615
- logger7.error("Error parsing incoming spotlight message", error);
1616
- }
1617
- }
1618
- isValidEnvelope(e) {
1619
- 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";
1620
- }
1621
- getSenderInfo() {
1622
- const localParticipant = this.room.localParticipant;
1623
- const sender = {
1624
- id: localParticipant.identity
1625
- };
1626
- if (localParticipant.metadata) {
1627
- try {
1628
- sender.info = JSON.parse(
1629
- localParticipant.metadata
1630
- );
1631
- } catch {
1632
- }
1633
- }
1634
- return sender;
1635
- }
1636
- };
1637
- var logger8 = createLogger("spotlight:hook");
1638
- function useSpotlight() {
1639
- const service = useFeatureService("spotlight");
1640
- const getSpotlightedUser = useSpotlightStore(
1641
- (state) => state.getSpotlightedUser
1642
- );
1643
- const isSpotlighted = useSpotlightStore((state) => state.isSpotlighted);
1644
- const spotlight = useCallback(
1645
- async (targetId, info) => {
1646
- if (!service) {
1647
- logger8.error("Cannot spotlight: service not ready");
1648
- return;
1649
- }
1650
- return service.spotlight(targetId, info);
1651
- },
1652
- [service]
1653
- );
1654
- const unspotlight = useCallback(async () => {
1655
- if (!service) {
1656
- logger8.error("Cannot unspotlight: service not ready");
1657
- return;
1658
- }
1659
- return service.unspotlight();
1660
- }, [service]);
1661
- return {
1662
- spotlight,
1663
- unspotlight,
1664
- getSpotlightedUser,
1665
- isSpotlighted,
1666
- isReady: !!service
1667
- };
1668
- }
1669
1671
 
1670
1672
  // src/channel/registry.ts
1671
1673
  var FEATURES = {