vg-x07df 1.8.1 → 1.8.3

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,313 @@ 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
+ if (state.raisedHands.size === 0) {
891
+ state.nextOrder = 1;
892
+ }
893
+ }),
894
+ lowerAllHands: () => set((state) => {
895
+ state.raisedHands.clear();
896
+ state.nextOrder = 1;
897
+ }),
898
+ isHandRaised: (participantId) => get().raisedHands.has(participantId),
899
+ getRaisedHandOrder: (participantId) => {
900
+ const hand = get().raisedHands.get(participantId);
901
+ return hand?.order ?? null;
902
+ },
903
+ upsertParticipantInfo: (id, info) => {
904
+ },
905
+ clear: () => set(() => ({
906
+ raisedHands: /* @__PURE__ */ new Map(),
907
+ nextOrder: 1
908
+ }))
909
+ }))
910
+ );
911
+ function applyIncomingRaiseHand(envelope) {
912
+ const { raiseHand, lowerHand, lowerAllHands, upsertParticipantInfo } = useRaiseHandStore.getState();
913
+ if (envelope.sender.info) {
914
+ upsertParticipantInfo(envelope.sender.id, envelope.sender.info);
915
+ }
916
+ switch (envelope.payload.action) {
917
+ case "raise": {
918
+ raiseHand(envelope.sender.id);
919
+ break;
920
+ }
921
+ case "lower": {
922
+ const targetId = envelope.payload.targetId || envelope.sender.id;
923
+ lowerHand(targetId);
924
+ break;
925
+ }
926
+ case "lower-all": {
927
+ lowerAllHands();
928
+ break;
929
+ }
930
+ }
931
+ }
932
+
933
+ // src/channel/raiseHand/service.ts
934
+ var logger2 = createLogger("raise-hand");
935
+ var RaiseHandService = class {
936
+ constructor(room) {
937
+ this.isSubscribed = false;
938
+ this.room = room;
939
+ }
940
+ isRoomReady() {
941
+ if (!this.room) {
942
+ logger2.warn("Room not initialized");
943
+ return false;
944
+ }
945
+ if (this.room.state !== livekitClient.ConnectionState.Connected) {
946
+ logger2.warn("Room not connected", { state: this.room.state });
947
+ return false;
948
+ }
949
+ if (!this.room.localParticipant) {
950
+ logger2.warn("Local participant not available");
951
+ return false;
952
+ }
953
+ return true;
954
+ }
955
+ subscribe() {
956
+ if (this.isSubscribed) return;
957
+ this.room.registerTextStreamHandler("raise-hand:v1", async (reader) => {
958
+ try {
959
+ const text = await reader.readAll();
960
+ this.handleIncoming(text);
961
+ } catch (err) {
962
+ logger2.error("Error reading raise-hand stream", err);
963
+ }
964
+ });
965
+ this.isSubscribed = true;
966
+ logger2.info("RaiseHandService subscribed");
967
+ }
968
+ unsubscribe() {
969
+ this.isSubscribed = false;
970
+ logger2.info("RaiseHandService unsubscribed");
971
+ }
972
+ getLocalParticipantId() {
973
+ return this.room.localParticipant.identity;
974
+ }
975
+ async raiseHand() {
976
+ if (!this.isRoomReady()) {
977
+ useRtcStore.getState().addError({
978
+ code: "RAISE_HAND_ROOM_NOT_READY",
979
+ message: "Cannot raise hand: room not connected",
980
+ timestamp: Date.now()
981
+ });
982
+ return;
983
+ }
984
+ const senderInfo = this.getSenderInfo();
985
+ useRaiseHandStore.getState().raiseHand(senderInfo.id);
986
+ try {
987
+ const envelope = {
988
+ v: 1,
989
+ kind: "raise-hand",
990
+ roomId: this.room.name,
991
+ ts: Date.now(),
992
+ sender: senderInfo,
993
+ payload: {
994
+ action: "raise"
995
+ }
996
+ };
997
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
998
+ topic: "raise-hand:v1"
999
+ });
1000
+ logger2.debug("Hand raised");
1001
+ } catch (error) {
1002
+ logger2.error("Failed to raise hand", error);
1003
+ useRtcStore.getState().addError({
1004
+ code: "RAISE_HAND_SEND_FAILED",
1005
+ message: error instanceof Error ? error.message : "Failed to raise hand",
1006
+ timestamp: Date.now()
1007
+ });
1008
+ }
1009
+ }
1010
+ async lowerHand(targetId) {
1011
+ if (!this.isRoomReady()) {
1012
+ useRtcStore.getState().addError({
1013
+ code: "LOWER_HAND_ROOM_NOT_READY",
1014
+ message: "Cannot lower hand: room not connected",
1015
+ timestamp: Date.now()
1016
+ });
1017
+ return;
1018
+ }
1019
+ const senderInfo = this.getSenderInfo();
1020
+ const participantId = targetId || senderInfo.id;
1021
+ useRaiseHandStore.getState().lowerHand(participantId);
1022
+ try {
1023
+ const envelope = {
1024
+ v: 1,
1025
+ kind: "raise-hand",
1026
+ roomId: this.room.name,
1027
+ ts: Date.now(),
1028
+ sender: senderInfo,
1029
+ payload: {
1030
+ action: "lower",
1031
+ targetId: participantId
1032
+ }
1033
+ };
1034
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1035
+ topic: "raise-hand:v1"
1036
+ });
1037
+ logger2.debug("Hand lowered", { targetId: participantId });
1038
+ } catch (error) {
1039
+ logger2.error("Failed to lower hand", error);
1040
+ useRtcStore.getState().addError({
1041
+ code: "LOWER_HAND_SEND_FAILED",
1042
+ message: error instanceof Error ? error.message : "Failed to lower hand",
1043
+ timestamp: Date.now()
1044
+ });
1045
+ }
1046
+ }
1047
+ async lowerAllHands() {
1048
+ if (!this.isRoomReady()) {
1049
+ useRtcStore.getState().addError({
1050
+ code: "LOWER_ALL_HANDS_ROOM_NOT_READY",
1051
+ message: "Cannot lower all hands: room not connected",
1052
+ timestamp: Date.now()
1053
+ });
1054
+ return;
1055
+ }
1056
+ const senderInfo = this.getSenderInfo();
1057
+ useRaiseHandStore.getState().lowerAllHands();
1058
+ try {
1059
+ const envelope = {
1060
+ v: 1,
1061
+ kind: "raise-hand",
1062
+ roomId: this.room.name,
1063
+ ts: Date.now(),
1064
+ sender: senderInfo,
1065
+ payload: {
1066
+ action: "lower-all"
1067
+ }
1068
+ };
1069
+ await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1070
+ topic: "raise-hand:v1"
1071
+ });
1072
+ logger2.debug("All hands lowered");
1073
+ } catch (error) {
1074
+ logger2.error("Failed to lower all hands", error);
1075
+ useRtcStore.getState().addError({
1076
+ code: "LOWER_ALL_HANDS_SEND_FAILED",
1077
+ message: error instanceof Error ? error.message : "Failed to lower all hands",
1078
+ timestamp: Date.now()
1079
+ });
1080
+ }
1081
+ }
1082
+ handleIncoming(text) {
1083
+ try {
1084
+ const parsed = JSON.parse(text);
1085
+ if (!this.isValidEnvelope(parsed)) {
1086
+ logger2.warn("Invalid raise-hand envelope received", parsed);
1087
+ return;
1088
+ }
1089
+ if (parsed.sender.id === this.getLocalParticipantId()) {
1090
+ logger2.debug("Ignoring self-echo raise-hand message");
1091
+ return;
1092
+ }
1093
+ applyIncomingRaiseHand(parsed);
1094
+ logger2.debug("Raise-hand message received", {
1095
+ action: parsed.payload.action
1096
+ });
1097
+ } catch (error) {
1098
+ logger2.error("Error parsing incoming raise-hand message", error);
1099
+ }
1100
+ }
1101
+ isValidEnvelope(e) {
1102
+ 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);
1103
+ }
1104
+ getSenderInfo() {
1105
+ const localParticipant = this.room.localParticipant;
1106
+ const sender = {
1107
+ id: localParticipant.identity
1108
+ };
1109
+ if (localParticipant.metadata) {
1110
+ try {
1111
+ sender.info = JSON.parse(
1112
+ localParticipant.metadata
1113
+ );
1114
+ } catch {
1115
+ }
1116
+ }
1117
+ return sender;
1118
+ }
1119
+ };
1120
+ var logger3 = createLogger("raise-hand:hook");
1121
+ function useRaiseHand() {
1122
+ const service = useFeatureService("raise-hand");
1123
+ const raisedHands = useRaiseHandStore((state) => state.raisedHands);
1124
+ const isHandRaised = useRaiseHandStore((state) => state.isHandRaised);
1125
+ const getRaisedHandOrder = useRaiseHandStore(
1126
+ (state) => state.getRaisedHandOrder
1127
+ );
1128
+ const raiseHand = react.useCallback(async () => {
1129
+ if (!service) {
1130
+ logger3.error("Cannot raise hand: service not ready");
1131
+ return;
1132
+ }
1133
+ return service.raiseHand();
1134
+ }, [service]);
1135
+ const lowerHand = react.useCallback(
1136
+ async (participantId) => {
1137
+ if (!service) {
1138
+ logger3.error("Cannot lower hand: service not ready");
1139
+ return;
1140
+ }
1141
+ return service.lowerHand(participantId);
1142
+ },
1143
+ [service]
1144
+ );
1145
+ const lowerAllHands = react.useCallback(async () => {
1146
+ if (!service) {
1147
+ logger3.error("Cannot lower all hands: service not ready");
1148
+ return;
1149
+ }
1150
+ return service.lowerAllHands();
1151
+ }, [service]);
1152
+ const getRaisedHands = react.useCallback(() => {
1153
+ const hands = [];
1154
+ for (const [participantId, hand] of raisedHands.entries()) {
1155
+ hands.push({
1156
+ participantId,
1157
+ order: hand.order,
1158
+ ts: hand.ts
1159
+ });
1160
+ }
1161
+ return hands.sort((a, b) => a.order - b.order);
1162
+ }, [raisedHands]);
1163
+ return {
1164
+ raiseHand,
1165
+ lowerHand,
1166
+ lowerAllHands,
1167
+ isHandRaised,
1168
+ getRaisedHandOrder,
1169
+ getRaisedHands,
1170
+ isReady: !!service
1171
+ };
1172
+ }
1173
+ var defaultState3 = {
884
1174
  reactions: /* @__PURE__ */ new Map(),
885
1175
  participantCache: {},
886
1176
  ttlMs: 4e3
887
1177
  };
