vg-x07df 1.8.1 → 1.8.2

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.
@@ -468,37 +468,29 @@ var ChatService = class {
468
468
  this.room.registerTextStreamHandler(
469
469
  "chat:v1",
470
470
  async (reader, participantInfo) => {
471
- console.log("Got here: chat:v1", participantInfo.identity);
472
471
  try {
473
472
  const text = await reader.readAll();
474
- console.log("Got here: chat:v1", text);
473
+ console.log("Got here: chat:v1", text, participantInfo.identity);
475
474
  this.handleIncomingMessage(text);
476
475
  } catch (error) {
477
476
  logger.error("Error reading text stream", error);
478
477
  }
479
478
  }
480
479
  );
481
- this.room.registerByteStreamHandler("chat:v1", async (reader, participantInfo) => {
482
- try {
483
- const info = reader.info;
484
- const filename = info.name;
485
- reader.onProgress = (progress) => {
486
- console.log(`${progress ? (progress * 100).toFixed(0) : "undefined"}% of ${filename} downloaded`);
487
- };
488
- const parts = (await reader.readAll()).map((chunk) => chunk.slice());
489
- const fileBlob = new Blob(parts, { type: info.mimeType });
490
- this.handleIncomingFile(fileBlob, filename, info.mimeType);
491
- console.log(
492
- `File "${info.name}" received from ${participantInfo.identity}
493
- Topic: ${info.topic}
494
- Timestamp: ${info.timestamp}
495
- ID: ${info.id}
496
- Size: ${info.size}`
497
- );
498
- } catch (error) {
499
- logger.error("Error reading file stream", error);
480
+ this.room.registerByteStreamHandler(
481
+ "chat:v1",
482
+ async (reader, participantInfo) => {
483
+ try {
484
+ const info = reader.info;
485
+ const filename = info.name;
486
+ const parts = (await reader.readAll()).map((chunk) => chunk.slice());
487
+ const fileBlob = new Blob(parts, { type: info.mimeType });
488
+ this.handleIncomingFile(fileBlob, filename, info.mimeType);
489
+ } catch (error) {
490
+ logger.error("Error reading file stream", error);
491
+ }
500
492
  }
501
- });
493
+ );
502
494
  this.isSubscribed = true;
503
495
  }
504
496
  unsubscribe() {
@@ -517,7 +509,6 @@ var ChatService = class {
517
509
  return;
518
510
  }
519
511
  if (content.length === 0 && typeof file !== "undefined") {
520
- console.log("file present", typeof file);
521
512
  content = file.name;
522
513
  }
523
514
  const validation = validateContent(content);
@@ -566,7 +557,6 @@ var ChatService = class {
566
557
  await this.room.localParticipant.sendText(JSON.stringify(envelope), {
567
558
  topic: "chat:v1"
568
559
  });
569
- console.log("Sent message", entry, envelope);
570
560
  if (file) {
571
561
  await this.room.localParticipant.sendFile(file, {
572
562
  mimeType: file.type,
@@ -881,13 +871,310 @@ function useChat() {
881
871
  };
882
872
  }
883
873
  var defaultState2 = {
874
+ raisedHands: /* @__PURE__ */ new Map(),
875
+ nextOrder: 1
876
+ };
877
+ var useRaiseHandStore = zustand.create()(
878
+ immer.immer((set, get) => ({
879
+ ...defaultState2,
880
+ raiseHand: (participantId) => set((state) => {
881
+ if (!state.raisedHands.has(participantId)) {
882
+ state.raisedHands.set(participantId, {
883
+ ts: Date.now(),
884
+ order: state.nextOrder++
885
+ });
886
+ }
887
+ }),
888
+ lowerHand: (participantId) => set((state) => {
889
+ state.raisedHands.delete(participantId);
890
+ }),
891
+ lowerAllHands: () => set((state) => {
892
+ state.raisedHands.clear();
893
+ state.nextOrder = 1;
894
+ }),
895
+ isHandRaised: (participantId) => get().raisedHands.has(participantId),
896
+ getRaisedHandOrder: (participantId) => {
897
+ const hand = get().raisedHands.get(participantId);
898
+ return hand?.order ?? null;
899
+ },
900
+ upsertParticipantInfo: (id, info) => {
901
+ },
902
+ clear: () => set(() => ({
903
+ raisedHands: /* @__PURE__ */ new Map(),
904
+ nextOrder: 1
905
+ }))
906
+ }))
907
+ );
908
+ function applyIncomingRaiseHand(envelope) {
909
+ const { raiseHand, lowerHand, lowerAllHands, upsertParticipantInfo } = useRaiseHandStore.getState();
910
+ if (envelope.sender.info) {
911
+ upsertParticipantInfo(envelope.sender.id, envelope.sender.info);
912
+ }
913
+ switch (envelope.payload.action) {
914
+ case "raise": {
915
+ raiseHand(envelope.sender.id);
916
+ break;
917
+ }
918
+ case "lower": {
919
+ const targetId = envelope.payload.targetId || envelope.sender.id;
920
+ lowerHand(targetId);
921
+ break;
922
+ }
923
+ case "lower-all": {
924
+ lowerAllHands();
925
+ break;
926
+ }
927
+ }
928
+ }
929
+
930
+ // src/channel/raiseHand/service.ts
931
+ var logger2 = createLogger("raise-hand");
932
+ var RaiseHandService = class {
933
+ constructor(room) {
934
+ this.isSubscribed = false;
935
+ this.room = room;
936
+ }
937
+ isRoomReady() {
938
+ if (!this.room) {
939
+ logger2.warn("Room not initialized");
940
+ return false;
941
+ }
942
+ if (this.room.state !== livekitClient.ConnectionState.Connected) {
943
+ logger2.warn("Room not connected", { state: this.room.state });
944
+ return false;
945
+ }
946
+ if (!this.room.localParticipant) {
947
+ logger2.warn("Local participant not available");
948
+ return false;
949
+ }
950
+ return true;
951
+ }
952
+ subscribe() {
953
+ if (this.isSubscribed) return;
954
+ this.room.registerTextStreamHandler("raise-hand:v1", async (reader) => {
955
+ try {
956
+ const text = await reader.readAll();
957
+ this.handleIncoming(text);
958
+ } catch (err) {
959
+ logger2.error("Error reading raise-hand stream", err);
960
+ }
961
+ });
962
+ this.isSubscribed = true;
963
+ logger2.info("RaiseHandService subscribed");
964
+ }
965
+ unsubscribe() {
966
+ this.isSubscribed = false;
967
+ logger2.info("RaiseHandService unsubscribed");
968
+ }
969
+ getLocalParticipantId() {
970
+ return this.room.localParticipant.identity;
971
+ }
972
+ async raiseHand() {
973
+ if (!this.isRoomReady()) {
974
+ useRtcStore.getState().addError({
975
+ code: "RAISE_HAND_ROOM_NOT_READY",
976
+ message: "Cannot raise hand: room not connected",
977
+ timestamp: Date.now()
978
+ });
979
+ return;
980
+ }
981
+ const senderInfo = this.getSenderInfo();
982
+ useRaiseHandStore.getState().raiseHand(senderInfo.id);
983
+ try {
984
+ const envelope = {
985
+ v: 1,
986
+ kind: "raise-hand",
987
+ roomId: this.room.name,
988
+ ts: Date.now(),
989
+ sender: senderInfo,
990
+ payload: {
991
+ action: "raise"
992
+ }
993
+ };
994
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
995
+ topic: "raise-hand:v1"
996
+ });
997
+ logger2.debug("Hand raised");
998
+ } catch (error) {
999
+ logger2.error("Failed to raise hand", error);
1000
+ useRtcStore.getState().addError({
1001
+ code: "RAISE_HAND_SEND_FAILED",
1002
+ message: error instanceof Error ? error.message : "Failed to raise hand",
1003
+ timestamp: Date.now()
1004
+ });
1005
+ }
1006
+ }
1007
+ async lowerHand(targetId) {
1008
+ if (!this.isRoomReady()) {
1009
+ useRtcStore.getState().addError({
1010
+ code: "LOWER_HAND_ROOM_NOT_READY",
1011
+ message: "Cannot lower hand: room not connected",
1012
+ timestamp: Date.now()
1013
+ });
1014
+ return;
1015
+ }
1016
+ const senderInfo = this.getSenderInfo();
1017
+ const participantId = targetId || senderInfo.id;
1018
+ useRaiseHandStore.getState().lowerHand(participantId);
1019
+ try {
1020
+ const envelope = {
1021
+ v: 1,
1022
+ kind: "raise-hand",
1023
+ roomId: this.room.name,
1024
+ ts: Date.now(),
1025
+ sender: senderInfo,
1026
+ payload: {
1027
+ action: "lower",
1028
+ targetId: participantId
1029
+ }
1030
+ };
1031
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1032
+ topic: "raise-hand:v1"
1033
+ });
1034
+ logger2.debug("Hand lowered", { targetId: participantId });
1035
+ } catch (error) {
1036
+ logger2.error("Failed to lower hand", error);
1037
+ useRtcStore.getState().addError({
1038
+ code: "LOWER_HAND_SEND_FAILED",
1039
+ message: error instanceof Error ? error.message : "Failed to lower hand",
1040
+ timestamp: Date.now()
1041
+ });
1042
+ }
1043
+ }
1044
+ async lowerAllHands() {
1045
+ if (!this.isRoomReady()) {
1046
+ useRtcStore.getState().addError({
1047
+ code: "LOWER_ALL_HANDS_ROOM_NOT_READY",
1048
+ message: "Cannot lower all hands: room not connected",
1049
+ timestamp: Date.now()
1050
+ });
1051
+ return;
1052
+ }
1053
+ const senderInfo = this.getSenderInfo();
1054
+ useRaiseHandStore.getState().lowerAllHands();
1055
+ try {
1056
+ const envelope = {
1057
+ v: 1,
1058
+ kind: "raise-hand",
1059
+ roomId: this.room.name,
1060
+ ts: Date.now(),
1061
+ sender: senderInfo,
1062
+ payload: {
1063
+ action: "lower-all"
1064
+ }
1065
+ };
1066
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1067
+ topic: "raise-hand:v1"
1068
+ });
1069
+ logger2.debug("All hands lowered");
1070
+ } catch (error) {
1071
+ logger2.error("Failed to lower all hands", error);
1072
+ useRtcStore.getState().addError({
1073
+ code: "LOWER_ALL_HANDS_SEND_FAILED",
1074
+ message: error instanceof Error ? error.message : "Failed to lower all hands",
1075
+ timestamp: Date.now()
1076
+ });
1077
+ }
1078
+ }
1079
+ handleIncoming(text) {
1080
+ try {
1081
+ const parsed = JSON.parse(text);
1082
+ if (!this.isValidEnvelope(parsed)) {
1083
+ logger2.warn("Invalid raise-hand envelope received", parsed);
1084
+ return;
1085
+ }
1086
+ if (parsed.sender.id === this.getLocalParticipantId()) {
1087
+ logger2.debug("Ignoring self-echo raise-hand message");
1088
+ return;
1089
+ }
1090
+ applyIncomingRaiseHand(parsed);
1091
+ logger2.debug("Raise-hand message received", {
1092
+ action: parsed.payload.action
1093
+ });
1094
+ } catch (error) {
1095
+ logger2.error("Error parsing incoming raise-hand message", error);
1096
+ }
1097
+ }
1098
+ isValidEnvelope(e) {
1099
+ return e && e.v === 1 && e.kind === "raise-hand" && 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" && ["raise", "lower", "lower-all"].includes(e.payload.action);
1100
+ }
1101
+ getSenderInfo() {
1102
+ const localParticipant = this.room.localParticipant;
1103
+ const sender = {
1104
+ id: localParticipant.identity
1105
+ };
1106
+ if (localParticipant.metadata) {
1107
+ try {
1108
+ sender.info = JSON.parse(
1109
+ localParticipant.metadata
1110
+ );
1111
+ } catch {
1112
+ }
1113
+ }
1114
+ return sender;
1115
+ }
1116
+ };
1117
+ var logger3 = createLogger("raise-hand:hook");
1118
+ function useRaiseHand() {
1119
+ const service = useFeatureService("raise-hand");
1120
+ const raisedHands = useRaiseHandStore((state) => state.raisedHands);
1121
+ const isHandRaised = useRaiseHandStore((state) => state.isHandRaised);
1122
+ const getRaisedHandOrder = useRaiseHandStore(
1123
+ (state) => state.getRaisedHandOrder
1124
+ );
1125
+ const raiseHand = react.useCallback(async () => {
1126
+ if (!service) {
1127
+ logger3.error("Cannot raise hand: service not ready");
1128
+ return;
1129
+ }
1130
+ return service.raiseHand();
1131
+ }, [service]);
1132
+ const lowerHand = react.useCallback(
1133
+ async (participantId) => {
1134
+ if (!service) {
1135
+ logger3.error("Cannot lower hand: service not ready");
1136
+ return;
1137
+ }
1138
+ return service.lowerHand(participantId);
1139
+ },
1140
+ [service]
1141
+ );
1142
+ const lowerAllHands = react.useCallback(async () => {
1143
+ if (!service) {
1144
+ logger3.error("Cannot lower all hands: service not ready");
1145
+ return;
1146
+ }
1147
+ return service.lowerAllHands();
1148
+ }, [service]);
1149
+ const getRaisedHands = react.useCallback(() => {
1150
+ const hands = [];
1151
+ for (const [participantId, hand] of raisedHands.entries()) {
1152
+ hands.push({
1153
+ participantId,
1154
+ order: hand.order,
1155
+ ts: hand.ts
1156
+ });
1157
+ }
1158
+ return hands.sort((a, b) => a.order - b.order);
1159
+ }, [raisedHands]);
1160
+ return {
1161
+ raiseHand,
1162
+ lowerHand,
1163
+ lowerAllHands,
1164
+ isHandRaised,
1165
+ getRaisedHandOrder,
1166
+ getRaisedHands,
1167
+ isReady: !!service
1168
+ };
1169
+ }
1170
+ var defaultState3 = {
884
1171
  reactions: /* @__PURE__ */ new Map(),
885
1172
  participantCache: {},
886
1173
  ttlMs: 4e3
887
1174
  };
