@cuekit-ai/react 1.6.9 → 1.6.10

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.
@@ -5901,8 +5901,8 @@ function requireEvents() {
5901
5901
  ReflectApply(handler, this, args);
5902
5902
  } else {
5903
5903
  var len = handler.length;
5904
- var listeners = arrayClone(handler, len);
5905
- for (var i = 0; i < len; ++i) ReflectApply(listeners[i], this, args);
5904
+ var listeners2 = arrayClone(handler, len);
5905
+ for (var i = 0; i < len; ++i) ReflectApply(listeners2[i], this, args);
5906
5906
  }
5907
5907
  return true;
5908
5908
  };
@@ -6018,7 +6018,7 @@ function requireEvents() {
6018
6018
  };
6019
6019
  EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
6020
6020
  EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
6021
- var listeners, events2, i;
6021
+ var listeners2, events2, i;
6022
6022
  events2 = this._events;
6023
6023
  if (events2 === void 0) return this;
6024
6024
  if (events2.removeListener === void 0) {
@@ -6044,12 +6044,12 @@ function requireEvents() {
6044
6044
  this._eventsCount = 0;
6045
6045
  return this;
6046
6046
  }
6047
- listeners = events2[type];
6048
- if (typeof listeners === "function") {
6049
- this.removeListener(type, listeners);
6050
- } else if (listeners !== void 0) {
6051
- for (i = listeners.length - 1; i >= 0; i--) {
6052
- this.removeListener(type, listeners[i]);
6047
+ listeners2 = events2[type];
6048
+ if (typeof listeners2 === "function") {
6049
+ this.removeListener(type, listeners2);
6050
+ } else if (listeners2 !== void 0) {
6051
+ for (i = listeners2.length - 1; i >= 0; i--) {
6052
+ this.removeListener(type, listeners2[i]);
6053
6053
  }
6054
6054
  }
6055
6055
  return this;
@@ -6062,7 +6062,7 @@ function requireEvents() {
6062
6062
  if (typeof evlistener === "function") return unwrap ? [evlistener.listener || evlistener] : [evlistener];
6063
6063
  return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
6064
6064
  }
6065
- EventEmitter.prototype.listeners = function listeners(type) {
6065
+ EventEmitter.prototype.listeners = function listeners2(type) {
6066
6066
  return _listeners(this, type, true);
6067
6067
  };
6068
6068
  EventEmitter.prototype.rawListeners = function rawListeners(type) {
@@ -23349,7 +23349,7 @@ function normalizeToNavigationCommand(message) {
23349
23349
  var room = null;
23350
23350
  var reconnectTimeout = null;
23351
23351
  var serverUrl = WEBRTC_BACKEND_SERVER_URL || "https://api-webrtc.ansyr.ai";
23352
- var callbacks = {};
23352
+ var listeners = /* @__PURE__ */ new Set();
23353
23353
  var audioContainerRef = null;
23354
23354
  var livekitUrl = null;
23355
23355
  var token = null;
@@ -23360,16 +23360,21 @@ function setServerUrl(url) {
23360
23360
  function setAudioContainer(newAudioContainerRef) {
23361
23361
  audioContainerRef = newAudioContainerRef;
23362
23362
  }
23363
+ function registerWebRTCCallbacks(callback) {
23364
+ listeners.add(callback);
23365
+ console.log("\u{1F4E1} WebRTC listener registered. Total listeners:", listeners.size);
23366
+ }
23367
+ function unregisterWebRTCCallbacks(callback) {
23368
+ listeners.delete(callback);
23369
+ console.log("\u{1F4E1} WebRTC listener unregistered. Total listeners:", listeners.size);
23370
+ }
23363
23371
  function setWebRTCCallbacks(newCallbacks) {
23364
- console.log("\u{1F4E1} setWebRTCCallbacks called with:", {
23365
- hasOnNavigationCommand: !!newCallbacks.onNavigationCommand,
23366
- hasOnConnectionStateChange: !!newCallbacks.onConnectionStateChange,
23367
- hasOnParticipantUpdate: !!newCallbacks.onParticipantUpdate
23368
- });
23369
- callbacks = newCallbacks;
23372
+ console.warn("\u26A0\uFE0F setWebRTCCallbacks is deprecated. Use registerWebRTCCallbacks instead.");
23373
+ listeners.clear();
23374
+ listeners.add(newCallbacks);
23370
23375
  }
23371
23376
  function getCurrentCallbacks() {
23372
- return callbacks;
23377
+ return Array.from(listeners)[0] || {};
23373
23378
  }
23374
23379
  async function authenticate(userIdentity, apiKey, appId, language) {
23375
23380
  try {
@@ -23428,7 +23433,7 @@ async function connectToRoom(newLivekitUrl, newToken) {
23428
23433
  function setupEventListeners() {
23429
23434
  if (!room) return;
23430
23435
  room.on(RoomEvent.ConnectionStateChanged, (state) => {
23431
- callbacks.onConnectionStateChange?.(state);
23436
+ listeners.forEach((l) => l.onConnectionStateChange?.(state));
23432
23437
  if (state === ConnectionState.Connected) {
23433
23438
  setWebRTCConnectionState({ isConnected: true, isConnecting: false });
23434
23439
  } else if (state === ConnectionState.Disconnected) {
@@ -23449,11 +23454,17 @@ function setupEventListeners() {
23449
23454
  audioContainerRef.current.appendChild(element);
23450
23455
  if (element instanceof HTMLAudioElement) {
23451
23456
  const trackId = track.sid || `track_${Date.now()}_${Math.random()}`;
23452
- callbacks.onAISpeechStart?.(trackId);
23457
+ listeners.forEach((l) => l.onAISpeechStart?.(trackId));
23453
23458
  element.play().catch((error) => {
23454
23459
  });
23455
- element.addEventListener("ended", () => callbacks.onAISpeechEnd?.(trackId));
23456
- element.addEventListener("pause", () => callbacks.onAISpeechEnd?.(trackId));
23460
+ element.addEventListener(
23461
+ "ended",
23462
+ () => listeners.forEach((l) => l.onAISpeechEnd?.(trackId))
23463
+ );
23464
+ element.addEventListener(
23465
+ "pause",
23466
+ () => listeners.forEach((l) => l.onAISpeechEnd?.(trackId))
23467
+ );
23457
23468
  }
23458
23469
  }
23459
23470
  }
@@ -23469,13 +23480,14 @@ function setupEventListeners() {
23469
23480
  const jsonPart = parts[1];
23470
23481
  console.log("\u{1F4E1} WebRTC Pipe-separated message:", { textPart, jsonPart });
23471
23482
  if (textPart) {
23472
- callbacks.onNavigationCommand?.({ type: "speech_text", data: textPart });
23483
+ listeners.forEach((l) => l.onNavigationCommand?.({ type: "speech_text", data: textPart }));
23473
23484
  }
23474
23485
  if (jsonPart) {
23475
23486
  try {
23476
23487
  const message = JSON.parse(jsonPart);
23477
23488
  console.log("\u{1F4E1} WebRTC Parsed JSON message:", message);
23478
- callbacks.onNavigationCommand?.(normalizeToNavigationCommand(message));
23489
+ const normalized = normalizeToNavigationCommand(message);
23490
+ listeners.forEach((l) => l.onNavigationCommand?.(normalized));
23479
23491
  } catch (error) {
23480
23492
  console.log("\u{1F4E1} WebRTC JSON parse error for JSON part:", error, "JSON part:", jsonPart);
23481
23493
  }
@@ -23484,11 +23496,12 @@ function setupEventListeners() {
23484
23496
  try {
23485
23497
  const message = JSON.parse(decodedPayload);
23486
23498
  console.log("\u{1F4E1} WebRTC Parsed message:", message);
23487
- callbacks.onNavigationCommand?.(normalizeToNavigationCommand(message));
23499
+ const normalized = normalizeToNavigationCommand(message);
23500
+ listeners.forEach((l) => l.onNavigationCommand?.(normalized));
23488
23501
  } catch (error) {
23489
23502
  console.log("\u{1F4E1} WebRTC JSON parse error:", error, "Raw payload:", decodedPayload);
23490
23503
  const message = decodedPayload;
23491
- callbacks.onNavigationCommand?.({ type: "raw_text", data: message });
23504
+ listeners.forEach((l) => l.onNavigationCommand?.({ type: "raw_text", data: message }));
23492
23505
  }
23493
23506
  }
23494
23507
  }).on(RoomEvent.Disconnected, () => {
@@ -23502,7 +23515,7 @@ function updateParticipantsList() {
23502
23515
  ...Array.from(room.remoteParticipants.values()).map((p) => p.identity)
23503
23516
  ];
23504
23517
  setWebRTCConnectionState({ participants });
23505
- callbacks.onParticipantUpdate?.(participants);
23518
+ listeners.forEach((l) => l.onParticipantUpdate?.(participants));
23506
23519
  }
23507
23520
  async function sendData(data, reliable = true) {
23508
23521
  if (!room) throw new Error("Not connected to room");
@@ -23635,6 +23648,8 @@ export {
23635
23648
  validateDynamicElements,
23636
23649
  setServerUrl,
23637
23650
  setAudioContainer,
23651
+ registerWebRTCCallbacks,
23652
+ unregisterWebRTCCallbacks,
23638
23653
  setWebRTCCallbacks,
23639
23654
  getCurrentCallbacks,
23640
23655
  authenticate,
package/dist/index.js CHANGED
@@ -2524,8 +2524,8 @@ function requireEvents() {
2524
2524
  ReflectApply(handler, this, args);
2525
2525
  } else {
2526
2526
  var len = handler.length;
2527
- var listeners = arrayClone(handler, len);
2528
- for (var i2 = 0; i2 < len; ++i2) ReflectApply(listeners[i2], this, args);
2527
+ var listeners2 = arrayClone(handler, len);
2528
+ for (var i2 = 0; i2 < len; ++i2) ReflectApply(listeners2[i2], this, args);
2529
2529
  }
2530
2530
  return true;
2531
2531
  };
@@ -2641,7 +2641,7 @@ function requireEvents() {
2641
2641
  };
2642
2642
  EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
2643
2643
  EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) {
2644
- var listeners, events2, i2;
2644
+ var listeners2, events2, i2;
2645
2645
  events2 = this._events;
2646
2646
  if (events2 === void 0) return this;
2647
2647
  if (events2.removeListener === void 0) {
@@ -2667,12 +2667,12 @@ function requireEvents() {
2667
2667
  this._eventsCount = 0;
2668
2668
  return this;
2669
2669
  }
2670
- listeners = events2[type];
2671
- if (typeof listeners === "function") {
2672
- this.removeListener(type, listeners);
2673
- } else if (listeners !== void 0) {
2674
- for (i2 = listeners.length - 1; i2 >= 0; i2--) {
2675
- this.removeListener(type, listeners[i2]);
2670
+ listeners2 = events2[type];
2671
+ if (typeof listeners2 === "function") {
2672
+ this.removeListener(type, listeners2);
2673
+ } else if (listeners2 !== void 0) {
2674
+ for (i2 = listeners2.length - 1; i2 >= 0; i2--) {
2675
+ this.removeListener(type, listeners2[i2]);
2676
2676
  }
2677
2677
  }
2678
2678
  return this;
@@ -2685,7 +2685,7 @@ function requireEvents() {
2685
2685
  if (typeof evlistener === "function") return unwrap ? [evlistener.listener || evlistener] : [evlistener];
2686
2686
  return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener, evlistener.length);
2687
2687
  }
2688
- EventEmitter.prototype.listeners = function listeners(type) {
2688
+ EventEmitter.prototype.listeners = function listeners2(type) {
2689
2689
  return _listeners(this, type, true);
2690
2690
  };
2691
2691
  EventEmitter.prototype.rawListeners = function rawListeners(type) {
@@ -23354,6 +23354,7 @@ __export(webrtc_service_exports, {
23354
23354
  getRoom: () => getRoom,
23355
23355
  getRoomName: () => getRoomName,
23356
23356
  isConnected: () => isConnected,
23357
+ registerWebRTCCallbacks: () => registerWebRTCCallbacks,
23357
23358
  sendData: () => sendData,
23358
23359
  sendRuntimeData: () => sendRuntimeData,
23359
23360
  sendScreenStatus: () => sendScreenStatus,
@@ -23361,7 +23362,8 @@ __export(webrtc_service_exports, {
23361
23362
  sendUserCommand: () => sendUserCommand,
23362
23363
  setAudioContainer: () => setAudioContainer,
23363
23364
  setServerUrl: () => setServerUrl,
23364
- setWebRTCCallbacks: () => setWebRTCCallbacks
23365
+ setWebRTCCallbacks: () => setWebRTCCallbacks,
23366
+ unregisterWebRTCCallbacks: () => unregisterWebRTCCallbacks
23365
23367
  });
23366
23368
  function normalizeToNavigationCommand(message) {
23367
23369
  if (!message || typeof message !== "object") return message;
@@ -23377,16 +23379,21 @@ function setServerUrl(url) {
23377
23379
  function setAudioContainer(newAudioContainerRef) {
23378
23380
  audioContainerRef = newAudioContainerRef;
23379
23381
  }
23382
+ function registerWebRTCCallbacks(callback) {
23383
+ listeners.add(callback);
23384
+ console.log("\u{1F4E1} WebRTC listener registered. Total listeners:", listeners.size);
23385
+ }
23386
+ function unregisterWebRTCCallbacks(callback) {
23387
+ listeners.delete(callback);
23388
+ console.log("\u{1F4E1} WebRTC listener unregistered. Total listeners:", listeners.size);
23389
+ }
23380
23390
  function setWebRTCCallbacks(newCallbacks) {
23381
- console.log("\u{1F4E1} setWebRTCCallbacks called with:", {
23382
- hasOnNavigationCommand: !!newCallbacks.onNavigationCommand,
23383
- hasOnConnectionStateChange: !!newCallbacks.onConnectionStateChange,
23384
- hasOnParticipantUpdate: !!newCallbacks.onParticipantUpdate
23385
- });
23386
- callbacks = newCallbacks;
23391
+ console.warn("\u26A0\uFE0F setWebRTCCallbacks is deprecated. Use registerWebRTCCallbacks instead.");
23392
+ listeners.clear();
23393
+ listeners.add(newCallbacks);
23387
23394
  }
23388
23395
  function getCurrentCallbacks() {
23389
- return callbacks;
23396
+ return Array.from(listeners)[0] || {};
23390
23397
  }
23391
23398
  async function authenticate(userIdentity, apiKey, appId, language) {
23392
23399
  try {
@@ -23445,7 +23452,7 @@ async function connectToRoom(newLivekitUrl, newToken) {
23445
23452
  function setupEventListeners() {
23446
23453
  if (!room) return;
23447
23454
  room.on(RoomEvent.ConnectionStateChanged, (state) => {
23448
- callbacks.onConnectionStateChange?.(state);
23455
+ listeners.forEach((l) => l.onConnectionStateChange?.(state));
23449
23456
  if (state === ConnectionState.Connected) {
23450
23457
  setWebRTCConnectionState({ isConnected: true, isConnecting: false });
23451
23458
  } else if (state === ConnectionState.Disconnected) {
@@ -23466,11 +23473,17 @@ function setupEventListeners() {
23466
23473
  audioContainerRef.current.appendChild(element3);
23467
23474
  if (element3 instanceof HTMLAudioElement) {
23468
23475
  const trackId = track.sid || `track_${Date.now()}_${Math.random()}`;
23469
- callbacks.onAISpeechStart?.(trackId);
23476
+ listeners.forEach((l) => l.onAISpeechStart?.(trackId));
23470
23477
  element3.play().catch((error) => {
23471
23478
  });
23472
- element3.addEventListener("ended", () => callbacks.onAISpeechEnd?.(trackId));
23473
- element3.addEventListener("pause", () => callbacks.onAISpeechEnd?.(trackId));
23479
+ element3.addEventListener(
23480
+ "ended",
23481
+ () => listeners.forEach((l) => l.onAISpeechEnd?.(trackId))
23482
+ );
23483
+ element3.addEventListener(
23484
+ "pause",
23485
+ () => listeners.forEach((l) => l.onAISpeechEnd?.(trackId))
23486
+ );
23474
23487
  }
23475
23488
  }
23476
23489
  }
@@ -23486,13 +23499,14 @@ function setupEventListeners() {
23486
23499
  const jsonPart = parts[1];
23487
23500
  console.log("\u{1F4E1} WebRTC Pipe-separated message:", { textPart, jsonPart });
23488
23501
  if (textPart) {
23489
- callbacks.onNavigationCommand?.({ type: "speech_text", data: textPart });
23502
+ listeners.forEach((l) => l.onNavigationCommand?.({ type: "speech_text", data: textPart }));
23490
23503
  }
23491
23504
  if (jsonPart) {
23492
23505
  try {
23493
23506
  const message = JSON.parse(jsonPart);
23494
23507
  console.log("\u{1F4E1} WebRTC Parsed JSON message:", message);
23495
- callbacks.onNavigationCommand?.(normalizeToNavigationCommand(message));
23508
+ const normalized = normalizeToNavigationCommand(message);
23509
+ listeners.forEach((l) => l.onNavigationCommand?.(normalized));
23496
23510
  } catch (error) {
23497
23511
  console.log("\u{1F4E1} WebRTC JSON parse error for JSON part:", error, "JSON part:", jsonPart);
23498
23512
  }
@@ -23501,11 +23515,12 @@ function setupEventListeners() {
23501
23515
  try {
23502
23516
  const message = JSON.parse(decodedPayload);
23503
23517
  console.log("\u{1F4E1} WebRTC Parsed message:", message);
23504
- callbacks.onNavigationCommand?.(normalizeToNavigationCommand(message));
23518
+ const normalized = normalizeToNavigationCommand(message);
23519
+ listeners.forEach((l) => l.onNavigationCommand?.(normalized));
23505
23520
  } catch (error) {
23506
23521
  console.log("\u{1F4E1} WebRTC JSON parse error:", error, "Raw payload:", decodedPayload);
23507
23522
  const message = decodedPayload;
23508
- callbacks.onNavigationCommand?.({ type: "raw_text", data: message });
23523
+ listeners.forEach((l) => l.onNavigationCommand?.({ type: "raw_text", data: message }));
23509
23524
  }
23510
23525
  }
23511
23526
  }).on(RoomEvent.Disconnected, () => {
@@ -23519,7 +23534,7 @@ function updateParticipantsList() {
23519
23534
  ...Array.from(room.remoteParticipants.values()).map((p) => p.identity)
23520
23535
  ];
23521
23536
  setWebRTCConnectionState({ participants });
23522
- callbacks.onParticipantUpdate?.(participants);
23537
+ listeners.forEach((l) => l.onParticipantUpdate?.(participants));
23523
23538
  }
23524
23539
  async function sendData(data, reliable = true) {
23525
23540
  if (!room) throw new Error("Not connected to room");
@@ -23624,7 +23639,7 @@ async function disconnectFromRoom() {
23624
23639
  function getRoom() {
23625
23640
  return room;
23626
23641
  }
23627
- var room, reconnectTimeout, serverUrl, callbacks, audioContainerRef, livekitUrl, token, roomName;
23642
+ var room, reconnectTimeout, serverUrl, listeners, audioContainerRef, livekitUrl, token, roomName;
23628
23643
  var init_webrtc_service = __esm({
23629
23644
  "src/utils/webrtc-service.ts"() {
23630
23645
  "use strict";
@@ -23636,7 +23651,7 @@ var init_webrtc_service = __esm({
23636
23651
  room = null;
23637
23652
  reconnectTimeout = null;
23638
23653
  serverUrl = WEBRTC_BACKEND_SERVER_URL || "https://api-webrtc.ansyr.ai";
23639
- callbacks = {};
23654
+ listeners = /* @__PURE__ */ new Set();
23640
23655
  audioContainerRef = null;
23641
23656
  livekitUrl = null;
23642
23657
  token = null;
@@ -25027,31 +25042,43 @@ var AnsyrProvider = ({
25027
25042
  };
25028
25043
  }, [navigationHandler2]);
25029
25044
  (0, import_react.useEffect)(() => {
25030
- Promise.resolve().then(() => (init_webrtc_service(), webrtc_service_exports)).then(({ setWebRTCCallbacks: setWebRTCCallbacks2 }) => {
25031
- setWebRTCCallbacks2({
25032
- onNavigationCommand: (command) => {
25033
- const data = command.data ?? command;
25034
- if (!data?.actionType) return;
25035
- if (data.actionType === "navigate" && data.routeName) {
25036
- if (navigationHandler2) {
25037
- navigationHandler2(data.routeName);
25038
- }
25039
- } else if (data.actionType === "click" && data.elementId) {
25040
- console.log("AI intent: Click element", data.elementId);
25041
- }
25042
- },
25043
- onConnectionStateChange: (state) => {
25044
- if (onConnectionStateChange) {
25045
- onConnectionStateChange(state);
25046
- }
25047
- },
25048
- onParticipantUpdate: (participants) => {
25049
- if (onParticipantUpdate) {
25050
- onParticipantUpdate(participants);
25045
+ let cleanup = null;
25046
+ Promise.resolve().then(() => (init_webrtc_service(), webrtc_service_exports)).then(
25047
+ ({ registerWebRTCCallbacks: registerWebRTCCallbacks2, unregisterWebRTCCallbacks: unregisterWebRTCCallbacks2 }) => {
25048
+ const listeners2 = {
25049
+ onNavigationCommand: (command) => {
25050
+ const data = command.data ?? command;
25051
+ if (!data?.actionType) return;
25052
+ if (data.actionType === "navigate" && data.routeName) {
25053
+ if (navigationHandler2) {
25054
+ navigationHandler2(data.routeName);
25055
+ }
25056
+ } else if (data.actionType === "click" && data.elementId) {
25057
+ console.log("AI intent: Click element", data.elementId);
25058
+ }
25059
+ },
25060
+ onConnectionStateChange: (state) => {
25061
+ if (onConnectionStateChange) {
25062
+ onConnectionStateChange(state);
25063
+ }
25064
+ },
25065
+ onParticipantUpdate: (participants) => {
25066
+ if (onParticipantUpdate) {
25067
+ onParticipantUpdate(participants);
25068
+ }
25051
25069
  }
25052
- }
25053
- });
25054
- });
25070
+ };
25071
+ registerWebRTCCallbacks2(listeners2);
25072
+ cleanup = () => {
25073
+ unregisterWebRTCCallbacks2(listeners2);
25074
+ };
25075
+ }
25076
+ );
25077
+ return () => {
25078
+ if (cleanup) {
25079
+ cleanup();
25080
+ }
25081
+ };
25055
25082
  }, [onConnectionStateChange, onParticipantUpdate, navigationHandler2]);
25056
25083
  (0, import_react.useEffect)(() => {
25057
25084
  const updateGlobalStore = (id) => {
@@ -25142,6 +25169,7 @@ var useWebRTC = (options) => {
25142
25169
  const [participants, setParticipants] = (0, import_react2.useState)([]);
25143
25170
  const [room2, setRoom] = (0, import_react2.useState)(null);
25144
25171
  const [error, setError] = (0, import_react2.useState)(null);
25172
+ const listenersRef = (0, import_react2.useRef)(null);
25145
25173
  const audioContainerRef2 = (0, import_react2.useRef)(null);
25146
25174
  (0, import_react2.useEffect)(() => {
25147
25175
  setAudioContainer(audioContainerRef2);
@@ -25157,15 +25185,20 @@ var useWebRTC = (options) => {
25157
25185
  setParticipants(participants2);
25158
25186
  options?.onParticipantUpdate?.(participants2);
25159
25187
  };
25160
- setWebRTCCallbacks({
25188
+ const listeners2 = {
25161
25189
  onConnectionStateChange: handleConnectionStateChange,
25162
25190
  onParticipantUpdate: handleParticipantUpdate,
25163
25191
  onNavigationCommand: options?.onNavigationCommand,
25164
25192
  onAISpeechStart: options?.onAISpeechStart,
25165
25193
  onAISpeechEnd: options?.onAISpeechEnd
25166
- });
25194
+ };
25195
+ listenersRef.current = listeners2;
25196
+ registerWebRTCCallbacks(listeners2);
25167
25197
  return () => {
25168
- setWebRTCCallbacks({});
25198
+ if (listenersRef.current) {
25199
+ unregisterWebRTCCallbacks(listenersRef.current);
25200
+ listenersRef.current = null;
25201
+ }
25169
25202
  };
25170
25203
  }, [
25171
25204
  options?.onConnectionStateChange,
@@ -25227,30 +25260,85 @@ var useCuekit = (options) => {
25227
25260
  const currentUserMessageRef = (0, import_react3.useRef)(null);
25228
25261
  const currentAIMessageRef = (0, import_react3.useRef)(null);
25229
25262
  const handleMessageChunk = (0, import_react3.useCallback)((text7, role, isFinal) => {
25230
- const currentMessageRef = role === "user" ? currentUserMessageRef : currentAIMessageRef;
25231
- const messageToUpdate = currentMessageRef.current;
25232
- if (!messageToUpdate && !text7 && isFinal) {
25233
- return;
25234
- }
25235
- if (messageToUpdate) {
25236
- const updatedMessage = { ...messageToUpdate, text: messageToUpdate.text + text7, isFinal };
25237
- currentMessageRef.current = updatedMessage;
25238
- setMessages(
25239
- (prev) => prev.map((msg) => msg.id === messageToUpdate.id ? updatedMessage : msg)
25240
- );
25241
- } else {
25242
- const newMessage = {
25243
- id: `${role}-${Date.now()}`,
25263
+ const activeRef = role === "user" ? currentUserMessageRef : currentAIMessageRef;
25264
+ console.log(`\u{1F50D} handleMessageChunk called:`, {
25265
+ role,
25266
+ text: text7.substring(0, 50) + (text7.length > 50 ? "..." : ""),
25267
+ textLength: text7.length,
25268
+ isFinal,
25269
+ hasExistingMessage: !!activeRef.current,
25270
+ existingMessageId: activeRef.current?.id
25271
+ });
25272
+ const messageToUpdate = activeRef.current;
25273
+ let updatedMessage = null;
25274
+ setMessages((prev) => {
25275
+ console.log(`\u{1F50D} setMessages callback:`, {
25244
25276
  role,
25245
- text: text7,
25246
- isFinal,
25247
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
25248
- };
25249
- currentMessageRef.current = newMessage;
25250
- setMessages((prev) => [...prev, newMessage]);
25277
+ messageToUpdate: messageToUpdate ? { id: messageToUpdate.id, textLength: messageToUpdate.text.length } : null,
25278
+ prevMessagesCount: prev.length,
25279
+ prevMessages: prev.map((m) => ({
25280
+ id: m.id,
25281
+ role: m.role,
25282
+ textLength: m.text.length,
25283
+ isFinal: m.isFinal
25284
+ }))
25285
+ });
25286
+ if (!messageToUpdate && !text7 && isFinal) {
25287
+ console.log(`\u{1F50D} Skipping empty final chunk (no existing message)`);
25288
+ return prev;
25289
+ }
25290
+ if (messageToUpdate) {
25291
+ const existsInState = prev.some((msg) => msg.id === messageToUpdate.id);
25292
+ const newUpdatedMessage = { ...messageToUpdate, text: messageToUpdate.text + text7, isFinal };
25293
+ updatedMessage = newUpdatedMessage;
25294
+ if (!existsInState) {
25295
+ console.log(`\u{1F50D} \u26A0\uFE0F Message ${messageToUpdate.id} not in state yet, adding it:`, {
25296
+ id: newUpdatedMessage.id,
25297
+ textLength: newUpdatedMessage.text.length,
25298
+ isFinal: newUpdatedMessage.isFinal
25299
+ });
25300
+ return [...prev, newUpdatedMessage];
25301
+ } else {
25302
+ console.log(`\u{1F50D} Updating existing ${role} message:`, {
25303
+ id: newUpdatedMessage.id,
25304
+ newTextLength: newUpdatedMessage.text.length,
25305
+ isFinal: newUpdatedMessage.isFinal
25306
+ });
25307
+ return prev.map((msg) => msg.id === messageToUpdate.id ? newUpdatedMessage : msg);
25308
+ }
25309
+ } else {
25310
+ const newMessage = {
25311
+ id: `${role}-${Date.now()}`,
25312
+ role,
25313
+ text: text7,
25314
+ isFinal,
25315
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
25316
+ };
25317
+ updatedMessage = newMessage;
25318
+ console.log(`\u{1F50D} Creating NEW ${role} message:`, {
25319
+ id: newMessage.id,
25320
+ textLength: newMessage.text.length,
25321
+ isFinal: newMessage.isFinal
25322
+ });
25323
+ const newArray = [...prev, newMessage];
25324
+ console.log(
25325
+ `\u{1F50D} New messages array:`,
25326
+ newArray.map((m) => ({
25327
+ id: m.id,
25328
+ role: m.role,
25329
+ textLength: m.text.length,
25330
+ isFinal: m.isFinal
25331
+ }))
25332
+ );
25333
+ return newArray;
25334
+ }
25335
+ });
25336
+ if (updatedMessage && !isFinal) {
25337
+ activeRef.current = updatedMessage;
25251
25338
  }
25252
25339
  if (isFinal) {
25253
- currentMessageRef.current = null;
25340
+ console.log(`\u{1F50D} Clearing ${role} ref (message finalized)`);
25341
+ activeRef.current = null;
25254
25342
  }
25255
25343
  }, []);
25256
25344
  const handleAIInterruption = (0, import_react3.useCallback)(() => {
@@ -25276,12 +25364,9 @@ var useCuekit = (options) => {
25276
25364
  const handleNavigationCommand = (event) => {
25277
25365
  console.log(`\u2B07\uFE0F Received event from backend: ${event.type}`, event);
25278
25366
  switch (event.type) {
25279
- case "speech_text": {
25280
- console.log("\u{1F5E3}\uFE0F AI Speech text:", event.data);
25281
- break;
25282
- }
25367
+ // raw_text is a redundant "reliability packet" - we already process ai_speech_chunk
25283
25368
  case "raw_text": {
25284
- console.log("\u{1F4DD} Raw text message:", event.data);
25369
+ console.log("\u{1F4DD} Raw text message (ignored, using ai_speech_chunk instead):", event.data);
25285
25370
  break;
25286
25371
  }
25287
25372
  case "user_speech_chunk":
package/dist/index.mjs CHANGED
@@ -23,6 +23,7 @@ import {
23
23
  getRoom,
24
24
  getRoomName,
25
25
  onStateChange,
26
+ registerWebRTCCallbacks,
26
27
  resolveRoutePath,
27
28
  sendData,
28
29
  sendRuntimeData,
@@ -34,10 +35,10 @@ import {
34
35
  setAudioContainer,
35
36
  setNavigationHandler,
36
37
  setServerUrl,
37
- setWebRTCCallbacks,
38
38
  setWebRTCConfig,
39
+ unregisterWebRTCCallbacks,
39
40
  validateDynamicElements
40
- } from "./chunk-ZP7AXODE.mjs";
41
+ } from "./chunk-XVA5MZMJ.mjs";
41
42
 
42
43
  // node_modules/inline-style-parser/index.js
43
44
  var require_inline_style_parser = __commonJS({
@@ -1384,31 +1385,43 @@ var AnsyrProvider = ({
1384
1385
  };
1385
1386
  }, [navigationHandler]);
1386
1387
  useEffect(() => {
1387
- import("./webrtc-service-IMARTHNM.mjs").then(({ setWebRTCCallbacks: setWebRTCCallbacks2 }) => {
1388
- setWebRTCCallbacks2({
1389
- onNavigationCommand: (command) => {
1390
- const data = command.data ?? command;
1391
- if (!data?.actionType) return;
1392
- if (data.actionType === "navigate" && data.routeName) {
1393
- if (navigationHandler) {
1394
- navigationHandler(data.routeName);
1388
+ let cleanup = null;
1389
+ import("./webrtc-service-XS2JQUBW.mjs").then(
1390
+ ({ registerWebRTCCallbacks: registerWebRTCCallbacks2, unregisterWebRTCCallbacks: unregisterWebRTCCallbacks2 }) => {
1391
+ const listeners = {
1392
+ onNavigationCommand: (command) => {
1393
+ const data = command.data ?? command;
1394
+ if (!data?.actionType) return;
1395
+ if (data.actionType === "navigate" && data.routeName) {
1396
+ if (navigationHandler) {
1397
+ navigationHandler(data.routeName);
1398
+ }
1399
+ } else if (data.actionType === "click" && data.elementId) {
1400
+ console.log("AI intent: Click element", data.elementId);
1401
+ }
1402
+ },
1403
+ onConnectionStateChange: (state) => {
1404
+ if (onConnectionStateChange) {
1405
+ onConnectionStateChange(state);
1406
+ }
1407
+ },
1408
+ onParticipantUpdate: (participants) => {
1409
+ if (onParticipantUpdate) {
1410
+ onParticipantUpdate(participants);
1395
1411
  }
1396
- } else if (data.actionType === "click" && data.elementId) {
1397
- console.log("AI intent: Click element", data.elementId);
1398
- }
1399
- },
1400
- onConnectionStateChange: (state) => {
1401
- if (onConnectionStateChange) {
1402
- onConnectionStateChange(state);
1403
- }
1404
- },
1405
- onParticipantUpdate: (participants) => {
1406
- if (onParticipantUpdate) {
1407
- onParticipantUpdate(participants);
1408
1412
  }
1409
- }
1410
- });
1411
- });
1413
+ };
1414
+ registerWebRTCCallbacks2(listeners);
1415
+ cleanup = () => {
1416
+ unregisterWebRTCCallbacks2(listeners);
1417
+ };
1418
+ }
1419
+ );
1420
+ return () => {
1421
+ if (cleanup) {
1422
+ cleanup();
1423
+ }
1424
+ };
1412
1425
  }, [onConnectionStateChange, onParticipantUpdate, navigationHandler]);
1413
1426
  useEffect(() => {
1414
1427
  const updateGlobalStore = (id) => {
@@ -1495,6 +1508,7 @@ var useWebRTC = (options) => {
1495
1508
  const [participants, setParticipants] = useState2([]);
1496
1509
  const [room, setRoom] = useState2(null);
1497
1510
  const [error, setError] = useState2(null);
1511
+ const listenersRef = useRef(null);
1498
1512
  const audioContainerRef = useRef(null);
1499
1513
  useEffect2(() => {
1500
1514
  setAudioContainer(audioContainerRef);
@@ -1510,15 +1524,20 @@ var useWebRTC = (options) => {
1510
1524
  setParticipants(participants2);
1511
1525
  options?.onParticipantUpdate?.(participants2);
1512
1526
  };
1513
- setWebRTCCallbacks({
1527
+ const listeners = {
1514
1528
  onConnectionStateChange: handleConnectionStateChange,
1515
1529
  onParticipantUpdate: handleParticipantUpdate,
1516
1530
  onNavigationCommand: options?.onNavigationCommand,
1517
1531
  onAISpeechStart: options?.onAISpeechStart,
1518
1532
  onAISpeechEnd: options?.onAISpeechEnd
1519
- });
1533
+ };
1534
+ listenersRef.current = listeners;
1535
+ registerWebRTCCallbacks(listeners);
1520
1536
  return () => {
1521
- setWebRTCCallbacks({});
1537
+ if (listenersRef.current) {
1538
+ unregisterWebRTCCallbacks(listenersRef.current);
1539
+ listenersRef.current = null;
1540
+ }
1522
1541
  };
1523
1542
  }, [
1524
1543
  options?.onConnectionStateChange,
@@ -1578,30 +1597,85 @@ var useCuekit = (options) => {
1578
1597
  const currentUserMessageRef = useRef2(null);
1579
1598
  const currentAIMessageRef = useRef2(null);
1580
1599
  const handleMessageChunk = useCallback2((text7, role, isFinal) => {
1581
- const currentMessageRef = role === "user" ? currentUserMessageRef : currentAIMessageRef;
1582
- const messageToUpdate = currentMessageRef.current;
1583
- if (!messageToUpdate && !text7 && isFinal) {
1584
- return;
1585
- }
1586
- if (messageToUpdate) {
1587
- const updatedMessage = { ...messageToUpdate, text: messageToUpdate.text + text7, isFinal };
1588
- currentMessageRef.current = updatedMessage;
1589
- setMessages(
1590
- (prev) => prev.map((msg) => msg.id === messageToUpdate.id ? updatedMessage : msg)
1591
- );
1592
- } else {
1593
- const newMessage = {
1594
- id: `${role}-${Date.now()}`,
1600
+ const activeRef = role === "user" ? currentUserMessageRef : currentAIMessageRef;
1601
+ console.log(`\u{1F50D} handleMessageChunk called:`, {
1602
+ role,
1603
+ text: text7.substring(0, 50) + (text7.length > 50 ? "..." : ""),
1604
+ textLength: text7.length,
1605
+ isFinal,
1606
+ hasExistingMessage: !!activeRef.current,
1607
+ existingMessageId: activeRef.current?.id
1608
+ });
1609
+ const messageToUpdate = activeRef.current;
1610
+ let updatedMessage = null;
1611
+ setMessages((prev) => {
1612
+ console.log(`\u{1F50D} setMessages callback:`, {
1595
1613
  role,
1596
- text: text7,
1597
- isFinal,
1598
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
1599
- };
1600
- currentMessageRef.current = newMessage;
1601
- setMessages((prev) => [...prev, newMessage]);
1614
+ messageToUpdate: messageToUpdate ? { id: messageToUpdate.id, textLength: messageToUpdate.text.length } : null,
1615
+ prevMessagesCount: prev.length,
1616
+ prevMessages: prev.map((m) => ({
1617
+ id: m.id,
1618
+ role: m.role,
1619
+ textLength: m.text.length,
1620
+ isFinal: m.isFinal
1621
+ }))
1622
+ });
1623
+ if (!messageToUpdate && !text7 && isFinal) {
1624
+ console.log(`\u{1F50D} Skipping empty final chunk (no existing message)`);
1625
+ return prev;
1626
+ }
1627
+ if (messageToUpdate) {
1628
+ const existsInState = prev.some((msg) => msg.id === messageToUpdate.id);
1629
+ const newUpdatedMessage = { ...messageToUpdate, text: messageToUpdate.text + text7, isFinal };
1630
+ updatedMessage = newUpdatedMessage;
1631
+ if (!existsInState) {
1632
+ console.log(`\u{1F50D} \u26A0\uFE0F Message ${messageToUpdate.id} not in state yet, adding it:`, {
1633
+ id: newUpdatedMessage.id,
1634
+ textLength: newUpdatedMessage.text.length,
1635
+ isFinal: newUpdatedMessage.isFinal
1636
+ });
1637
+ return [...prev, newUpdatedMessage];
1638
+ } else {
1639
+ console.log(`\u{1F50D} Updating existing ${role} message:`, {
1640
+ id: newUpdatedMessage.id,
1641
+ newTextLength: newUpdatedMessage.text.length,
1642
+ isFinal: newUpdatedMessage.isFinal
1643
+ });
1644
+ return prev.map((msg) => msg.id === messageToUpdate.id ? newUpdatedMessage : msg);
1645
+ }
1646
+ } else {
1647
+ const newMessage = {
1648
+ id: `${role}-${Date.now()}`,
1649
+ role,
1650
+ text: text7,
1651
+ isFinal,
1652
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
1653
+ };
1654
+ updatedMessage = newMessage;
1655
+ console.log(`\u{1F50D} Creating NEW ${role} message:`, {
1656
+ id: newMessage.id,
1657
+ textLength: newMessage.text.length,
1658
+ isFinal: newMessage.isFinal
1659
+ });
1660
+ const newArray = [...prev, newMessage];
1661
+ console.log(
1662
+ `\u{1F50D} New messages array:`,
1663
+ newArray.map((m) => ({
1664
+ id: m.id,
1665
+ role: m.role,
1666
+ textLength: m.text.length,
1667
+ isFinal: m.isFinal
1668
+ }))
1669
+ );
1670
+ return newArray;
1671
+ }
1672
+ });
1673
+ if (updatedMessage && !isFinal) {
1674
+ activeRef.current = updatedMessage;
1602
1675
  }
1603
1676
  if (isFinal) {
1604
- currentMessageRef.current = null;
1677
+ console.log(`\u{1F50D} Clearing ${role} ref (message finalized)`);
1678
+ activeRef.current = null;
1605
1679
  }
1606
1680
  }, []);
1607
1681
  const handleAIInterruption = useCallback2(() => {
@@ -1627,12 +1701,9 @@ var useCuekit = (options) => {
1627
1701
  const handleNavigationCommand = (event) => {
1628
1702
  console.log(`\u2B07\uFE0F Received event from backend: ${event.type}`, event);
1629
1703
  switch (event.type) {
1630
- case "speech_text": {
1631
- console.log("\u{1F5E3}\uFE0F AI Speech text:", event.data);
1632
- break;
1633
- }
1704
+ // raw_text is a redundant "reliability packet" - we already process ai_speech_chunk
1634
1705
  case "raw_text": {
1635
- console.log("\u{1F4DD} Raw text message:", event.data);
1706
+ console.log("\u{1F4DD} Raw text message (ignored, using ai_speech_chunk instead):", event.data);
1636
1707
  break;
1637
1708
  }
1638
1709
  case "user_speech_chunk":
@@ -7,6 +7,7 @@ import {
7
7
  getRoom,
8
8
  getRoomName,
9
9
  isConnected,
10
+ registerWebRTCCallbacks,
10
11
  sendData,
11
12
  sendRuntimeData,
12
13
  sendScreenStatus,
@@ -14,8 +15,9 @@ import {
14
15
  sendUserCommand,
15
16
  setAudioContainer,
16
17
  setServerUrl,
17
- setWebRTCCallbacks
18
- } from "./chunk-ZP7AXODE.mjs";
18
+ setWebRTCCallbacks,
19
+ unregisterWebRTCCallbacks
20
+ } from "./chunk-XVA5MZMJ.mjs";
19
21
  export {
20
22
  authenticate,
21
23
  connectToRoom,
@@ -25,6 +27,7 @@ export {
25
27
  getRoom,
26
28
  getRoomName,
27
29
  isConnected,
30
+ registerWebRTCCallbacks,
28
31
  sendData,
29
32
  sendRuntimeData,
30
33
  sendScreenStatus,
@@ -32,5 +35,6 @@ export {
32
35
  sendUserCommand,
33
36
  setAudioContainer,
34
37
  setServerUrl,
35
- setWebRTCCallbacks
38
+ setWebRTCCallbacks,
39
+ unregisterWebRTCCallbacks
36
40
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cuekit-ai/react",
3
- "version": "1.6.9",
3
+ "version": "1.6.10",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "exports": {