@cuekit-ai/react 1.6.8 → 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.
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;
@@ -24820,6 +24835,94 @@ function InitCuekit(config) {
24820
24835
  setWebRTCConfig(webRTCConfig);
24821
24836
  }
24822
24837
 
24838
+ // src/utils/colors.ts
24839
+ function hexToHSL(hex) {
24840
+ hex = hex.replace(/^#/, "");
24841
+ if (hex.length === 3) {
24842
+ hex = hex.split("").map((char) => char + char).join("");
24843
+ }
24844
+ if (hex.length !== 6) return null;
24845
+ const r3 = parseInt(hex.substring(0, 2), 16) / 255;
24846
+ const g = parseInt(hex.substring(2, 4), 16) / 255;
24847
+ const b2 = parseInt(hex.substring(4, 6), 16) / 255;
24848
+ const max = Math.max(r3, g, b2), min = Math.min(r3, g, b2);
24849
+ let h2 = 0, s = 0, l = (max + min) / 2;
24850
+ if (max !== min) {
24851
+ const d = max - min;
24852
+ s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
24853
+ switch (max) {
24854
+ case r3:
24855
+ h2 = (g - b2) / d + (g < b2 ? 6 : 0);
24856
+ break;
24857
+ case g:
24858
+ h2 = (b2 - r3) / d + 2;
24859
+ break;
24860
+ case b2:
24861
+ h2 = (r3 - g) / d + 4;
24862
+ break;
24863
+ }
24864
+ h2 /= 6;
24865
+ }
24866
+ const res = {
24867
+ h: Math.round(h2 * 360),
24868
+ s: Math.round(s * 100),
24869
+ l: Math.round(l * 100)
24870
+ };
24871
+ return {
24872
+ ...res,
24873
+ toString: () => `${res.h} ${res.s}% ${res.l}%`
24874
+ };
24875
+ }
24876
+
24877
+ // src/utils/theme.ts
24878
+ var generateThemeStyles = (theme, currentTheme = "dark") => {
24879
+ if (!theme) return {};
24880
+ const colors = currentTheme === "light" ? theme.light : theme.dark;
24881
+ if (!colors) return {};
24882
+ const mapping = {
24883
+ primary: "--voice-accent",
24884
+ statusBg: "--voice-status-bg",
24885
+ statusText: "--voice-status-text",
24886
+ background: "--voice-bg",
24887
+ surface: "--voice-surface",
24888
+ border: "--voice-border",
24889
+ text: "--voice-text",
24890
+ textMuted: "--voice-text-muted",
24891
+ userBubble: "--voice-user-bubble",
24892
+ userText: "--voice-user-text",
24893
+ aiBubble: "--voice-ai-bubble",
24894
+ aiText: "--voice-ai-text"
24895
+ };
24896
+ const styles = {};
24897
+ Object.entries(colors).forEach(([key, value]) => {
24898
+ const varName = mapping[key];
24899
+ if (varName && value) {
24900
+ const hsl = hexToHSL(value);
24901
+ if (hsl) {
24902
+ styles[varName] = hsl.toString();
24903
+ if (key === "primary") {
24904
+ const indicatorHsl = colors.indicator ? hexToHSL(colors.indicator) : hsl;
24905
+ styles["--indicator-bg"] = indicatorHsl ? indicatorHsl.toString() : hsl.toString();
24906
+ const vibrantHsl = colors.primaryVibrant ? hexToHSL(colors.primaryVibrant) : null;
24907
+ const lVibrant = vibrantHsl ? vibrantHsl.l : Math.min(hsl.l + 7, 100);
24908
+ styles["--voice-accent-vibrant"] = vibrantHsl ? vibrantHsl.toString() : `${hsl.h} ${hsl.s}% ${lVibrant}%`;
24909
+ const darkHsl = colors.primaryDark ? hexToHSL(colors.primaryDark) : null;
24910
+ const lDark = darkHsl ? darkHsl.l : Math.max(hsl.l - 10, 0);
24911
+ styles["--voice-accent-dark"] = darkHsl ? darkHsl.toString() : `${hsl.h} ${hsl.s}% ${lDark}%`;
24912
+ styles["--voice-accent-light"] = currentTheme === "light" ? `${hsl.h} ${hsl.s}% 96%` : `${hsl.h} ${hsl.s}% 25%`;
24913
+ const finalVibrant = vibrantHsl ? `hsl(${vibrantHsl.toString()})` : `hsl(${hsl.h} ${hsl.s}% ${lVibrant}%)`;
24914
+ const finalDark = darkHsl ? `hsl(${darkHsl.toString()})` : `hsl(${hsl.h} ${hsl.s}% ${lDark}%)`;
24915
+ styles["--gradient-primary"] = `linear-gradient(135deg, hsl(${hsl.toString()}), ${finalVibrant})`;
24916
+ styles["--gradient-mic-button"] = `radial-gradient(circle at 50% 0%, ${finalVibrant}, ${finalDark})`;
24917
+ const shadowColor = currentTheme === "light" ? `hsl(${hsl.toString()} / 0.1)` : `hsl(${hsl.toString()} / 0.2)`;
24918
+ styles["--shadow-soft"] = `0 4px 20px -2px ${shadowColor}`;
24919
+ }
24920
+ }
24921
+ }
24922
+ });
24923
+ return styles;
24924
+ };
24925
+
24823
24926
  // src/providers/ansyr-provider.tsx
24824
24927
  init_navigation();
24825
24928
  init_intent_store();
@@ -24865,45 +24968,6 @@ var initWebRTC = (apiKey) => {
24865
24968
  return initWebRTCWithDeployedBackend(apiKey);
24866
24969
  };
24867
24970
 
24868
- // src/utils/colors.ts
24869
- function hexToHSL(hex) {
24870
- hex = hex.replace(/^#/, "");
24871
- if (hex.length === 3) {
24872
- hex = hex.split("").map((char) => char + char).join("");
24873
- }
24874
- if (hex.length !== 6) return null;
24875
- const r3 = parseInt(hex.substring(0, 2), 16) / 255;
24876
- const g = parseInt(hex.substring(2, 4), 16) / 255;
24877
- const b2 = parseInt(hex.substring(4, 6), 16) / 255;
24878
- const max = Math.max(r3, g, b2), min = Math.min(r3, g, b2);
24879
- let h2 = 0, s = 0, l = (max + min) / 2;
24880
- if (max !== min) {
24881
- const d = max - min;
24882
- s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
24883
- switch (max) {
24884
- case r3:
24885
- h2 = (g - b2) / d + (g < b2 ? 6 : 0);
24886
- break;
24887
- case g:
24888
- h2 = (b2 - r3) / d + 2;
24889
- break;
24890
- case b2:
24891
- h2 = (r3 - g) / d + 4;
24892
- break;
24893
- }
24894
- h2 /= 6;
24895
- }
24896
- const res = {
24897
- h: Math.round(h2 * 360),
24898
- s: Math.round(s * 100),
24899
- l: Math.round(l * 100)
24900
- };
24901
- return {
24902
- ...res,
24903
- toString: () => `${res.h} ${res.s}% ${res.l}%`
24904
- };
24905
- }
24906
-
24907
24971
  // src/providers/ansyr-provider.tsx
24908
24972
  if (typeof window !== "undefined" && !globalThis.AnsyrStore) {
24909
24973
  globalThis.AnsyrStore = {
@@ -24958,47 +25022,7 @@ var AnsyrProvider = ({
24958
25022
  emptyStateMessage
24959
25023
  }) => {
24960
25024
  const [internalDeviceId, setInternalDeviceId] = (0, import_react.useState)(deviceId);
24961
- const generateThemeStyles2 = (theme2) => {
24962
- if (!theme2) return {};
24963
- const mapColorsToVars = (colors) => {
24964
- const mapping = {
24965
- primary: "--voice-accent",
24966
- background: "--voice-bg",
24967
- surface: "--voice-surface",
24968
- border: "--voice-border",
24969
- text: "--voice-text",
24970
- textMuted: "--voice-text-muted",
24971
- userBubble: "--voice-user-bubble",
24972
- userText: "--voice-user-text",
24973
- aiBubble: "--voice-ai-bubble",
24974
- aiText: "--voice-ai-text"
24975
- };
24976
- const styles2 = {};
24977
- Object.entries(colors).forEach(([key, value]) => {
24978
- const varName = mapping[key];
24979
- if (varName && value) {
24980
- const hsl = hexToHSL(value);
24981
- if (hsl) {
24982
- styles2[varName] = hsl.toString();
24983
- if (key === "primary") {
24984
- styles2["--indicator-bg"] = hsl.toString();
24985
- styles2["--voice-accent-light"] = `${hsl.h} ${hsl.s}% 96%`;
24986
- }
24987
- }
24988
- }
24989
- });
24990
- return styles2;
24991
- };
24992
- const styles = {};
24993
- if (theme2.light) {
24994
- const lightStyles = mapColorsToVars(theme2.light);
24995
- Object.entries(lightStyles).forEach(([key, value]) => {
24996
- styles[key] = value;
24997
- });
24998
- }
24999
- return styles;
25000
- };
25001
- const themeStyles = generateThemeStyles2(theme);
25025
+ const themeStyles = generateThemeStyles(theme, "dark");
25002
25026
  (0, import_react.useEffect)(() => {
25003
25027
  InitCuekit({ apiKey, appId });
25004
25028
  }, [apiKey, appId]);
@@ -25018,31 +25042,43 @@ var AnsyrProvider = ({
25018
25042
  };
25019
25043
  }, [navigationHandler2]);
25020
25044
  (0, import_react.useEffect)(() => {
25021
- Promise.resolve().then(() => (init_webrtc_service(), webrtc_service_exports)).then(({ setWebRTCCallbacks: setWebRTCCallbacks2 }) => {
25022
- setWebRTCCallbacks2({
25023
- onNavigationCommand: (command) => {
25024
- const data = command.data ?? command;
25025
- if (!data?.actionType) return;
25026
- if (data.actionType === "navigate" && data.routeName) {
25027
- if (navigationHandler2) {
25028
- navigationHandler2(data.routeName);
25029
- }
25030
- } else if (data.actionType === "click" && data.elementId) {
25031
- console.log("AI intent: Click element", data.elementId);
25032
- }
25033
- },
25034
- onConnectionStateChange: (state) => {
25035
- if (onConnectionStateChange) {
25036
- onConnectionStateChange(state);
25037
- }
25038
- },
25039
- onParticipantUpdate: (participants) => {
25040
- if (onParticipantUpdate) {
25041
- 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
+ }
25042
25069
  }
25043
- }
25044
- });
25045
- });
25070
+ };
25071
+ registerWebRTCCallbacks2(listeners2);
25072
+ cleanup = () => {
25073
+ unregisterWebRTCCallbacks2(listeners2);
25074
+ };
25075
+ }
25076
+ );
25077
+ return () => {
25078
+ if (cleanup) {
25079
+ cleanup();
25080
+ }
25081
+ };
25046
25082
  }, [onConnectionStateChange, onParticipantUpdate, navigationHandler2]);