888
1175
  var useReactionsStore = zustand.create()(
889
1176
  immer.immer((set) => ({
890
- ...defaultState2,
1177
+ ...defaultState3,
891
1178
  setReaction: (participantId, reaction) => set((state) => {
892
1179
  state.reactions.set(participantId, reaction);
893
1180
  }),
@@ -908,7 +1195,7 @@ var useReactionsStore = zustand.create()(
908
1195
  clear: () => set(() => ({
909
1196
  reactions: /* @__PURE__ */ new Map(),
910
1197
  participantCache: {},
911
- ttlMs: defaultState2.ttlMs
1198
+ ttlMs: defaultState3.ttlMs
912
1199
  }))
913
1200
  }))
914
1201
  );
@@ -940,7 +1227,7 @@ function generateNonce() {
940
1227
  }
941
1228
 
942
1229
  // src/channel/reactions/service.ts
943
- var logger3 = createLogger("reactions");
1230
+ var logger5 = createLogger("reactions");
944
1231
  var ReactionsService = class {
945
1232
  constructor(room) {
946
1233
  this.isSubscribed = false;
@@ -951,15 +1238,15 @@ var ReactionsService = class {
951
1238
  }
952
1239
  isRoomReady() {
953
1240
  if (!this.room) {
954
- logger3.warn("Room not initialized");
1241
+ logger5.warn("Room not initialized");
955
1242
  return false;
956
1243
  }
957
1244
  if (this.room.state !== livekitClient.ConnectionState.Connected) {
958
- logger3.warn("Room not connected", { state: this.room.state });
1245
+ logger5.warn("Room not connected", { state: this.room.state });
959
1246
  return false;
960
1247
  }
961
1248
  if (!this.room.localParticipant) {
962
- logger3.warn("Local participant not available");
1249
+ logger5.warn("Local participant not available");
963
1250
  return false;
964
1251
  }
965
1252
  return true;
@@ -977,14 +1264,14 @@ var ReactionsService = class {
977
1264
  const text = await reader.readAll();
978
1265
  this.handleIncoming(text);
979
1266
  } catch (err) {
980
- logger3.error("Error reading reactions stream", err);
1267
+ logger5.error("Error reading reactions stream", err);
981
1268
  }
982
1269
  });
983
1270
  this.pruneInterval = setInterval(() => {
984
1271
  useReactionsStore.getState().pruneExpired();
985
1272
  }, 1e3);
986
1273
  this.isSubscribed = true;
987
- logger3.info("ReactionsService subscribed");
1274
+ logger5.info("ReactionsService subscribed");
988
1275
  }
989
1276
  unsubscribe() {
990
1277
  this.isSubscribed = false;
@@ -992,7 +1279,7 @@ var ReactionsService = class {
992
1279
  clearInterval(this.pruneInterval);
993
1280
  this.pruneInterval = void 0;
994
1281
  }
995
- logger3.info("ReactionsService unsubscribed");
1282
+ logger5.info("ReactionsService unsubscribed");
996
1283
  }
997
1284
  getLocalParticipantId() {
998
1285
  return this.room.localParticipant.identity;
@@ -1016,7 +1303,7 @@ var ReactionsService = class {
1016
1303
  return;
1017
1304
  }
1018
1305
  if (!this.canSendNow()) {
1019
- logger3.debug("Rate limited, skipping send");
1306
+ logger5.debug("Rate limited, skipping send");
1020
1307
  return;
1021
1308
  }
1022
1309
  const senderInfo = this.getSenderInfo();
@@ -1042,9 +1329,9 @@ var ReactionsService = class {
1042
1329
  await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1043
1330
  topic: "reactions:v1"
1044
1331
  });
1045
- logger3.debug("Reaction sent", { emoji });
1332
+ logger5.debug("Reaction sent", { emoji });
1046
1333
  } catch (error) {
1047
- logger3.error("Failed to send reaction", error);
1334
+ logger5.error("Failed to send reaction", error);
1048
1335
  useRtcStore.getState().addError({
1049
1336
  code: "REACTIONS_SEND_FAILED",
1050
1337
  message: error instanceof Error ? error.message : "Failed to send reaction",
@@ -1057,16 +1344,16 @@ var ReactionsService = class {
1057
1344
  try {
1058
1345
  const parsed = JSON.parse(text);
1059
1346
  if (!this.isValidEnvelope(parsed)) {
1060
- logger3.warn("Invalid reaction envelope received", parsed);
1347
+ logger5.warn("Invalid reaction envelope received", parsed);
1061
1348
  return;
1062
1349
  }
1063
1350
  if (parsed.sender.id === this.getLocalParticipantId()) {
1064
- logger3.debug("Ignoring self-echo reaction");
1351
+ logger5.debug("Ignoring self-echo reaction");
1065
1352
  return;
1066
1353
  }
1067
1354
  const lastTs = this.lastRemoteTs.get(parsed.sender.id) ?? Number.NEGATIVE_INFINITY;
1068
1355
  if (parsed.ts < lastTs) {
1069
- logger3.debug("Ignoring out-of-order reaction", {
1356
+ logger5.debug("Ignoring out-of-order reaction", {
1070
1357
  sender: parsed.sender.id,
1071
1358
  ts: parsed.ts,
1072
1359
  lastTs
@@ -1075,9 +1362,9 @@ var ReactionsService = class {
1075
1362
  }
1076
1363
  this.lastRemoteTs.set(parsed.sender.id, parsed.ts);
1077
1364
  applyIncomingReaction(parsed);
1078
- logger3.debug("Reaction received", { emoji: parsed.payload.emoji });
1365
+ logger5.debug("Reaction received", { emoji: parsed.payload.emoji });
1079
1366
  } catch (error) {
1080
- logger3.error("Error parsing incoming reaction", error);
1367
+ logger5.error("Error parsing incoming reaction", error);
1081
1368
  }
1082
1369
  }
1083
1370
  isValidEnvelope(e) {
@@ -1099,7 +1386,7 @@ var ReactionsService = class {
1099
1386
  return sender;
1100
1387
  }
1101
1388
  };
1102
- var logger4 = createLogger("reactions:hook");
1389
+ var logger6 = createLogger("reactions:hook");
1103
1390
  function useReactions() {
1104
1391
  const service = useFeatureService("reactions");
1105
1392
  const reactions = useReactionsStore((state) => state.reactions);
@@ -1118,7 +1405,7 @@ function useReactions() {
1118
1405
  const sendReaction = react.useCallback(
1119
1406
  async (emoji) => {
1120
1407
  if (!service) {
1121
- logger4.error("Cannot send reaction: service not ready");
1408
+ logger6.error("Cannot send reaction: service not ready");
1122
1409
  return;
1123
1410
  }
1124
1411
  return service.sendReaction(emoji);
@@ -1148,10 +1435,15 @@ var FEATURES = {
1148
1435
  name: "reactions",
1149
1436
  createService: (room) => new ReactionsService(room),
1150
1437
  cleanupStore: () => useReactionsStore.getState().clear()
1438
+ },
1439
+ "raise-hand": {
1440
+ name: "raise-hand",
1441
+ createService: (room) => new RaiseHandService(room),
1442
+ cleanupStore: () => useRaiseHandStore.getState().clear()
1151
1443
  }
1152
1444
  };
1153
- var DEFAULT_FEATURES = ["chat", "reactions"];
1154
- var logger5 = createLogger("channels:provider");
1445
+ var DEFAULT_FEATURES = ["chat", "reactions", "raise-hand"];
1446
+ var logger7 = createLogger("channels:provider");
1155
1447
  function DataChannelProvider({
1156
1448
  room,
1157
1449
  features = DEFAULT_FEATURES,
@@ -1161,52 +1453,52 @@ function DataChannelProvider({
1161
1453
  const [isReady, setIsReady] = react.useState(false);
1162
1454
  react.useEffect(() => {
1163
1455
  if (!room) {
1164
- logger5.warn("DataChannelProvider mounted without room");
1456
+ logger7.warn("DataChannelProvider mounted without room");
1165
1457
  return;
1166
1458
  }
1167
- logger5.debug("Initializing features", { features });
1459
+ logger7.debug("Initializing features", { features });
1168
1460
  for (const featureName of features) {
1169
1461
  const feature = FEATURES[featureName];
1170
1462
  if (!feature) {
1171
- logger5.warn(`Feature "${featureName}" not found in registry`);
1463
+ logger7.warn(`Feature "${featureName}" not found in registry`);
1172
1464
  continue;
1173
1465
  }
1174
1466
  try {
1175
- logger5.debug(`Initializing feature: ${featureName}`);
1467
+ logger7.debug(`Initializing feature: ${featureName}`);
1176
1468
  const service = feature.createService(room);
1177
1469
  service.subscribe();
1178
1470
  services.current.set(featureName, service);
1179
- logger5.info(`Feature "${featureName}" initialized`);
1471
+ logger7.info(`Feature "${featureName}" initialized`);
1180
1472
  } catch (error) {
1181
- logger5.error(`Failed to initialize feature "${featureName}"`, error);
1473
+ logger7.error(`Failed to initialize feature "${featureName}"`, error);
1182
1474
  }
1183
1475
  }
1184
1476
  setIsReady(true);
1185
- logger5.info("All features initialized");
1477
+ logger7.info("All features initialized");
1186
1478
  return () => {
1187
- logger5.debug("Cleaning up features");
1479
+ logger7.debug("Cleaning up features");
1188
1480
  services.current.forEach((service, name) => {
1189
1481
  try {
1190
- logger5.debug(`Unsubscribing feature: ${name}`);
1482
+ logger7.debug(`Unsubscribing feature: ${name}`);
1191
1483
  service.unsubscribe();
1192
1484
  } catch (error) {
1193
- logger5.error(`Failed to unsubscribe feature "${name}"`, error);
1485
+ logger7.error(`Failed to unsubscribe feature "${name}"`, error);
1194
1486
  }
1195
1487
  });
1196
1488
  for (const featureName of features) {
1197
1489
  const feature = FEATURES[featureName];
1198
1490
  if (feature?.cleanupStore) {
1199
1491
  try {
1200
- logger5.debug(`Cleaning store for feature: ${featureName}`);
1492
+ logger7.debug(`Cleaning store for feature: ${featureName}`);
1201
1493
  feature.cleanupStore();
1202
1494
  } catch (error) {
1203
- logger5.error(`Failed to cleanup store for "${featureName}"`, error);
1495
+ logger7.error(`Failed to cleanup store for "${featureName}"`, error);
1204
1496
  }
1205
1497
  }
1206
1498
  }
1207
1499
  services.current.clear();
1208
1500
  setIsReady(false);
1209
- logger5.info("Features cleanup complete");
1501
+ logger7.info("Features cleanup complete");
1210
1502
  };
1211
1503
  }, [room, features]);
1212
1504
  const contextValue = react.useMemo(
@@ -1221,6 +1513,7 @@ function DataChannelProvider({
1221
1513
 
1222
1514
  exports.ChatService = ChatService;
1223
1515
  exports.DataChannelProvider = DataChannelProvider;
1516
+ exports.RaiseHandService = RaiseHandService;
1224
1517
  exports.ReactionsService = ReactionsService;
1225
1518
  exports.applyIncomingReaction = applyIncomingReaction;
1226
1519
  exports.compareEntries = compareEntries;
@@ -1232,6 +1525,8 @@ exports.useChat = useChat;
1232
1525
  exports.useChatStore = useChatStore;
1233
1526
  exports.useDataChannelContext = useDataChannelContext;
1234
1527
  exports.useFeatureService = useFeatureService;
1528
+ exports.useRaiseHand = useRaiseHand;
1529
+ exports.useRaiseHandStore = useRaiseHandStore;
1235
1530
  exports.useReactions = useReactions;
1236
1531
  exports.useReactionsStore = useReactionsStore;
1237
1532
  exports.validateContent = validateContent;