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