25047
25083
  (0, import_react.useEffect)(() => {
25048
25084
  const updateGlobalStore = (id) => {
@@ -25133,6 +25169,7 @@ var useWebRTC = (options) => {
25133
25169
  const [participants, setParticipants] = (0, import_react2.useState)([]);
25134
25170
  const [room2, setRoom] = (0, import_react2.useState)(null);
25135
25171
  const [error, setError] = (0, import_react2.useState)(null);
25172
+ const listenersRef = (0, import_react2.useRef)(null);
25136
25173
  const audioContainerRef2 = (0, import_react2.useRef)(null);
25137
25174
  (0, import_react2.useEffect)(() => {
25138
25175
  setAudioContainer(audioContainerRef2);
@@ -25148,15 +25185,20 @@ var useWebRTC = (options) => {
25148
25185
  setParticipants(participants2);
25149
25186
  options?.onParticipantUpdate?.(participants2);
25150
25187
  };
25151
- setWebRTCCallbacks({
25188
+ const listeners2 = {
25152
25189
  onConnectionStateChange: handleConnectionStateChange,
25153
25190
  onParticipantUpdate: handleParticipantUpdate,
25154
25191
  onNavigationCommand: options?.onNavigationCommand,
25155
25192
  onAISpeechStart: options?.onAISpeechStart,
25156
25193
  onAISpeechEnd: options?.onAISpeechEnd
25157
- });
25194
+ };
25195
+ listenersRef.current = listeners2;
25196
+ registerWebRTCCallbacks(listeners2);
25158
25197
  return () => {
25159
- setWebRTCCallbacks({});
25198
+ if (listenersRef.current) {
25199
+ unregisterWebRTCCallbacks(listenersRef.current);
25200
+ listenersRef.current = null;
25201
+ }
25160
25202
  };
25161
25203
  }, [
25162
25204
  options?.onConnectionStateChange,
@@ -25218,30 +25260,85 @@ var useCuekit = (options) => {
25218
25260
  const currentUserMessageRef = (0, import_react3.useRef)(null);
25219
25261
  const currentAIMessageRef = (0, import_react3.useRef)(null);
25220
25262
  const handleMessageChunk = (0, import_react3.useCallback)((text7, role, isFinal) => {
25221
- const currentMessageRef = role === "user" ? currentUserMessageRef : currentAIMessageRef;
25222
- const messageToUpdate = currentMessageRef.current;
25223
- if (!messageToUpdate && !text7 && isFinal) {
25224
- return;
25225
- }
25226
- if (messageToUpdate) {
25227
- const updatedMessage = { ...messageToUpdate, text: messageToUpdate.text + text7, isFinal };
25228
- currentMessageRef.current = updatedMessage;
25229
- setMessages(
25230
- (prev) => prev.map((msg) => msg.id === messageToUpdate.id ? updatedMessage : msg)
25231
- );
25232
- } else {
25233
- const newMessage = {
25234
- 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:`, {
25235
25276
  role,
25236
- text: text7,
25237
- isFinal,
25238
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
25239
- };
25240
- currentMessageRef.current = newMessage;
25241
- 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;
25242
25338
  }
25243
25339
  if (isFinal) {
25244
- currentMessageRef.current = null;
25340
+ console.log(`\u{1F50D} Clearing ${role} ref (message finalized)`);
25341
+ activeRef.current = null;
25245
25342
  }
25246
25343
  }, []);
25247
25344
  const handleAIInterruption = (0, import_react3.useCallback)(() => {
@@ -25267,12 +25364,9 @@ var useCuekit = (options) => {
25267
25364
  const handleNavigationCommand = (event) => {
25268
25365
  console.log(`\u2B07\uFE0F Received event from backend: ${event.type}`, event);
25269
25366
  switch (event.type) {
25270
- case "speech_text": {
25271
- console.log("\u{1F5E3}\uFE0F AI Speech text:", event.data);
25272
- break;
25273
- }
25367
+ // raw_text is a redundant "reliability packet" - we already process ai_speech_chunk
25274
25368
  case "raw_text": {
25275
- 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);
25276
25370
  break;
25277
25371
  }
25278
25372
  case "user_speech_chunk":
@@ -38921,38 +39015,6 @@ var useDraggableResizableContainer = (storageKey) => {
38921
39015
  };
38922
39016
 
38923
39017
  // src/components/chat-popup.tsx
38924
- var generateThemeStyles = (theme, currentTheme = "dark") => {
38925
- if (!theme) return {};
38926
- const colors = currentTheme === "light" ? theme.light : theme.dark;
38927
- if (!colors) return {};
38928
- const mapping = {
38929
- primary: "--voice-accent",
38930
- background: "--voice-bg",
38931
- surface: "--voice-surface",
38932
- border: "--voice-border",
38933
- text: "--voice-text",
38934
- textMuted: "--voice-text-muted",
38935
- userBubble: "--voice-user-bubble",
38936
- userText: "--voice-user-text",
38937
- aiBubble: "--voice-ai-bubble",
38938
- aiText: "--voice-ai-text"
38939
- };
38940
- const styles = {};
38941
- Object.entries(colors).forEach(([key, value]) => {
38942
- const varName = mapping[key];
38943
- if (varName && value) {
38944
- const hsl = hexToHSL(value);
38945
- if (hsl) {
38946
- styles[varName] = hsl.toString();
38947
- if (key === "primary") {
38948
- styles["--indicator-bg"] = hsl.toString();
38949
- styles["--voice-accent-light"] = currentTheme === "light" ? `${hsl.h} ${hsl.s}% 96%` : `${hsl.h} ${hsl.s}% 25%`;
38950
- }
38951
- }
38952
- }
38953
- });
38954
- return styles;
38955
- };
38956
39018
  var ChatPopup = ({
38957
39019
  isOpen,
38958
39020
  isMinimized,
@@ -39143,8 +39205,8 @@ var ChatPopup = ({
39143
39205
  "div",
39144
39206
  {
39145
39207
  style: {
39146
- background: "hsl(var(--voice-accent-light))",
39147
- color: "hsl(var(--voice-accent))",
39208
+ background: "hsl(var(--voice-status-bg))",
39209
+ color: "hsl(var(--voice-status-text))",
39148
39210
  minHeight: "16px",
39149
39211
  display: "flex",
39150
39212
  alignItems: "center",
@@ -39588,8 +39650,8 @@ var ChatPopup = ({
39588
39650
  style: {
39589
39651
  padding: "10px 12px",
39590
39652
  borderRadius: 8,
39591
- border: `1px solid ${currentTheme === "dark" ? "#8177ed" : "#6257d9"}`,
39592
- background: currentTheme === "dark" ? "#8177ed" : "#6257d9",
39653
+ border: "1px solid hsl(var(--voice-accent))",
39654
+ background: "hsl(var(--voice-accent))",
39593
39655
  color: "#fff",
39594
39656
  fontSize: 12,
39595
39657
  fontWeight: 700,
@@ -39612,8 +39674,8 @@ var ChatPopup = ({
39612
39674
  style: {
39613
39675
  padding: "10px 12px",
39614
39676
  borderRadius: 8,
39615
- border: `1px solid ${currentTheme === "dark" ? "#8177ed" : "#6257d9"}`,
39616
- background: currentTheme === "dark" ? "#8177ed" : "#6257d9",
39677
+ border: "1px solid hsl(var(--voice-accent))",
39678
+ background: "hsl(var(--voice-accent))",
39617
39679
  color: "#fff",
39618
39680
  fontSize: 12,
39619
39681
  fontWeight: 700,
@@ -39651,17 +39713,14 @@ var BorderGlow = ({ isActive }) => {
39651
39713
  right: 0,
39652
39714
  bottom: 0,
39653
39715
  width: "4px",
39654
- background: "linear-gradient(to bottom, #A39EEC, #8177ed, #6257d9)",
39716
+ background: "linear-gradient(to bottom, hsl(var(--voice-accent-light)), hsl(var(--voice-accent)), hsl(var(--voice-accent-light)))",
39655
39717
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
39656
39718
  opacity: 0.8
39657
39719
  },
39658
39720
  rightBorder2: {
39659
39721
  position: "absolute",
39660
39722
  top: 0,
39661
- right: 0,
39662
- bottom: 0,
39663
- width: "8px",
39664
- background: "linear-gradient(to bottom, #c0bbfb, #8177ed, #8177ed)",
39723
+ background: "linear-gradient(to bottom, hsl(var(--voice-accent-light)), hsl(var(--voice-accent)), hsl(var(--voice-accent)))",
39665
39724
  filter: "blur(4px)",
39666
39725
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
39667
39726
  opacity: 0.6
@@ -39670,9 +39729,7 @@ var BorderGlow = ({ isActive }) => {
39670
39729
  position: "absolute",
39671
39730
  top: 0,
39672
39731
  right: 0,
39673
- bottom: 0,
39674
- width: "16px",
39675
- background: "linear-gradient(to bottom, #dad7fe, #8177ed, #9a92f6)",
39732
+ background: "linear-gradient(to bottom, hsl(var(--voice-accent-light)), hsl(var(--voice-accent)), hsl(var(--voice-accent-light)))",
39676
39733
  filter: "blur(12px)",
39677
39734
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
39678
39735
  opacity: 0.4
@@ -39683,7 +39740,7 @@ var BorderGlow = ({ isActive }) => {
39683
39740
  left: 0,
39684
39741
  bottom: 0,
39685
39742
  width: "4px",
39686
- background: "linear-gradient(to bottom, #A39EEC, #8177ed, #6257d9)",
39743
+ background: "linear-gradient(to bottom, hsl(var(--voice-accent-light)), hsl(var(--voice-accent)), hsl(var(--voice-accent-light)))",
39687
39744
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
39688
39745
  opacity: 0.8
39689
39746
  },
@@ -39692,8 +39749,7 @@ var BorderGlow = ({ isActive }) => {
39692
39749
  top: 0,
39693
39750
  left: 0,
39694
39751
  bottom: 0,
39695
- width: "8px",
39696
- background: "linear-gradient(to bottom, #c0bbfb, #8177ed, #8177ed)",
39752
+ background: "linear-gradient(to bottom, hsl(var(--voice-accent-light)), hsl(var(--voice-accent)), hsl(var(--voice-accent)))",
39697
39753
  filter: "blur(4px)",
39698
39754
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
39699
39755
  opacity: 0.6
@@ -39703,8 +39759,7 @@ var BorderGlow = ({ isActive }) => {
39703
39759
  top: 0,
39704
39760
  left: 0,
39705
39761
  bottom: 0,
39706
- width: "16px",
39707
- background: "linear-gradient(to bottom, #dad7fe, #8177ed, #9a92f6)",
39762
+ background: "linear-gradient(to bottom, hsl(var(--voice-accent-light)), hsl(var(--voice-accent)), hsl(var(--voice-accent-light)))",
39708
39763
  filter: "blur(12px)",
39709
39764
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
39710
39765
  opacity: 0.4
@@ -39715,7 +39770,7 @@ var BorderGlow = ({ isActive }) => {
39715
39770
  left: 0,
39716
39771
  width: "32px",
39717
39772
  height: "32px",
39718
- background: "#9a92f6",
39773
+ background: "hsl(var(--voice-accent))",
39719
39774
  borderBottomRightRadius: "50%",
39720
39775
  filter: "blur(16px)",
39721
39776
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
@@ -39727,7 +39782,7 @@ var BorderGlow = ({ isActive }) => {
39727
39782
  right: 0,
39728
39783
  width: "32px",
39729
39784
  height: "32px",
39730
- background: "#A39EEC",
39785
+ background: "hsl(var(--voice-accent))",
39731
39786
  borderBottomLeftRadius: "50%",
39732
39787
  filter: "blur(16px)",
39733
39788
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
@@ -39739,7 +39794,7 @@ var BorderGlow = ({ isActive }) => {
39739
39794
  right: 0,
39740
39795
  width: "32px",
39741
39796
  height: "32px",
39742
- background: "#8177ed",
39797
+ background: "hsl(var(--voice-accent))",
39743
39798
  borderTopLeftRadius: "50%",
39744
39799
  filter: "blur(16px)",
39745
39800
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
@@ -39751,7 +39806,7 @@ var BorderGlow = ({ isActive }) => {
39751
39806
  left: 0,
39752
39807
  width: "32px",
39753
39808
  height: "32px",
39754
- background: "#6257d9",
39809
+ background: "hsl(var(--voice-accent))",
39755
39810
  borderTopRightRadius: "50%",
39756
39811
  filter: "blur(16px)",
39757
39812
  animation: "borderPulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite",
@@ -42150,47 +42205,7 @@ var MicButton = ({
42150
42205
  }
42151
42206
  };
42152
42207
  const generateMicButtonGradient = () => {
42153
- const colors = currentTheme === "light" ? theme?.light : theme?.dark;
42154
- if (colors?.primary) {
42155
- const hsl = hexToHSL(colors.primary);
42156
- if (hsl) {
42157
- const lightHsl = `${hsl.h} ${hsl.s}% ${Math.min(hsl.l + 10, 100)}%`;
42158
- return `radial-gradient(circle at 50% 0%, hsl(${lightHsl}), hsl(${hsl.toString()}))`;
42159
- }
42160
- }
42161
- return currentTheme === "dark" ? "radial-gradient(circle at 50% 0%, #9a92f6, #5447cd)" : "radial-gradient(circle at 50% 0%, #9a92f6, #6257d9)";
42162
- };
42163
- const generateThemeStyles2 = (theme2, currentTheme2 = "dark") => {
42164
- if (!theme2) return {};
42165
- const colors = currentTheme2 === "light" ? theme2.light : theme2.dark;
42166
- if (!colors) return {};
42167
- const mapping = {
42168
- primary: "--voice-accent",
42169
- background: "--voice-bg",
42170
- surface: "--voice-surface",
42171
- border: "--voice-border",
42172
- text: "--voice-text",
42173
- textMuted: "--voice-text-muted",
42174
- userBubble: "--voice-user-bubble",
42175
- userText: "--voice-user-text",
42176
- aiBubble: "--voice-ai-bubble",
42177
- aiText: "--voice-ai-text"
42178
- };
42179
- const styles = {};
42180
- Object.entries(colors).forEach(([key, value]) => {
42181
- const varName = mapping[key];
42182
- if (varName && value) {
42183
- const hsl = hexToHSL(value);
42184
- if (hsl) {
42185
- styles[varName] = hsl.toString();
42186
- if (key === "primary") {
42187
- styles["--indicator-bg"] = hsl.toString();
42188
- styles["--voice-accent-light"] = currentTheme2 === "light" ? `${hsl.h} ${hsl.s}% 96%` : `${hsl.h} ${hsl.s}% 25%`;
42189
- }
42190
- }
42191
- }
42192
- });
42193
- return styles;
42208
+ return "radial-gradient(circle at 50% 0%, hsl(var(--voice-accent-vibrant)), hsl(var(--voice-accent-dark)))";
42194
42209
  };
42195
42210
  const micButtonGradient = generateMicButtonGradient();
42196
42211
  const buttonStyles = {
@@ -42227,18 +42242,18 @@ var MicButton = ({
42227
42242
  switch (micState) {
42228
42243
  case "listening":
42229
42244
  baseStyle.transform = "scale(1.05)";
42230
- baseStyle.boxShadow = "0 25px 50px -12px rgba(129, 119, 237, 0.6)";
42245
+ baseStyle.boxShadow = "0 25px 50px -12px hsla(var(--voice-accent) / 0.6)";
42231
42246
  break;
42232
42247
  case "thinking":
42233
42248
  baseStyle.transform = "scale(1.02)";
42234
- baseStyle.boxShadow = "0 20px 25px -5px rgba(154, 146, 246, 0.4)";
42249
+ baseStyle.boxShadow = "0 20px 25px -5px hsla(var(--voice-accent) / 0.4)";
42235
42250
  break;
42236
42251
  case "replying":
42237
42252
  baseStyle.transform = "scale(1.02)";
42238
- baseStyle.boxShadow = "0 20px 25px -5px rgba(129, 119, 237, 0.4)";
42253
+ baseStyle.boxShadow = "0 20px 25px -5px hsla(var(--voice-accent) / 0.4)";
42239
42254
  break;
42240
42255
  default:
42241
- baseStyle.boxShadow = "0 10px 15px -3px rgba(129, 119, 237, 0.3)";
42256
+ baseStyle.boxShadow = "0 10px 15px -3px hsla(var(--voice-accent) / 0.3)";
42242
42257
  break;
42243
42258
  }
42244
42259
  return baseStyle;
@@ -42251,7 +42266,7 @@ var MicButton = ({
42251
42266
  style: {
42252
42267
  ...buttonStyles.container,
42253
42268
  ...getPositionStyle(),
42254
- ...generateThemeStyles2(theme, currentTheme)
42269
+ ...generateThemeStyles(theme, currentTheme)
42255
42270
  }
42256
42271
  },
42257
42272
  showLanguageSelector && showLanguageDropdown ? /* @__PURE__ */ import_react20.default.createElement("div", { className: "voice-chat-language-selection" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "voice-chat-language-header" }, /* @__PURE__ */ import_react20.default.createElement("div", { className: "voice-chat-language-icon" }, /* @__PURE__ */ import_react20.default.createElement(mic_default, { width: 20, height: 20 })), /* @__PURE__ */ import_react20.default.createElement("span", { className: "voice-chat-language-title" }, "Select Language")), /* @__PURE__ */ import_react20.default.createElement(
@@ -42269,9 +42284,9 @@ var MicButton = ({
42269
42284
  className: "voice-chat-cancel-button",
42270
42285
  onClick: () => setShowLanguageSelector(false),
42271
42286
  style: {
42272
- background: currentTheme === "dark" ? "hsl(248 32% 17.5%)" : "#f6f5ff",
42273
- color: currentTheme === "dark" ? "hsl(252 20% 65%)" : "#6257d9",
42274
- border: `1px solid ${currentTheme === "dark" ? "hsl(248 32% 22%)" : "#c0bbfb"}`
42287
+ background: currentTheme === "dark" ? "hsl(var(--voice-surface))" : "hsl(var(--voice-accent-light))",
42288
+ color: currentTheme === "dark" ? "hsl(var(--voice-text-muted))" : "hsl(var(--voice-accent))",
42289
+ border: "1px solid hsl(var(--voice-border))"
42275
42290
  }
42276
42291
  },
42277
42292
  "Cancel"
@@ -42281,9 +42296,9 @@ var MicButton = ({
42281
42296
  className: "voice-chat-confirm-button",
42282
42297
  onClick: handleLanguageConfirm,
42283
42298
  style: {
42284
- background: currentTheme === "dark" ? "#8177ed" : "#6257d9",
42299
+ background: "hsl(var(--voice-accent))",
42285
42300
  color: "#fff",
42286
- border: `1px solid ${currentTheme === "dark" ? "#8177ed" : "#6257d9"}`
42301
+ border: "1px solid hsl(var(--voice-accent))"
42287
42302
  }
42288
42303
  },
42289
42304
  "Start Voice Chat"
@@ -42308,13 +42323,13 @@ var MicButton = ({
42308
42323
  onMouseEnter: (e3) => {
42309
42324
  if (micState === "idle") {
42310
42325
  e3.currentTarget.style.transform = "scale(1.05)";
42311
- e3.currentTarget.style.boxShadow = "0 20px 25px -5px rgba(129, 119, 237, 0.4)";
42326
+ e3.currentTarget.style.boxShadow = "0 20px 25px -5px hsla(var(--voice-accent) / 0.4)";
42312
42327
  }
42313
42328
  },
42314
42329
  onMouseLeave: (e3) => {
42315
42330
  if (micState === "idle") {
42316
42331
  e3.currentTarget.style.transform = "scale(1)";
42317
- e3.currentTarget.style.boxShadow = "0 10px 15px -3px rgba(129, 119, 237, 0.3)";
42332
+ e3.currentTarget.style.boxShadow = "0 10px 15px -3px hsla(var(--voice-accent) / 0.3)";
42318
42333
  }
42319
42334
  },
42320
42335
  "aria-label": micState === "thinking" ? "Processing..." : micState === "replying" ? "AI is responding..." : isListening ? "Stop listening" : "Start listening"