888
1178
  var useReactionsStore = zustand.create()(
889
1179
  immer.immer((set) => ({
890
- ...defaultState2,
1180
+ ...defaultState3,
891
1181
  setReaction: (participantId, reaction) => set((state) => {
892
1182
  state.reactions.set(participantId, reaction);
893
1183
  }),
@@ -908,7 +1198,7 @@ var useReactionsStore = zustand.create()(
908
1198
  clear: () => set(() => ({
909
1199
  reactions: /* @__PURE__ */ new Map(),
910
1200
  participantCache: {},
911
- ttlMs: defaultState2.ttlMs
1201
+ ttlMs: defaultState3.ttlMs
912
1202
  }))
913
1203
  }))
914
1204
  );
@@ -940,7 +1230,7 @@ function generateNonce() {
940
1230
  }
941
1231
 
942
1232
  // src/channel/reactions/service.ts
943
- var logger3 = createLogger("reactions");
1233
+ var logger5 = createLogger("reactions");
944
1234
  var ReactionsService = class {
945
1235
  constructor(room) {
946
1236
  this.isSubscribed = false;
@@ -951,15 +1241,15 @@ var ReactionsService = class {
951
1241
  }
952
1242
  isRoomReady() {
953
1243
  if (!this.room) {
954
- logger3.warn("Room not initialized");
1244
+ logger5.warn("Room not initialized");
955
1245
  return false;
956
1246
  }
957
1247
  if (this.room.state !== livekitClient.ConnectionState.Connected) {
958
- logger3.warn("Room not connected", { state: this.room.state });
1248
+ logger5.warn("Room not connected", { state: this.room.state });
959
1249
  return false;
960
1250
  }
961
1251
  if (!this.room.localParticipant) {
962
- logger3.warn("Local participant not available");
1252
+ logger5.warn("Local participant not available");
963
1253
  return false;
964
1254
  }
965
1255
  return true;
@@ -977,14 +1267,14 @@ var ReactionsService = class {
977
1267
  const text = await reader.readAll();
978
1268
  this.handleIncoming(text);
979
1269
  } catch (err) {
980
- logger3.error("Error reading reactions stream", err);
1270
+ logger5.error("Error reading reactions stream", err);
981
1271
  }
982
1272
  });
983
1273
  this.pruneInterval = setInterval(() => {
984
1274
  useReactionsStore.getState().pruneExpired();
985
1275
  }, 1e3);
986
1276
  this.isSubscribed = true;
987
- logger3.info("ReactionsService subscribed");
1277
+ logger5.info("ReactionsService subscribed");
988
1278
  }
989
1279
  unsubscribe() {
990
1280
  this.isSubscribed = false;
@@ -992,7 +1282,7 @@ var ReactionsService = class {
992
1282
  clearInterval(this.pruneInterval);
993
1283
  this.pruneInterval = void 0;
994
1284
  }
995
- logger3.info("ReactionsService unsubscribed");
1285
+ logger5.info("ReactionsService unsubscribed");
996
1286
  }
997
1287
  getLocalParticipantId() {
998
1288
  return this.room.localParticipant.identity;
@@ -1016,7 +1306,7 @@ var ReactionsService = class {
1016
1306
  return;
1017
1307
  }
1018
1308
  if (!this.canSendNow()) {
1019
- logger3.debug("Rate limited, skipping send");
1309
+ logger5.debug("Rate limited, skipping send");
1020
1310
  return;
1021
1311
  }
1022
1312
  const senderInfo = this.getSenderInfo();
@@ -1042,9 +1332,9 @@ var ReactionsService = class {
1042
1332
  await this.room.localParticipant.sendText(JSON.stringify(envelope), {
1043
1333
  topic: "reactions:v1"
1044
1334
  });
1045
- logger3.debug("Reaction sent", { emoji });
1335
+ logger5.debug("Reaction sent", { emoji });
1046
1336
  } catch (error) {
1047
- logger3.error("Failed to send reaction", error);
1337
+ logger5.error("Failed to send reaction", error);
1048
1338
  useRtcStore.getState().addError({
1049
1339
  code: "REACTIONS_SEND_FAILED",
1050
1340
  message: error instanceof Error ? error.message : "Failed to send reaction",
@@ -1057,16 +1347,16 @@ var ReactionsService = class {
1057
1347
  try {
1058
1348
  const parsed = JSON.parse(text);
1059
1349
  if (!this.isValidEnvelope(parsed)) {
1060
- logger3.warn("Invalid reaction envelope received", parsed);
1350
+ logger5.warn("Invalid reaction envelope received", parsed);
1061
1351
  return;
1062
1352
  }
1063
1353
  if (parsed.sender.id === this.getLocalParticipantId()) {
1064
- logger3.debug("Ignoring self-echo reaction");
1354
+ logger5.debug("Ignoring self-echo reaction");
1065
1355
  return;
1066
1356
  }
1067
1357
  const lastTs = this.lastRemoteTs.get(parsed.sender.id) ?? Number.NEGATIVE_INFINITY;
1068
1358
  if (parsed.ts < lastTs) {
1069
- logger3.debug("Ignoring out-of-order reaction", {
1359
+ logger5.debug("Ignoring out-of-order reaction", {
1070
1360
  sender: parsed.sender.id,
1071
1361
  ts: parsed.ts,
1072
1362
  lastTs
@@ -1075,9 +1365,9 @@ var ReactionsService = class {
1075
1365
  }
1076
1366
  this.lastRemoteTs.set(parsed.sender.id, parsed.ts);
1077
1367
  applyIncomingReaction(parsed);
1078
- logger3.debug("Reaction received", { emoji: parsed.payload.emoji });
1368
+ logger5.debug("Reaction received", { emoji: parsed.payload.emoji });
1079
1369
  } catch (error) {
1080
- logger3.error("Error parsing incoming reaction", error);
1370
+ logger5.error("Error parsing incoming reaction", error);
1081
1371
  }
1082
1372
  }
1083
1373
  isValidEnvelope(e) {
@@ -1099,7 +1389,7 @@ var ReactionsService = class {
1099
1389
  return sender;
1100
1390
  }
1101
1391
  };
1102
- var logger4 = createLogger("reactions:hook");
1392
+ var logger6 = createLogger("reactions:hook");
1103
1393
  function useReactions() {
1104
1394
  const service = useFeatureService("reactions");
1105
1395
  const reactions = useReactionsStore((state) => state.reactions);
@@ -1118,7 +1408,7 @@ function useReactions() {
1118
1408
  const sendReaction = react.useCallback(
1119
1409
  async (emoji) => {
1120
1410
  if (!service) {
1121
- logger4.error("Cannot send reaction: service not ready");
1411
+ logger6.error("Cannot send reaction: service not ready");
1122
1412
  return;
1123
1413
  }
1124
1414
  return service.sendReaction(emoji);
@@ -1148,10 +1438,15 @@ var FEATURES = {
1148
1438
  name: "reactions",
1149
1439
  createService: (room) => new ReactionsService(room),
1150
1440
  cleanupStore: () => useReactionsStore.getState().clear()
1441
+ },
1442
+ "raise-hand": {
1443
+ name: "raise-hand",
1444
+ createService: (room) => new RaiseHandService(room),
1445
+ cleanupStore: () => useRaiseHandStore.getState().clear()
1151
1446
  }
1152
1447
  };
1153
- var DEFAULT_FEATURES = ["chat", "reactions"];
1154
- var logger5 = createLogger("channels:provider");
1448
+ var DEFAULT_FEATURES = ["chat", "reactions", "raise-hand"];
1449
+ var logger7 = createLogger("channels:provider");
1155
1450
  function DataChannelProvider({
1156
1451
  room,
1157
1452
  features = DEFAULT_FEATURES,
@@ -1161,52 +1456,52 @@ function DataChannelProvider({
1161
1456
  const [isReady, setIsReady] = react.useState(false);
1162
1457
  react.useEffect(() => {
1163
1458
  if (!room) {
1164
- logger5.warn("DataChannelProvider mounted without room");
1459
+ logger7.warn("DataChannelProvider mounted without room");
1165
1460
  return;
1166
1461
  }
1167
- logger5.debug("Initializing features", { features });
1462
+ logger7.debug("Initializing features", { features });
1168
1463
  for (const featureName of features) {
1169
1464
  const feature = FEATURES[featureName];
1170
1465
  if (!feature) {
1171
- logger5.warn(`Feature "${featureName}" not found in registry`);
1466
+ logger7.warn(`Feature "${featureName}" not found in registry`);
1172
1467
  continue;
1173
1468
  }
1174
1469
  try {
1175
- logger5.debug(`Initializing feature: ${featureName}`);
1470
+ logger7.debug(`Initializing feature: ${featureName}`);
1176
1471
  const service = feature.createService(room);
1177
1472
  service.subscribe();
1178
1473
  services.current.set(featureName, service);
1179
- logger5.info(`Feature "${featureName}" initialized`);
1474
+ logger7.info(`Feature "${featureName}" initialized`);
1180
1475
  } catch (error) {
1181
- logger5.error(`Failed to initialize feature "${featureName}"`, error);
1476
+ logger7.error(`Failed to initialize feature "${featureName}"`, error);
1182
1477
  }
1183
1478
  }
1184
1479
  setIsReady(true);
1185
- logger5.info("All features initialized");
1480
+ logger7.info("All features initialized");
1186
1481
  return () => {
1187
- logger5.debug("Cleaning up features");
1482
+ logger7.debug("Cleaning up features");
1188
1483
  services.current.forEach((service, name) => {
1189
1484
  try {
1190
- logger5.debug(`Unsubscribing feature: ${name}`);
1485
+ logger7.debug(`Unsubscribing feature: ${name}`);
1191
1486
  service.unsubscribe();
1192
1487
  } catch (error) {
1193
- logger5.error(`Failed to unsubscribe feature "${name}"`, error);
1488
+ logger7.error(`Failed to unsubscribe feature "${name}"`, error);
1194
1489
  }
1195
1490
  });
1196
1491
  for (const featureName of features) {
1197
1492
  const feature = FEATURES[featureName];
1198
1493
  if (feature?.cleanupStore) {
1199
1494
  try {
1200
- logger5.debug(`Cleaning store for feature: ${featureName}`);
1495
+ logger7.debug(`Cleaning store for feature: ${featureName}`);
1201
1496
  feature.cleanupStore();
1202
1497
  } catch (error) {
1203
- logger5.error(`Failed to cleanup store for "${featureName}"`, error);
1498
+ logger7.error(`Failed to cleanup store for "${featureName}"`, error);
1204
1499
  }
1205
1500
  }
1206
1501
  }
1207
1502
  services.current.clear();
1208
1503
  setIsReady(false);
1209
- logger5.info("Features cleanup complete");
1504
+ logger7.info("Features cleanup complete");
1210
1505
  };
1211
1506
  }, [room, features]);
1212
1507
  const contextValue = react.useMemo(
@@ -1221,6 +1516,7 @@ function DataChannelProvider({
1221
1516
 
1222
1517
  exports.ChatService = ChatService;
1223
1518
  exports.DataChannelProvider = DataChannelProvider;
1519
+ exports.RaiseHandService = RaiseHandService;
1224
1520
  exports.ReactionsService = ReactionsService;
1225
1521
  exports.applyIncomingReaction = applyIncomingReaction;
1226
1522
  exports.compareEntries = compareEntries;
@@ -1232,6 +1528,8 @@ exports.useChat = useChat;
1232
1528
  exports.useChatStore = useChatStore;
1233
1529
  exports.useDataChannelContext = useDataChannelContext;
1234
1530
  exports.useFeatureService = useFeatureService;
1531
+ exports.useRaiseHand = useRaiseHand;
1532
+ exports.useRaiseHandStore = useRaiseHandStore;
1235
1533
  exports.useReactions = useReactions;
1236
1534
  exports.useReactionsStore = useReactionsStore;
1237
1535
  exports.validateContent = validateContent;