@eshal-bot/chat-widget 0.1.2 → 0.1.4

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.
@@ -644,19 +644,145 @@
644
644
  return null;
645
645
  };
646
646
 
647
+ /**
648
+ * Initializes the PCM audio player worklet that streams inline PCM chunks
649
+ * coming from the ADK websocket events.
650
+ */
651
+ async function startAudioPlayerWorklet() {
652
+ if (typeof window === 'undefined') {
653
+ throw new Error('Audio player can only be initialized in the browser');
654
+ }
655
+ const audioContext = new AudioContext({
656
+ sampleRate: 24000
657
+ });
658
+
659
+ // For webpack/vite, we need to use a different approach
660
+ // We'll inline the worklet code or use a blob URL
661
+ const workletCode = "\n class PCMPlayerProcessor extends AudioWorkletProcessor {\n constructor() {\n super();\n this.bufferSize = 24000 * 180;\n this.buffer = new Float32Array(this.bufferSize);\n this.writeIndex = 0;\n this.readIndex = 0;\n this.port.onmessage = (event) => {\n if (event.data?.command === 'endOfAudio') {\n this.readIndex = this.writeIndex;\n return;\n }\n const int16Samples = new Int16Array(event.data);\n this.enqueue(int16Samples);\n };\n }\n\n enqueue(samples) {\n for (let i = 0; i < samples.length; i += 1) {\n const floatVal = samples[i] / 32768;\n this.buffer[this.writeIndex] = floatVal;\n this.writeIndex = (this.writeIndex + 1) % this.bufferSize;\n if (this.writeIndex === this.readIndex) {\n this.readIndex = (this.readIndex + 1) % this.bufferSize;\n }\n }\n }\n\n process(inputs, outputs) {\n const output = outputs[0];\n const framesPerBlock = output[0].length;\n for (let frame = 0; frame < framesPerBlock; frame += 1) {\n output[0][frame] = this.buffer[this.readIndex];\n if (output.length > 1) {\n output[1][frame] = this.buffer[this.readIndex];\n }\n if (this.readIndex !== this.writeIndex) {\n this.readIndex = (this.readIndex + 1) % this.bufferSize;\n }\n }\n return true;\n }\n }\n\n registerProcessor('pcm-player-processor', PCMPlayerProcessor);\n ";
662
+ const blob = new Blob([workletCode], {
663
+ type: 'application/javascript'
664
+ });
665
+ const workletUrl = URL.createObjectURL(blob);
666
+ await audioContext.audioWorklet.addModule(workletUrl);
667
+ const audioPlayerNode = new AudioWorkletNode(audioContext, 'pcm-player-processor');
668
+ audioPlayerNode.connect(audioContext.destination);
669
+ URL.revokeObjectURL(workletUrl);
670
+ return [audioPlayerNode, audioContext];
671
+ }
672
+
673
+ /**
674
+ * Bootstraps the PCM recorder audio worklet, captures mic audio at 16kHz
675
+ * mono, and forwards buffers through the provided handler.
676
+ */
677
+ async function startAudioRecorderWorklet(onPcmChunk) {
678
+ if (typeof window === 'undefined') {
679
+ throw new Error('Audio recorder can only run in the browser');
680
+ }
681
+ const audioRecorderContext = new AudioContext({
682
+ sampleRate: 16000
683
+ });
684
+ const workletCode = "\n class PCMRecorderProcessor extends AudioWorkletProcessor {\n process(inputs) {\n if (!inputs.length || !inputs[0].length) {\n return true;\n }\n const inputChannel = inputs[0][0];\n this.port.postMessage(new Float32Array(inputChannel));\n return true;\n }\n }\n\n registerProcessor('pcm-recorder-processor', PCMRecorderProcessor);\n ";
685
+ const blob = new Blob([workletCode], {
686
+ type: 'application/javascript'
687
+ });
688
+ const workletUrl = URL.createObjectURL(blob);
689
+ await audioRecorderContext.audioWorklet.addModule(workletUrl);
690
+ const micStream = await navigator.mediaDevices.getUserMedia({
691
+ audio: {
692
+ channelCount: 1
693
+ }
694
+ });
695
+ const source = audioRecorderContext.createMediaStreamSource(micStream);
696
+ const audioRecorderNode = new AudioWorkletNode(audioRecorderContext, 'pcm-recorder-processor');
697
+ audioRecorderNode.port.onmessage = event => {
698
+ const pcmData = convertFloat32ToPCM(event.data);
699
+ onPcmChunk(pcmData);
700
+ };
701
+ source.connect(audioRecorderNode);
702
+ URL.revokeObjectURL(workletUrl);
703
+ return [audioRecorderNode, audioRecorderContext, micStream];
704
+ }
705
+
706
+ /**
707
+ * Stops all microphone tracks.
708
+ */
709
+ function stopMicrophone(stream) {
710
+ if (!stream) {
711
+ return;
712
+ }
713
+ stream.getTracks().forEach(track => track.stop());
714
+ }
715
+ const INT16_MAX = 0x7fff;
716
+ function convertFloat32ToPCM(float32Array) {
717
+ const pcm16Array = new Int16Array(float32Array.length);
718
+ for (let i = 0; i < float32Array.length; i += 1) {
719
+ pcm16Array[i] = Math.max(-1, Math.min(1, float32Array[i])) * INT16_MAX;
720
+ }
721
+ return pcm16Array.buffer;
722
+ }
723
+
724
+ var _process$env;
725
+
726
+ // BIDI WebSocket base URL - can be overridden via config
727
+ const BIDI_WS_BASE = typeof process !== 'undefined' && (_process$env = process.env) !== null && _process$env !== void 0 && _process$env.NEXT_PUBLIC_BIDI_WS_BASE ? process.env.NEXT_PUBLIC_BIDI_WS_BASE : 'wss://dev.eshal.ai/knowledge-api/ws';
728
+
729
+ /**
730
+ * Clean CJK (Chinese, Japanese, Korean) spaces
731
+ */
732
+ const cleanCJKSpaces = value => {
733
+ return value.replace(/([\u3000-\u303f\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uff00-\uffef])\s+([\u3000-\u303f\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uff00-\uffef])/g, '$1$2');
734
+ };
735
+
736
+ /**
737
+ * Convert base64 to ArrayBuffer
738
+ */
739
+ const base64ToArrayBuffer = base64 => {
740
+ if (typeof window === 'undefined') {
741
+ return new ArrayBuffer(0);
742
+ }
743
+ let normalized = base64.replace(/-/g, '+').replace(/_/g, '/');
744
+ while (normalized.length % 4 !== 0) {
745
+ normalized += '=';
746
+ }
747
+ const binary = window.atob(normalized);
748
+ const len = binary.length;
749
+ const bytes = new Uint8Array(len);
750
+ for (let i = 0; i < len; i += 1) {
751
+ bytes[i] = binary.charCodeAt(i);
752
+ }
753
+ return bytes.buffer;
754
+ };
755
+
756
+ /**
757
+ * Sanitize WebSocket base URL
758
+ */
759
+ const sanitizeWsBase = value => value.replace(/\/$/, '');
760
+
761
+ /**
762
+ * CN utility for conditional class names
763
+ */
764
+ const cn = function () {
765
+ for (var _len = arguments.length, classes = new Array(_len), _key = 0; _key < _len; _key++) {
766
+ classes[_key] = arguments[_key];
767
+ }
768
+ return classes.filter(Boolean).join(' ');
769
+ };
770
+
647
771
  const createMessage = _ref => {
648
772
  let {
649
773
  id,
650
774
  role,
651
775
  content,
652
- isProcessing = false
776
+ isProcessing = false,
777
+ metadata
653
778
  } = _ref;
654
779
  return {
655
780
  id,
656
781
  role,
657
782
  content,
658
783
  timestamp: new Date(),
659
- isProcessing
784
+ isProcessing,
785
+ metadata
660
786
  };
661
787
  };
662
788
  const useChatState = _ref2 => {
@@ -668,7 +794,8 @@
668
794
  organizationId,
669
795
  autoOpen,
670
796
  openDelay,
671
- darkMode
797
+ darkMode,
798
+ enableVoiceInteraction = false
672
799
  } = _ref2;
673
800
  const [isOpen, setIsOpen] = reactExports.useState(autoOpen);
674
801
  const [isMinimized, setIsMinimized] = reactExports.useState(false);
@@ -689,6 +816,31 @@
689
816
  const [isLoading, setIsLoading] = reactExports.useState(false);
690
817
  const widgetRef = reactExports.useRef(null);
691
818
  const messageIdRef = reactExports.useRef(1);
819
+
820
+ // Bidi/Voice state
821
+ const [isVoiceSessionActive, setIsVoiceSessionActive] = reactExports.useState(false);
822
+ const [bidiMessages, setBidiMessages] = reactExports.useState([]);
823
+ const [voiceStatus, setVoiceStatus] = reactExports.useState("idle"); // 'idle' | 'connecting' | 'connected' | 'error'
824
+ const [voiceError, setVoiceError] = reactExports.useState(null);
825
+ const [bidiSessionId] = reactExports.useState(() => "widget-session-".concat(Math.random().toString(36).slice(2, 9)));
826
+
827
+ // Bidi refs
828
+ const websocketRef = reactExports.useRef(null);
829
+ const audioPlayerNodeRef = reactExports.useRef(null);
830
+ const audioPlayerContextRef = reactExports.useRef(null);
831
+ const audioRecorderNodeRef = reactExports.useRef(null);
832
+ const audioRecorderContextRef = reactExports.useRef(null);
833
+ const micStreamRef = reactExports.useRef(null);
834
+ const currentInputMessageIdRef = reactExports.useRef(null);
835
+ const currentAssistantMessageIdRef = reactExports.useRef(null);
836
+ const isAudioReadyRef = reactExports.useRef(false);
837
+ const isVoiceSessionActiveRef = reactExports.useRef(false);
838
+ reactExports.useRef(null);
839
+ const websocketUrl = reactExports.useMemo(() => {
840
+ if (!organizationId) return null;
841
+ const normalizedBase = sanitizeWsBase(BIDI_WS_BASE);
842
+ return "".concat(normalizedBase, "/").concat(organizationId, "/").concat(bidiSessionId);
843
+ }, [organizationId, bidiSessionId]);
692
844
  const getNextMessageId = reactExports.useCallback(() => {
693
845
  const id = "msg-".concat(messageIdRef.current);
694
846
  messageIdRef.current += 1;
@@ -840,17 +992,386 @@
840
992
  const handleQuickQuestion = question => {
841
993
  void sendMessage(question);
842
994
  };
995
+
996
+ /**
997
+ * Handle user decision for HITL (Human-in-the-Loop)
998
+ */
999
+ const handleDecision = reactExports.useCallback(async (decisionId, optionId, optionValue) => {
1000
+ // Remove the decision message from the list
1001
+ setMessages(prev => prev.filter(msg => {
1002
+ var _msg$decisionData;
1003
+ return ((_msg$decisionData = msg.decisionData) === null || _msg$decisionData === void 0 ? void 0 : _msg$decisionData.decisionId) !== decisionId;
1004
+ }));
1005
+
1006
+ // Handle the decision based on the selected option
1007
+ if (optionValue === "connect_agent") {
1008
+ // User wants to connect with a human agent
1009
+ const responseMessage = createMessage({
1010
+ id: getNextMessageId(),
1011
+ role: "assistant",
1012
+ content: "Great! I'm setting up a connection with a live customer service agent. Please hold on for a moment while I transfer you."
1013
+ });
1014
+ setMessages(prev => [...prev, responseMessage]);
1015
+ } else if (optionValue === "decline") {
1016
+ // User declined to connect with a human agent
1017
+ const responseMessage = createMessage({
1018
+ id: getNextMessageId(),
1019
+ role: "assistant",
1020
+ content: "I understand. I apologize that I don't have specific information about your query in my knowledge base. Is there anything else I can help you with?"
1021
+ });
1022
+ setMessages(prev => [...prev, responseMessage]);
1023
+ }
1024
+ }, [getNextMessageId]);
1025
+
1026
+ // Bidi/Voice functions
1027
+ const finalizePendingInputMessage = reactExports.useCallback(() => {
1028
+ if (!currentInputMessageIdRef.current) {
1029
+ return;
1030
+ }
1031
+ setBidiMessages(prev => prev.map(message => message.id === currentInputMessageIdRef.current ? _objectSpread2(_objectSpread2({}, message), {}, {
1032
+ isProcessing: false
1033
+ }) : message));
1034
+ currentInputMessageIdRef.current = null;
1035
+ }, []);
1036
+ const appendUserTranscription = reactExports.useCallback(function (text) {
1037
+ let finished = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1038
+ if (!text) {
1039
+ return;
1040
+ }
1041
+ const chunk = cleanCJKSpaces(text);
1042
+ setBidiMessages(prev => {
1043
+ if (!currentInputMessageIdRef.current) {
1044
+ const newId = "bidi-input-".concat(Date.now());
1045
+ currentInputMessageIdRef.current = newId;
1046
+ const userMessage = createMessage({
1047
+ id: newId,
1048
+ role: "user",
1049
+ content: chunk,
1050
+ isProcessing: !finished,
1051
+ metadata: {
1052
+ source: "bidi",
1053
+ transcriptionType: "input"
1054
+ }
1055
+ });
1056
+ return [...prev, userMessage];
1057
+ }
1058
+ return prev.map(message => {
1059
+ if (message.id !== currentInputMessageIdRef.current) {
1060
+ return message;
1061
+ }
1062
+ const combined = cleanCJKSpaces("".concat(message.content || "").concat(chunk));
1063
+ return _objectSpread2(_objectSpread2({}, message), {}, {
1064
+ content: combined,
1065
+ timestamp: new Date(),
1066
+ isProcessing: !finished,
1067
+ metadata: _objectSpread2(_objectSpread2({}, message.metadata), {}, {
1068
+ transcriptionType: "input"
1069
+ })
1070
+ });
1071
+ });
1072
+ });
1073
+ if (finished) {
1074
+ currentInputMessageIdRef.current = null;
1075
+ }
1076
+ }, []);
1077
+ const appendAssistantContent = reactExports.useCallback(function (text) {
1078
+ let finished = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
1079
+ if (!text) {
1080
+ return;
1081
+ }
1082
+ finalizePendingInputMessage();
1083
+ setBidiMessages(prev => {
1084
+ if (!currentAssistantMessageIdRef.current) {
1085
+ const newId = "bidi-assistant-".concat(Date.now());
1086
+ currentAssistantMessageIdRef.current = newId;
1087
+ const assistantMessage = createMessage({
1088
+ id: newId,
1089
+ role: "assistant",
1090
+ content: text,
1091
+ isProcessing: !finished,
1092
+ metadata: {
1093
+ source: "bidi"
1094
+ }
1095
+ });
1096
+ return [...prev, assistantMessage];
1097
+ }
1098
+ return prev.map(message => {
1099
+ if (message.id !== currentAssistantMessageIdRef.current) {
1100
+ return message;
1101
+ }
1102
+ return _objectSpread2(_objectSpread2({}, message), {}, {
1103
+ content: "".concat(message.content || "").concat(text),
1104
+ timestamp: new Date(),
1105
+ isProcessing: !finished
1106
+ });
1107
+ });
1108
+ });
1109
+ if (finished) {
1110
+ currentAssistantMessageIdRef.current = null;
1111
+ }
1112
+ }, [finalizePendingInputMessage]);
1113
+ const handleContentParts = reactExports.useCallback(parts => {
1114
+ parts.forEach(part => {
1115
+ var _part$inlineData, _part$inlineData$mime;
1116
+ if ((_part$inlineData = part.inlineData) !== null && _part$inlineData !== void 0 && _part$inlineData.data && (_part$inlineData$mime = part.inlineData.mimeType) !== null && _part$inlineData$mime !== void 0 && _part$inlineData$mime.startsWith("audio/pcm")) {
1117
+ if (audioPlayerNodeRef.current) {
1118
+ audioPlayerNodeRef.current.port.postMessage(base64ToArrayBuffer(part.inlineData.data));
1119
+ }
1120
+ }
1121
+ if (part.text) {
1122
+ appendAssistantContent(part.text, false);
1123
+ }
1124
+ });
1125
+ }, [appendAssistantContent]);
1126
+ const handleTurnComplete = reactExports.useCallback(() => {
1127
+ currentAssistantMessageIdRef.current = null;
1128
+ currentInputMessageIdRef.current = null;
1129
+ setBidiMessages(prev => prev.map(message => {
1130
+ var _message$metadata;
1131
+ return ((_message$metadata = message.metadata) === null || _message$metadata === void 0 ? void 0 : _message$metadata.source) === "bidi" && message.isProcessing ? _objectSpread2(_objectSpread2({}, message), {}, {
1132
+ isProcessing: false
1133
+ }) : message;
1134
+ }));
1135
+ }, []);
1136
+ const handleInterrupted = reactExports.useCallback(() => {
1137
+ if (audioPlayerNodeRef.current) {
1138
+ audioPlayerNodeRef.current.port.postMessage({
1139
+ command: "endOfAudio"
1140
+ });
1141
+ }
1142
+ currentAssistantMessageIdRef.current = null;
1143
+ currentInputMessageIdRef.current = null;
1144
+ setBidiMessages(prev => prev.map(message => {
1145
+ var _message$metadata2;
1146
+ return ((_message$metadata2 = message.metadata) === null || _message$metadata2 === void 0 ? void 0 : _message$metadata2.source) === "bidi" ? _objectSpread2(_objectSpread2({}, message), {}, {
1147
+ isProcessing: false,
1148
+ metadata: _objectSpread2(_objectSpread2({}, message.metadata), {}, {
1149
+ interrupted: true
1150
+ })
1151
+ }) : message;
1152
+ }));
1153
+ }, []);
1154
+ const handleBidiEvent = reactExports.useCallback(event => {
1155
+ var _event$inputTranscrip, _event$outputTranscri, _event$content;
1156
+ if (!event) {
1157
+ return;
1158
+ }
1159
+ if (event.turnComplete) {
1160
+ handleTurnComplete();
1161
+ return;
1162
+ }
1163
+ if (event.interrupted) {
1164
+ handleInterrupted();
1165
+ return;
1166
+ }
1167
+ if ((_event$inputTranscrip = event.inputTranscription) !== null && _event$inputTranscrip !== void 0 && _event$inputTranscrip.text) {
1168
+ appendUserTranscription(event.inputTranscription.text, Boolean(event.inputTranscription.finished));
1169
+ }
1170
+ if ((_event$outputTranscri = event.outputTranscription) !== null && _event$outputTranscri !== void 0 && _event$outputTranscri.text) {
1171
+ appendAssistantContent(event.outputTranscription.text, Boolean(event.outputTranscription.finished));
1172
+ }
1173
+ if ((_event$content = event.content) !== null && _event$content !== void 0 && _event$content.parts) {
1174
+ handleContentParts(event.content.parts);
1175
+ }
1176
+ }, [appendAssistantContent, appendUserTranscription, handleContentParts, handleInterrupted, handleTurnComplete]);
1177
+ const handleRecorderAudio = reactExports.useCallback(pcmData => {
1178
+ const ws = websocketRef.current;
1179
+ if (!isVoiceSessionActiveRef.current || !ws || ws.readyState !== WebSocket.OPEN) {
1180
+ return;
1181
+ }
1182
+ ws.send(pcmData);
1183
+ }, []);
1184
+ const initializeAudioPipeline = reactExports.useCallback(async () => {
1185
+ if (isAudioReadyRef.current || typeof window === "undefined") {
1186
+ return;
1187
+ }
1188
+ try {
1189
+ const [playerNode, playerContext] = await startAudioPlayerWorklet();
1190
+ audioPlayerNodeRef.current = playerNode;
1191
+ audioPlayerContextRef.current = playerContext;
1192
+ await playerContext.resume();
1193
+ const [recorderNode, recorderContext, micStream] = await startAudioRecorderWorklet(handleRecorderAudio);
1194
+ audioRecorderNodeRef.current = recorderNode;
1195
+ audioRecorderContextRef.current = recorderContext;
1196
+ micStreamRef.current = micStream;
1197
+ await recorderContext.resume();
1198
+ isAudioReadyRef.current = true;
1199
+ } catch (error) {
1200
+ console.error("Failed to initialize audio pipeline", error);
1201
+ throw error;
1202
+ }
1203
+ }, [handleRecorderAudio]);
1204
+ const connectWebsocket = reactExports.useCallback(() => {
1205
+ if (typeof window === "undefined" || !websocketUrl) {
1206
+ return;
1207
+ }
1208
+ if (websocketRef.current && websocketRef.current.readyState === WebSocket.OPEN) {
1209
+ return;
1210
+ }
1211
+ setVoiceStatus("connecting");
1212
+ setVoiceError(null);
1213
+ try {
1214
+ const ws = new WebSocket(websocketUrl);
1215
+ websocketRef.current = ws;
1216
+ ws.onopen = () => {
1217
+ setVoiceStatus("connected");
1218
+ };
1219
+ ws.onmessage = event => {
1220
+ try {
1221
+ const payload = JSON.parse(event.data);
1222
+ handleBidiEvent(payload);
1223
+ } catch (error) {
1224
+ console.error("Failed to parse ADK event", error);
1225
+ }
1226
+ };
1227
+ ws.onerror = () => {
1228
+ setVoiceStatus("error");
1229
+ setVoiceError("Voice session connection error");
1230
+ };
1231
+ ws.onclose = () => {
1232
+ websocketRef.current = null;
1233
+ setVoiceStatus("idle");
1234
+ if (isVoiceSessionActiveRef.current) {
1235
+ setTimeout(() => {
1236
+ connectWebsocket();
1237
+ }, 5000);
1238
+ }
1239
+ };
1240
+ } catch (error) {
1241
+ console.error("Failed to open voice websocket", error);
1242
+ setVoiceStatus("error");
1243
+ setVoiceError(error instanceof Error ? error.message : "Unable to start voice session");
1244
+ }
1245
+ }, [handleBidiEvent, websocketUrl]);
1246
+ const startVoiceSession = reactExports.useCallback(async () => {
1247
+ if (!enableVoiceInteraction || !organizationId) {
1248
+ return;
1249
+ }
1250
+ setVoiceError(null);
1251
+ setVoiceStatus(prev => prev === "connected" ? prev : "connecting");
1252
+ setIsVoiceSessionActive(true);
1253
+ isVoiceSessionActiveRef.current = true;
1254
+ setBidiMessages(prev => {
1255
+ if (prev.length > 0) {
1256
+ return prev;
1257
+ }
1258
+ if (!welcomeMessage || welcomeMessage.trim() === "") {
1259
+ return [];
1260
+ }
1261
+ const welcomeMsg = createMessage({
1262
+ id: "bidi-welcome-".concat(Date.now()),
1263
+ role: "assistant",
1264
+ content: welcomeMessage,
1265
+ metadata: {
1266
+ source: "bidi"
1267
+ }
1268
+ });
1269
+ return [welcomeMsg];
1270
+ });
1271
+ connectWebsocket();
1272
+ try {
1273
+ await initializeAudioPipeline();
1274
+ } catch (error) {
1275
+ console.error("Failed to start audio pipeline", error);
1276
+ setVoiceError(error instanceof Error ? error.message : "Unable to access microphone");
1277
+ setVoiceStatus("error");
1278
+ }
1279
+ }, [enableVoiceInteraction, organizationId, welcomeMessage, connectWebsocket, initializeAudioPipeline]);
1280
+ const stopVoiceSession = reactExports.useCallback(() => {
1281
+ var _audioPlayerNodeRef$c, _audioPlayerContextRe, _audioRecorderNodeRef, _audioRecorderContext;
1282
+ setIsVoiceSessionActive(false);
1283
+ isVoiceSessionActiveRef.current = false;
1284
+ setVoiceStatus("idle");
1285
+ setVoiceError(null);
1286
+ if (websocketRef.current && websocketRef.current.readyState === WebSocket.OPEN) {
1287
+ websocketRef.current.close();
1288
+ }
1289
+ websocketRef.current = null;
1290
+ (_audioPlayerNodeRef$c = audioPlayerNodeRef.current) === null || _audioPlayerNodeRef$c === void 0 || _audioPlayerNodeRef$c.disconnect();
1291
+ (_audioPlayerContextRe = audioPlayerContextRef.current) === null || _audioPlayerContextRe === void 0 || _audioPlayerContextRe.close();
1292
+ audioPlayerNodeRef.current = null;
1293
+ audioPlayerContextRef.current = null;
1294
+ (_audioRecorderNodeRef = audioRecorderNodeRef.current) === null || _audioRecorderNodeRef === void 0 || _audioRecorderNodeRef.disconnect();
1295
+ (_audioRecorderContext = audioRecorderContextRef.current) === null || _audioRecorderContext === void 0 || _audioRecorderContext.close();
1296
+ audioRecorderNodeRef.current = null;
1297
+ audioRecorderContextRef.current = null;
1298
+ stopMicrophone(micStreamRef.current);
1299
+ micStreamRef.current = null;
1300
+ isAudioReadyRef.current = false;
1301
+ }, []);
1302
+ const handleVoiceToggle = reactExports.useCallback(() => {
1303
+ if (isVoiceSessionActive) {
1304
+ stopVoiceSession();
1305
+ return;
1306
+ }
1307
+ if (voiceStatus === "connecting") {
1308
+ return;
1309
+ }
1310
+ void startVoiceSession();
1311
+ }, [isVoiceSessionActive, startVoiceSession, stopVoiceSession, voiceStatus]);
1312
+ const sendBidiTextMessage = reactExports.useCallback(text => {
1313
+ const trimmed = text.trim();
1314
+ if (!trimmed) {
1315
+ return;
1316
+ }
1317
+ const ws = websocketRef.current;
1318
+ if (!ws || ws.readyState !== WebSocket.OPEN) {
1319
+ setVoiceError("Voice session is not connected yet");
1320
+ return;
1321
+ }
1322
+ ws.send(JSON.stringify({
1323
+ type: "text",
1324
+ text: trimmed
1325
+ }));
1326
+ const userMessage = createMessage({
1327
+ id: "bidi-user-".concat(Date.now()),
1328
+ role: "user",
1329
+ content: trimmed,
1330
+ metadata: {
1331
+ source: "bidi"
1332
+ }
1333
+ });
1334
+ setBidiMessages(prev => [...prev, userMessage]);
1335
+ setInputValue("");
1336
+ }, []);
1337
+
1338
+ // Cleanup on unmount
1339
+ reactExports.useEffect(() => {
1340
+ isVoiceSessionActiveRef.current = isVoiceSessionActive;
1341
+ }, [isVoiceSessionActive]);
1342
+ reactExports.useEffect(() => {
1343
+ return () => {
1344
+ var _audioPlayerNodeRef$c2, _audioPlayerContextRe2, _audioRecorderNodeRef2, _audioRecorderContext2;
1345
+ if (websocketRef.current && websocketRef.current.readyState === WebSocket.OPEN) {
1346
+ websocketRef.current.close();
1347
+ }
1348
+ (_audioPlayerNodeRef$c2 = audioPlayerNodeRef.current) === null || _audioPlayerNodeRef$c2 === void 0 || _audioPlayerNodeRef$c2.disconnect();
1349
+ (_audioPlayerContextRe2 = audioPlayerContextRef.current) === null || _audioPlayerContextRe2 === void 0 || _audioPlayerContextRe2.close();
1350
+ (_audioRecorderNodeRef2 = audioRecorderNodeRef.current) === null || _audioRecorderNodeRef2 === void 0 || _audioRecorderNodeRef2.disconnect();
1351
+ (_audioRecorderContext2 = audioRecorderContextRef.current) === null || _audioRecorderContext2 === void 0 || _audioRecorderContext2.close();
1352
+ stopMicrophone(micStreamRef.current);
1353
+ micStreamRef.current = null;
1354
+ isAudioReadyRef.current = false;
1355
+ };
1356
+ }, []);
843
1357
  const memoizedQuickQuestions = reactExports.useMemo(() => quickQuestions, [quickQuestions]);
1358
+
1359
+ // Determine which messages to use
1360
+ const activeMessages = isVoiceSessionActive ? bidiMessages : messages;
844
1361
  return {
845
1362
  // State
846
1363
  isOpen,
847
1364
  isMinimized,
848
1365
  isDark,
849
- messages,
1366
+ messages: activeMessages,
850
1367
  inputValue,
851
1368
  isLoading,
852
1369
  widgetRef,
853
1370
  quickQuestions: memoizedQuickQuestions,
1371
+ // Bidi/Voice state
1372
+ isVoiceSessionActive,
1373
+ voiceStatus,
1374
+ voiceError,
854
1375
  // Actions
855
1376
  setInputValue,
856
1377
  handleSend,
@@ -859,7 +1380,11 @@
859
1380
  toggleChat,
860
1381
  toggleTheme,
861
1382
  toggleMinimize,
862
- closeChat
1383
+ closeChat,
1384
+ handleDecision,
1385
+ // Bidi/Voice actions
1386
+ handleVoiceToggle,
1387
+ sendBidiTextMessage
863
1388
  };
864
1389
  };
865
1390
 
@@ -923,13 +1448,13 @@
923
1448
  */
924
1449
 
925
1450
 
926
- const Bot = createLucideIcon("Bot", [
927
- ["path", { d: "M12 8V4H8", key: "hb8ula" }],
928
- ["rect", { width: "16", height: "12", x: "4", y: "8", rx: "2", key: "enze0r" }],
929
- ["path", { d: "M2 14h2", key: "vft8re" }],
930
- ["path", { d: "M20 14h2", key: "4cs60a" }],
931
- ["path", { d: "M15 13v2", key: "1xurst" }],
932
- ["path", { d: "M9 13v2", key: "rq6x2g" }]
1451
+ const AudioLines = createLucideIcon("AudioLines", [
1452
+ ["path", { d: "M2 10v3", key: "1fnikh" }],
1453
+ ["path", { d: "M6 6v11", key: "11sgs0" }],
1454
+ ["path", { d: "M10 3v18", key: "yhl04a" }],
1455
+ ["path", { d: "M14 8v7", key: "3a1oy3" }],
1456
+ ["path", { d: "M18 5v13", key: "123xd1" }],
1457
+ ["path", { d: "M22 10v3", key: "154ddg" }]
933
1458
  ]);
934
1459
 
935
1460
  /**
@@ -940,11 +1465,8 @@
940
1465
  */
941
1466
 
942
1467
 
943
- const Maximize2 = createLucideIcon("Maximize2", [
944
- ["polyline", { points: "15 3 21 3 21 9", key: "mznyad" }],
945
- ["polyline", { points: "9 21 3 21 3 15", key: "1avn1i" }],
946
- ["line", { x1: "21", x2: "14", y1: "3", y2: "10", key: "ota7mn" }],
947
- ["line", { x1: "3", x2: "10", y1: "21", y2: "14", key: "1atl0r" }]
1468
+ const Loader2 = createLucideIcon("Loader2", [
1469
+ ["path", { d: "M21 12a9 9 0 1 1-6.219-8.56", key: "13zald" }]
948
1470
  ]);
949
1471
 
950
1472
  /**
@@ -955,10 +1477,11 @@
955
1477
  */
956
1478
 
957
1479
 
958
- const MessageCircleQuestion = createLucideIcon("MessageCircleQuestion", [
959
- ["path", { d: "M7.9 20A9 9 0 1 0 4 16.1L2 22Z", key: "vv11sd" }],
960
- ["path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3", key: "1u773s" }],
961
- ["path", { d: "M12 17h.01", key: "p32p05" }]
1480
+ const Maximize2 = createLucideIcon("Maximize2", [
1481
+ ["polyline", { points: "15 3 21 3 21 9", key: "mznyad" }],
1482
+ ["polyline", { points: "9 21 3 21 3 15", key: "1avn1i" }],
1483
+ ["line", { x1: "21", x2: "14", y1: "3", y2: "10", key: "ota7mn" }],
1484
+ ["line", { x1: "3", x2: "10", y1: "21", y2: "14", key: "1atl0r" }]
962
1485
  ]);
963
1486
 
964
1487
  /**
@@ -1059,322 +1582,6 @@
1059
1582
  ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
1060
1583
  ]);
1061
1584
 
1062
- var jsxRuntime = {exports: {}};
1063
-
1064
- var reactJsxRuntime_production_min = {};
1065
-
1066
- /**
1067
- * @license React
1068
- * react-jsx-runtime.production.min.js
1069
- *
1070
- * Copyright (c) Facebook, Inc. and its affiliates.
1071
- *
1072
- * This source code is licensed under the MIT license found in the
1073
- * LICENSE file in the root directory of this source tree.
1074
- */
1075
- var f=reactExports,k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};
1076
- function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return {$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}reactJsxRuntime_production_min.Fragment=l;reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q;
1077
-
1078
- {
1079
- jsxRuntime.exports = reactJsxRuntime_production_min;
1080
- }
1081
-
1082
- var jsxRuntimeExports = jsxRuntime.exports;
1083
-
1084
- const LOGO_MAX_HEIGHT = "3.5rem";
1085
- const LOGO_MAX_HEIGHT_COMPACT = "2.5rem";
1086
- const LOGO_MAX_WIDTH_DEFAULT = "4.5rem";
1087
- const LOGO_MAX_WIDTH_DEFAULT_COMPACT = "3rem";
1088
- const LOGO_MAX_WIDTH_WIDE = "9rem";
1089
- const LOGO_MAX_WIDTH_WIDE_COMPACT = "5.5rem";
1090
- const STATUS_DOT_SIZE = "0.8rem";
1091
- const STATUS_DOT_SIZE_COMPACT = "0.65rem";
1092
- const STATUS_DOT_BORDER_WIDTH = "2px";
1093
- const STATUS_DOT_BORDER_WIDTH_COMPACT = "1.5px";
1094
- const STATUS_DOT_DEFAULT_COLOR = "#34d399";
1095
- const STATUS_DOT_OFFSET = "38%";
1096
- const STATUS_DOT_OFFSET_COMPACT = "22%";
1097
- const LogoImage = _ref => {
1098
- let {
1099
- src,
1100
- showStatusDot = false,
1101
- statusColor = STATUS_DOT_DEFAULT_COLOR,
1102
- size = "default"
1103
- } = _ref;
1104
- const [aspect, setAspect] = reactExports.useState("square");
1105
- const isCompact = size === "compact";
1106
- reactExports.useEffect(() => {
1107
- if (typeof window === "undefined" || !src) {
1108
- return;
1109
- }
1110
- const img = new Image();
1111
- img.src = src;
1112
- img.onload = () => {
1113
- if (img.width > img.height) {
1114
- setAspect("wide");
1115
- } else if (img.height > img.width) {
1116
- setAspect("tall");
1117
- } else {
1118
- setAspect("square");
1119
- }
1120
- };
1121
- }, [src]);
1122
- const containerStyle = {
1123
- position: "relative",
1124
- display: "inline-flex"
1125
- };
1126
- const imageStyle = {
1127
- display: "block",
1128
- objectFit: "contain",
1129
- height: "auto",
1130
- width: "auto",
1131
- maxHeight: isCompact ? LOGO_MAX_HEIGHT_COMPACT : LOGO_MAX_HEIGHT,
1132
- maxWidth: isCompact ? LOGO_MAX_WIDTH_DEFAULT_COMPACT : LOGO_MAX_WIDTH_DEFAULT
1133
- };
1134
- if (aspect === "wide") {
1135
- imageStyle.maxWidth = isCompact ? LOGO_MAX_WIDTH_WIDE_COMPACT : LOGO_MAX_WIDTH_WIDE;
1136
- }
1137
- if (aspect === "tall") {
1138
- imageStyle.maxHeight = isCompact ? "3rem" : "4rem";
1139
- imageStyle.maxWidth = isCompact ? "3rem" : "4rem";
1140
- }
1141
- return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
1142
- style: containerStyle,
1143
- children: [/*#__PURE__*/jsxRuntimeExports.jsx("img", {
1144
- src: src,
1145
- alt: "Logo",
1146
- style: imageStyle,
1147
- loading: "lazy"
1148
- }), showStatusDot && /*#__PURE__*/jsxRuntimeExports.jsx("span", {
1149
- style: {
1150
- position: "absolute",
1151
- bottom: 0,
1152
- right: 0,
1153
- width: isCompact ? STATUS_DOT_SIZE_COMPACT : STATUS_DOT_SIZE,
1154
- height: isCompact ? STATUS_DOT_SIZE_COMPACT : STATUS_DOT_SIZE,
1155
- borderRadius: "9999px",
1156
- backgroundColor: statusColor,
1157
- border: "".concat(isCompact ? STATUS_DOT_BORDER_WIDTH_COMPACT : STATUS_DOT_BORDER_WIDTH, " solid #ffffff"),
1158
- transform: "translate(".concat(isCompact ? STATUS_DOT_OFFSET_COMPACT : STATUS_DOT_OFFSET, ", ").concat(isCompact ? STATUS_DOT_OFFSET_COMPACT : STATUS_DOT_OFFSET, ")"),
1159
- pointerEvents: "none"
1160
- }
1161
- })]
1162
- });
1163
- };
1164
-
1165
- const IS_BROWSER = typeof window !== "undefined";
1166
- const SYSTEM_FONT_FAMILIES = new Set(["Arial", "Helvetica", "Times New Roman", "Georgia", "Courier New", "Verdana", "Trebuchet MS", "Palatino", "Garamond", "Tahoma", "Lucida Sans", "Lucida Grande", "Lucida Console", "Gill Sans", "Impact", "Comic Sans MS", "system-ui"].map(font => font.toLowerCase()));
1167
- const GENERIC_FONT_KEYWORDS = ["sans-serif", "serif", "monospace", "cursive", "fantasy"];
1168
- const GOOGLE_FONT_WEIGHTS = "300,400,500,600,700";
1169
- const loadedFonts = new Set();
1170
- let webFontLoaderPromise = null;
1171
- const shouldSkipLoading = fontFamily => {
1172
- const normalized = fontFamily.trim().toLowerCase();
1173
- if (!normalized) return true;
1174
- if (SYSTEM_FONT_FAMILIES.has(normalized)) return true;
1175
- return GENERIC_FONT_KEYWORDS.some(keyword => normalized.includes(keyword));
1176
- };
1177
- const toGoogleFontQuery = fontFamily => fontFamily.trim().replace(/\s+/g, "+");
1178
- function useGoogleFont(fontFamily) {
1179
- reactExports.useEffect(() => {
1180
- var _fontFamily$split$;
1181
- if (!IS_BROWSER || !fontFamily) {
1182
- return;
1183
- }
1184
- const baseFont = (_fontFamily$split$ = fontFamily.split(",")[0]) === null || _fontFamily$split$ === void 0 ? void 0 : _fontFamily$split$.trim();
1185
- if (!baseFont || loadedFonts.has(baseFont)) {
1186
- return;
1187
- }
1188
- if (shouldSkipLoading(baseFont)) {
1189
- loadedFonts.add(baseFont);
1190
- return;
1191
- }
1192
- let isMounted = true;
1193
- const loadFont = async () => {
1194
- try {
1195
- if (!webFontLoaderPromise) {
1196
- webFontLoaderPromise = Promise.resolve().then(function () { return webfontloader; });
1197
- }
1198
- const webFontModule = await webFontLoaderPromise;
1199
- const WebFont = webFontModule && "default" in webFontModule ? webFontModule.default : webFontModule;
1200
- const googleQuery = toGoogleFontQuery(baseFont);
1201
- WebFont.load({
1202
- google: {
1203
- families: ["".concat(googleQuery, ":").concat(GOOGLE_FONT_WEIGHTS)]
1204
- },
1205
- fontactive: family => {
1206
- if (isMounted) {
1207
- loadedFonts.add(family);
1208
- }
1209
- },
1210
- fontinactive: family => {
1211
- if (isMounted) {
1212
- loadedFonts.add(family);
1213
- }
1214
- console.warn("Warning: failed to load Google Font: ".concat(family));
1215
- }
1216
- });
1217
- } catch (error) {
1218
- console.error("Warning: WebFont loader error:", error);
1219
- }
1220
- };
1221
- void loadFont();
1222
- return () => {
1223
- isMounted = false;
1224
- };
1225
- }, [fontFamily]);
1226
- }
1227
-
1228
- const lightenColor = function (color) {
1229
- let amount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.12;
1230
- if (!color) {
1231
- return color;
1232
- }
1233
- const hexMatch = color.trim().match(/^#([0-9a-f]{3,8})$/i);
1234
- if (!hexMatch) {
1235
- return color;
1236
- }
1237
- let hex = hexMatch[1];
1238
- if (hex.length === 3) {
1239
- hex = hex.split("").map(char => char + char).join("");
1240
- }
1241
- if (hex.length === 8) {
1242
- hex = hex.slice(0, 6);
1243
- }
1244
- if (hex.length !== 6) {
1245
- return color;
1246
- }
1247
- const num = parseInt(hex, 16);
1248
- const r = num >> 16 & 255;
1249
- const g = num >> 8 & 255;
1250
- const b = num & 255;
1251
- const mix = channel => {
1252
- const mixed = Math.round(channel + (255 - channel) * amount);
1253
- return Math.min(255, Math.max(0, mixed));
1254
- };
1255
- const [lr, lg, lb] = [mix(r), mix(g), mix(b)];
1256
- const toHex = channel => channel.toString(16).padStart(2, "0");
1257
- return "#".concat(toHex(lr)).concat(toHex(lg)).concat(toHex(lb));
1258
- };
1259
- const ChatHeader = _ref => {
1260
- let {
1261
- companyName,
1262
- companyLogo,
1263
- primaryColor,
1264
- isDark,
1265
- isMinimized = false,
1266
- textColor,
1267
- fontFamily,
1268
- fontSize,
1269
- onToggleTheme,
1270
- onMinimize,
1271
- onClose
1272
- } = _ref;
1273
- useGoogleFont(fontFamily);
1274
- return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
1275
- className: "text-white ".concat(isMinimized ? "px-4 py-2.5" : "px-5 py-4", " flex items-center justify-between relative overflow-hidden rounded-t-2xl"),
1276
- style: {
1277
- background: "linear-gradient(135deg, ".concat(primaryColor, " 0%, ").concat(lightenColor(primaryColor, 0.2), " 100%)"),
1278
- fontFamily
1279
- },
1280
- children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
1281
- className: "absolute inset-0 pointer-events-none transition-opacity duration-200 ".concat(isMinimized ? "opacity-0" : "opacity-100", " bg-gradient-to-br from-white/10 to-transparent")
1282
- }), /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
1283
- className: "flex items-center ".concat(isMinimized ? "gap-1.5" : "gap-3", " relative z-10 min-w-0"),
1284
- children: [companyLogo ? /*#__PURE__*/jsxRuntimeExports.jsx(LogoImage, {
1285
- src: companyLogo,
1286
- showStatusDot: true,
1287
- statusColor: "#34d399",
1288
- size: isMinimized ? "compact" : "default"
1289
- }) : /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
1290
- className: "relative",
1291
- children: [/*#__PURE__*/jsxRuntimeExports.jsx(MessageCircle, {
1292
- className: "transition-all ".concat(isMinimized ? "w-8 h-8" : "w-10 h-10")
1293
- }), /*#__PURE__*/jsxRuntimeExports.jsx("span", {
1294
- className: "absolute rounded-full",
1295
- style: {
1296
- bottom: 0,
1297
- right: 0,
1298
- width: isMinimized ? "0.65rem" : "0.8rem",
1299
- height: isMinimized ? "0.65rem" : "0.8rem",
1300
- backgroundColor: "#34d399",
1301
- border: isMinimized ? "1.5px solid #ffffff" : "2px solid #ffffff",
1302
- transform: "translate(".concat(isMinimized ? "22%" : "38%", ", ").concat(isMinimized ? "22%" : "38%", ")"),
1303
- pointerEvents: "none"
1304
- }
1305
- })]
1306
- }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
1307
- className: "flex items-center gap-2 min-w-0",
1308
- children: /*#__PURE__*/jsxRuntimeExports.jsx("h3", {
1309
- className: "font-semibold leading-tight truncate",
1310
- style: {
1311
- fontSize: fontSize ? "calc(".concat(fontSize, " + ").concat(isMinimized ? "6px" : "10px", ")") : isMinimized ? "13px" : "15px",
1312
- color: textColor || "#FFFFFF"
1313
- },
1314
- children: companyName
1315
- })
1316
- })]
1317
- }), /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
1318
- className: "flex items-center ".concat(isMinimized ? "gap-3" : "gap-4", " relative z-10 text-white"),
1319
- children: [/*#__PURE__*/jsxRuntimeExports.jsx("span", {
1320
- role: "button",
1321
- tabIndex: 0,
1322
- "aria-label": "Toggle theme",
1323
- onClick: onToggleTheme,
1324
- onKeyDown: event => {
1325
- if (event.key === "Enter" || event.key === " ") {
1326
- event.preventDefault();
1327
- onToggleTheme === null || onToggleTheme === void 0 || onToggleTheme(event);
1328
- }
1329
- },
1330
- className: "inline-flex h-8 w-8 items-center justify-center cursor-pointer transition-opacity duration-200 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 rounded-full",
1331
- children: isDark ? /*#__PURE__*/jsxRuntimeExports.jsx(Sun, {
1332
- className: "w-4 h-4",
1333
- strokeWidth: 1.6
1334
- }) : /*#__PURE__*/jsxRuntimeExports.jsx(Moon, {
1335
- className: "w-4 h-4",
1336
- strokeWidth: 1.6
1337
- })
1338
- }), onMinimize && /*#__PURE__*/jsxRuntimeExports.jsx("span", {
1339
- role: "button",
1340
- tabIndex: 0,
1341
- "aria-label": isMinimized ? "Expand" : "Minimize",
1342
- onClick: onMinimize,
1343
- onKeyDown: event => {
1344
- if (event.key === "Enter" || event.key === " ") {
1345
- event.preventDefault();
1346
- onMinimize === null || onMinimize === void 0 || onMinimize(event);
1347
- }
1348
- },
1349
- className: "inline-flex h-8 w-8 items-center justify-center cursor-pointer transition-opacity duration-200 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 rounded-full",
1350
- children: isMinimized ? /*#__PURE__*/jsxRuntimeExports.jsx(Maximize2, {
1351
- className: "w-4 h-4",
1352
- strokeWidth: 1.6
1353
- }) : /*#__PURE__*/jsxRuntimeExports.jsx(Minimize2, {
1354
- className: "w-4 h-4",
1355
- strokeWidth: 1.6
1356
- })
1357
- }), /*#__PURE__*/jsxRuntimeExports.jsx("span", {
1358
- role: "button",
1359
- tabIndex: 0,
1360
- "aria-label": "Close",
1361
- onClick: onClose,
1362
- onKeyDown: event => {
1363
- if (event.key === "Enter" || event.key === " ") {
1364
- event.preventDefault();
1365
- onClose === null || onClose === void 0 || onClose(event);
1366
- }
1367
- },
1368
- className: "inline-flex h-8 w-8 items-center justify-center cursor-pointer transition-opacity duration-200 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 rounded-full",
1369
- children: /*#__PURE__*/jsxRuntimeExports.jsx(X, {
1370
- className: "w-4 h-4",
1371
- strokeWidth: 1.6
1372
- })
1373
- })]
1374
- })]
1375
- });
1376
- };
1377
-
1378
1585
  function ok$2() {}
1379
1586
 
1380
1587
  function unreachable() {}
@@ -4507,6 +4714,28 @@
4507
4714
  ]
4508
4715
  };
4509
4716
 
4717
+ var jsxRuntime = {exports: {}};
4718
+
4719
+ var reactJsxRuntime_production_min = {};
4720
+
4721
+ /**
4722
+ * @license React
4723
+ * react-jsx-runtime.production.min.js
4724
+ *
4725
+ * Copyright (c) Facebook, Inc. and its affiliates.
4726
+ *
4727
+ * This source code is licensed under the MIT license found in the
4728
+ * LICENSE file in the root directory of this source tree.
4729
+ */
4730
+ var f=reactExports,k=Symbol.for("react.element"),l=Symbol.for("react.fragment"),m=Object.prototype.hasOwnProperty,n=f.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,p={key:!0,ref:!0,__self:!0,__source:!0};
4731
+ function q(c,a,g){var b,d={},e=null,h=null;void 0!==g&&(e=""+g);void 0!==a.key&&(e=""+a.key);void 0!==a.ref&&(h=a.ref);for(b in a)m.call(a,b)&&!p.hasOwnProperty(b)&&(d[b]=a[b]);if(c&&c.defaultProps)for(b in a=c.defaultProps,a)void 0===d[b]&&(d[b]=a[b]);return {$$typeof:k,type:c,key:e,ref:h,props:d,_owner:n.current}}reactJsxRuntime_production_min.Fragment=l;reactJsxRuntime_production_min.jsx=q;reactJsxRuntime_production_min.jsxs=q;
4732
+
4733
+ {
4734
+ jsxRuntime.exports = reactJsxRuntime_production_min;
4735
+ }
4736
+
4737
+ var jsxRuntimeExports = jsxRuntime.exports;
4738
+
4510
4739
  /**
4511
4740
  * @typedef {import('mdast').Nodes} Nodes
4512
4741
  *
@@ -55073,24 +55302,91 @@
55073
55302
  }
55074
55303
  };
55075
55304
 
55305
+ const IS_BROWSER = typeof window !== "undefined";
55306
+ const SYSTEM_FONT_FAMILIES = new Set(["Arial", "Helvetica", "Times New Roman", "Georgia", "Courier New", "Verdana", "Trebuchet MS", "Palatino", "Garamond", "Tahoma", "Lucida Sans", "Lucida Grande", "Lucida Console", "Gill Sans", "Impact", "Comic Sans MS", "system-ui"].map(font => font.toLowerCase()));
55307
+ const GENERIC_FONT_KEYWORDS = ["sans-serif", "serif", "monospace", "cursive", "fantasy"];
55308
+ const GOOGLE_FONT_WEIGHTS = "300,400,500,600,700";
55309
+ const loadedFonts = new Set();
55310
+ let webFontLoaderPromise = null;
55311
+ const shouldSkipLoading = fontFamily => {
55312
+ const normalized = fontFamily.trim().toLowerCase();
55313
+ if (!normalized) return true;
55314
+ if (SYSTEM_FONT_FAMILIES.has(normalized)) return true;
55315
+ return GENERIC_FONT_KEYWORDS.some(keyword => normalized.includes(keyword));
55316
+ };
55317
+ const toGoogleFontQuery = fontFamily => fontFamily.trim().replace(/\s+/g, "+");
55318
+ function useGoogleFont(fontFamily) {
55319
+ reactExports.useEffect(() => {
55320
+ var _fontFamily$split$;
55321
+ if (!IS_BROWSER || !fontFamily) {
55322
+ return;
55323
+ }
55324
+ const baseFont = (_fontFamily$split$ = fontFamily.split(",")[0]) === null || _fontFamily$split$ === void 0 ? void 0 : _fontFamily$split$.trim();
55325
+ if (!baseFont || loadedFonts.has(baseFont)) {
55326
+ return;
55327
+ }
55328
+ if (shouldSkipLoading(baseFont)) {
55329
+ loadedFonts.add(baseFont);
55330
+ return;
55331
+ }
55332
+ let isMounted = true;
55333
+ const loadFont = async () => {
55334
+ try {
55335
+ if (!webFontLoaderPromise) {
55336
+ webFontLoaderPromise = Promise.resolve().then(function () { return webfontloader; });
55337
+ }
55338
+ const webFontModule = await webFontLoaderPromise;
55339
+ const WebFont = webFontModule && "default" in webFontModule ? webFontModule.default : webFontModule;
55340
+ const googleQuery = toGoogleFontQuery(baseFont);
55341
+ WebFont.load({
55342
+ google: {
55343
+ families: ["".concat(googleQuery, ":").concat(GOOGLE_FONT_WEIGHTS)]
55344
+ },
55345
+ fontactive: family => {
55346
+ if (isMounted) {
55347
+ loadedFonts.add(family);
55348
+ }
55349
+ },
55350
+ fontinactive: family => {
55351
+ if (isMounted) {
55352
+ loadedFonts.add(family);
55353
+ }
55354
+ console.warn("Warning: failed to load Google Font: ".concat(family));
55355
+ }
55356
+ });
55357
+ } catch (error) {
55358
+ console.error("Warning: WebFont loader error:", error);
55359
+ }
55360
+ };
55361
+ void loadFont();
55362
+ return () => {
55363
+ isMounted = false;
55364
+ };
55365
+ }, [fontFamily]);
55366
+ }
55367
+
55076
55368
  const _excluded = ["node", "children"],
55077
55369
  _excluded2 = ["node", "children"],
55078
55370
  _excluded3 = ["node", "children"],
55079
55371
  _excluded4 = ["node", "children"],
55080
- _excluded5 = ["className", "children"],
55372
+ _excluded5 = ["node", "children"],
55081
55373
  _excluded6 = ["node", "children"],
55082
55374
  _excluded7 = ["node", "children"],
55083
55375
  _excluded8 = ["node", "children"],
55084
55376
  _excluded9 = ["node", "children"],
55085
55377
  _excluded0 = ["node", "children"],
55086
55378
  _excluded1 = ["node", "children"],
55087
- _excluded10 = ["node", "children"];
55379
+ _excluded10 = ["node", "children"],
55380
+ _excluded11 = ["className", "children"];
55088
55381
  const StreamingMarkdown = _ref => {
55089
55382
  let {
55090
55383
  content,
55091
55384
  isStreaming,
55092
55385
  isDark = false,
55093
- isRtl = false
55386
+ isRtl = false,
55387
+ textColor,
55388
+ fontFamily,
55389
+ fontSize
55094
55390
  } = _ref;
55095
55391
  const [displayedContent, setDisplayedContent] = reactExports.useState("");
55096
55392
  const [showCursor, setShowCursor] = reactExports.useState(false);
@@ -55099,19 +55395,23 @@
55099
55395
  const animationFrameRef = reactExports.useRef(null);
55100
55396
  const lastUpdateRef = reactExports.useRef(0);
55101
55397
  reactExports.useEffect(() => {
55398
+ // Reset if content changes significantly
55102
55399
  if (content !== contentRef.current) {
55103
55400
  contentRef.current = content;
55401
+ // If we're already past the new content length, show it all
55104
55402
  if (indexRef.current >= content.length) {
55105
55403
  setDisplayedContent(content);
55106
55404
  setShowCursor(false);
55107
55405
  return;
55108
55406
  }
55407
+ // If new content is shorter, reset
55109
55408
  if (content.length < displayedContent.length) {
55110
55409
  indexRef.current = 0;
55111
55410
  setDisplayedContent("");
55112
55411
  }
55113
55412
  }
55114
55413
  if (!isStreaming) {
55414
+ // Not streaming - show full content immediately
55115
55415
  setDisplayedContent(content);
55116
55416
  setShowCursor(false);
55117
55417
  if (animationFrameRef.current) {
@@ -55119,20 +55419,25 @@
55119
55419
  }
55120
55420
  return;
55121
55421
  }
55422
+
55423
+ // Streaming mode - animate character by character
55122
55424
  setShowCursor(true);
55123
55425
  const animate = timestamp => {
55426
+ // Throttle updates for smooth 60fps animation
55124
55427
  if (timestamp - lastUpdateRef.current < 16) {
55125
55428
  animationFrameRef.current = requestAnimationFrame(animate);
55126
55429
  return;
55127
55430
  }
55128
55431
  lastUpdateRef.current = timestamp;
55129
55432
  if (indexRef.current < content.length) {
55433
+ // Add 2-5 characters per frame for natural speed
55130
55434
  const charsToAdd = Math.max(2, Math.floor(Math.random() * 4));
55131
55435
  const newIndex = Math.min(indexRef.current + charsToAdd, content.length);
55132
55436
  setDisplayedContent(content.slice(0, newIndex));
55133
55437
  indexRef.current = newIndex;
55134
55438
  animationFrameRef.current = requestAnimationFrame(animate);
55135
55439
  } else {
55440
+ // Finished streaming
55136
55441
  setShowCursor(false);
55137
55442
  }
55138
55443
  };
@@ -55142,7 +55447,7 @@
55142
55447
  cancelAnimationFrame(animationFrameRef.current);
55143
55448
  }
55144
55449
  };
55145
- }, [content, displayedContent.length, isStreaming]);
55450
+ }, [content, isStreaming]);
55146
55451
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55147
55452
  className: "relative",
55148
55453
  style: {
@@ -55151,113 +55456,143 @@
55151
55456
  },
55152
55457
  children: [/*#__PURE__*/jsxRuntimeExports.jsx(Markdown, {
55153
55458
  remarkPlugins: [remarkGfm],
55154
- className: "markdown-body text-sm leading-relaxed space-y-3 ".concat(isDark ? "text-gray-100" : "text-gray-800"),
55459
+ className: "prose prose-sm max-w-none [&>*:last-child]:!mb-0 [&>*:first-child]:!mt-0",
55460
+ style: {
55461
+ color: textColor || (isDark ? "#f3f4f6" : "#1A1A1A"),
55462
+ fontFamily: fontFamily || "Inter",
55463
+ fontSize: fontSize || "14px",
55464
+ margin: 0,
55465
+ padding: 0
55466
+ },
55155
55467
  components: {
55156
- p(_ref2) {
55468
+ a(_ref2) {
55157
55469
  let {
55158
55470
  node,
55159
55471
  children
55160
55472
  } = _ref2,
55161
55473
  props = _objectWithoutProperties$1(_ref2, _excluded);
55162
- return /*#__PURE__*/jsxRuntimeExports.jsx("p", _objectSpread2(_objectSpread2({
55163
- className: "mb-2 leading-relaxed ".concat(isDark ? "text-gray-100" : "text-gray-800")
55474
+ return /*#__PURE__*/jsxRuntimeExports.jsx("a", _objectSpread2(_objectSpread2({
55475
+ className: isDark ? "text-blue-400 hover:text-blue-300 hover:underline" : "text-blue-600 hover:underline",
55476
+ target: "_blank",
55477
+ rel: "noopener noreferrer"
55164
55478
  }, props), {}, {
55165
55479
  children: children
55166
55480
  }));
55167
55481
  },
55168
- h1(_ref3) {
55482
+ p(_ref3) {
55169
55483
  let {
55170
55484
  node,
55171
55485
  children
55172
55486
  } = _ref3,
55173
55487
  props = _objectWithoutProperties$1(_ref3, _excluded2);
55174
- return /*#__PURE__*/jsxRuntimeExports.jsx("h1", _objectSpread2(_objectSpread2({
55175
- className: "text-xl font-semibold mt-3 mb-2 ".concat(isDark ? "text-gray-100" : "text-gray-900")
55488
+ return /*#__PURE__*/jsxRuntimeExports.jsx("p", _objectSpread2(_objectSpread2({
55489
+ className: "leading-relaxed [&:not(:last-child)]:mb-2 [&:first-child]:!mt-0",
55490
+ style: {
55491
+ color: textColor || (isDark ? "#f3f4f6" : "#1A1A1A"),
55492
+ fontFamily: fontFamily || "Inter",
55493
+ fontSize: fontSize || "14px",
55494
+ margin: 0,
55495
+ marginTop: 0,
55496
+ padding: 0
55497
+ }
55176
55498
  }, props), {}, {
55177
55499
  children: children
55178
55500
  }));
55179
55501
  },
55180
- h2(_ref4) {
55502
+ ul(_ref4) {
55181
55503
  let {
55182
55504
  node,
55183
55505
  children
55184
55506
  } = _ref4,
55185
55507
  props = _objectWithoutProperties$1(_ref4, _excluded3);
55186
- return /*#__PURE__*/jsxRuntimeExports.jsx("h2", _objectSpread2(_objectSpread2({
55187
- className: "text-lg font-semibold mt-3 mb-2 ".concat(isDark ? "text-gray-100" : "text-gray-900")
55508
+ return /*#__PURE__*/jsxRuntimeExports.jsx("ul", _objectSpread2(_objectSpread2({
55509
+ className: "list-disc pl-5 space-y-1 [&:first-child]:!mt-0",
55510
+ style: {
55511
+ marginTop: 0
55512
+ }
55188
55513
  }, props), {}, {
55189
55514
  children: children
55190
55515
  }));
55191
55516
  },
55192
- h3(_ref5) {
55517
+ ol(_ref5) {
55193
55518
  let {
55194
55519
  node,
55195
55520
  children
55196
55521
  } = _ref5,
55197
55522
  props = _objectWithoutProperties$1(_ref5, _excluded4);
55198
- return /*#__PURE__*/jsxRuntimeExports.jsx("h3", _objectSpread2(_objectSpread2({
55199
- className: "text-base font-semibold mt-3 mb-2 ".concat(isDark ? "text-gray-100" : "text-gray-900")
55523
+ return /*#__PURE__*/jsxRuntimeExports.jsx("ol", _objectSpread2(_objectSpread2({
55524
+ className: "list-decimal pl-5 space-y-1 [&:first-child]:!mt-0",
55525
+ style: {
55526
+ marginTop: 0
55527
+ }
55200
55528
  }, props), {}, {
55201
55529
  children: children
55202
55530
  }));
55203
55531
  },
55204
- code(_ref6) {
55532
+ li(_ref6) {
55205
55533
  let {
55206
- className,
55534
+ node,
55207
55535
  children
55208
55536
  } = _ref6,
55209
55537
  props = _objectWithoutProperties$1(_ref6, _excluded5);
55210
- const match = /language-(\w+)/.exec(className || "");
55211
- if (match) {
55212
- return /*#__PURE__*/jsxRuntimeExports.jsx(SyntaxHighlighter, _objectSpread2(_objectSpread2({
55213
- style: oneDark,
55214
- language: match[1],
55215
- PreTag: "div",
55216
- wrapLines: true
55217
- }, props), {}, {
55218
- children: String(children).replace(/\n$/, "")
55219
- }));
55220
- }
55221
- return /*#__PURE__*/jsxRuntimeExports.jsx("code", _objectSpread2(_objectSpread2({
55222
- className: className
55538
+ return /*#__PURE__*/jsxRuntimeExports.jsx("li", _objectSpread2(_objectSpread2({
55539
+ className: "mb-1",
55540
+ style: {
55541
+ color: textColor || (isDark ? "#f3f4f6" : "#1A1A1A"),
55542
+ fontFamily: fontFamily || "Inter",
55543
+ fontSize: fontSize || "14px"
55544
+ }
55223
55545
  }, props), {}, {
55224
55546
  children: children
55225
55547
  }));
55226
55548
  },
55227
- a(_ref7) {
55549
+ h1(_ref7) {
55228
55550
  let {
55229
55551
  node,
55230
55552
  children
55231
55553
  } = _ref7,
55232
55554
  props = _objectWithoutProperties$1(_ref7, _excluded6);
55233
- return /*#__PURE__*/jsxRuntimeExports.jsx("a", _objectSpread2(_objectSpread2({
55234
- className: isDark ? "text-blue-400 hover:text-blue-300 hover:underline" : "text-blue-600 hover:underline",
55235
- target: "_blank",
55236
- rel: "noopener noreferrer"
55555
+ return /*#__PURE__*/jsxRuntimeExports.jsx("h1", _objectSpread2(_objectSpread2({
55556
+ className: "text-xl font-bold mb-2 [&:first-child]:!mt-0",
55557
+ style: {
55558
+ color: textColor || (isDark ? "#f3f4f6" : "#1A1A1A"),
55559
+ fontFamily: fontFamily || "Inter",
55560
+ marginTop: "0.75rem"
55561
+ }
55237
55562
  }, props), {}, {
55238
55563
  children: children
55239
55564
  }));
55240
55565
  },
55241
- ul(_ref8) {
55566
+ h2(_ref8) {
55242
55567
  let {
55243
55568
  node,
55244
55569
  children
55245
55570
  } = _ref8,
55246
55571
  props = _objectWithoutProperties$1(_ref8, _excluded7);
55247
- return /*#__PURE__*/jsxRuntimeExports.jsx("ul", _objectSpread2(_objectSpread2({
55248
- className: "list-disc pl-5 space-y-1"
55572
+ return /*#__PURE__*/jsxRuntimeExports.jsx("h2", _objectSpread2(_objectSpread2({
55573
+ className: "text-lg font-semibold mb-2 [&:first-child]:!mt-0",
55574
+ style: {
55575
+ color: textColor || (isDark ? "#f3f4f6" : "#1A1A1A"),
55576
+ fontFamily: fontFamily || "Inter",
55577
+ marginTop: "0.75rem"
55578
+ }
55249
55579
  }, props), {}, {
55250
55580
  children: children
55251
55581
  }));
55252
55582
  },
55253
- ol(_ref9) {
55583
+ h3(_ref9) {
55254
55584
  let {
55255
55585
  node,
55256
55586
  children
55257
55587
  } = _ref9,
55258
55588
  props = _objectWithoutProperties$1(_ref9, _excluded8);
55259
- return /*#__PURE__*/jsxRuntimeExports.jsx("ol", _objectSpread2(_objectSpread2({
55260
- className: "list-decimal pl-5 space-y-1"
55589
+ return /*#__PURE__*/jsxRuntimeExports.jsx("h3", _objectSpread2(_objectSpread2({
55590
+ className: "text-base font-semibold mb-2 [&:first-child]:!mt-0",
55591
+ style: {
55592
+ color: textColor || (isDark ? "#f3f4f6" : "#1A1A1A"),
55593
+ fontFamily: fontFamily || "Inter",
55594
+ marginTop: "0.75rem"
55595
+ }
55261
55596
  }, props), {}, {
55262
55597
  children: children
55263
55598
  }));
@@ -55312,6 +55647,29 @@
55312
55647
  }, props), {}, {
55313
55648
  children: children
55314
55649
  }));
55650
+ },
55651
+ code(_ref12) {
55652
+ let {
55653
+ className,
55654
+ children
55655
+ } = _ref12,
55656
+ props = _objectWithoutProperties$1(_ref12, _excluded11);
55657
+ const match = /language-(\w+)/.exec(className || "");
55658
+ if (match) {
55659
+ return /*#__PURE__*/jsxRuntimeExports.jsx(SyntaxHighlighter, _objectSpread2(_objectSpread2({
55660
+ style: oneDark,
55661
+ language: match[1],
55662
+ PreTag: "div",
55663
+ wrapLines: true
55664
+ }, props), {}, {
55665
+ children: String(children).replace(/\n$/, "")
55666
+ }));
55667
+ }
55668
+ return /*#__PURE__*/jsxRuntimeExports.jsx("code", _objectSpread2(_objectSpread2({
55669
+ className: className
55670
+ }, props), {}, {
55671
+ children: children
55672
+ }));
55315
55673
  }
55316
55674
  },
55317
55675
  children: displayedContent
@@ -55323,34 +55681,54 @@
55323
55681
  })]
55324
55682
  });
55325
55683
  };
55326
- const TypingIndicator = _ref12 => {
55327
- let {
55328
- isDark
55329
- } = _ref12;
55684
+ const TypingIndicator = _ref13 => {
55330
55685
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55331
55686
  className: "flex gap-1.5",
55332
55687
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
55333
55688
  className: "w-2 h-2 rounded-full bg-gray-400 animate-bounce",
55334
55689
  style: {
55335
- animationDelay: "0ms",
55336
- backgroundColor: isDark ? "#9CA3AF" : "#9CA3AF"
55690
+ animationDelay: "0ms"
55337
55691
  }
55338
55692
  }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55339
55693
  className: "w-2 h-2 rounded-full bg-gray-400 animate-bounce",
55340
55694
  style: {
55341
- animationDelay: "150ms",
55342
- backgroundColor: isDark ? "#9CA3AF" : "#9CA3AF"
55695
+ animationDelay: "150ms"
55343
55696
  }
55344
55697
  }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55345
55698
  className: "w-2 h-2 rounded-full bg-gray-400 animate-bounce",
55346
55699
  style: {
55347
- animationDelay: "300ms",
55348
- backgroundColor: isDark ? "#9CA3AF" : "#9CA3AF"
55700
+ animationDelay: "300ms"
55349
55701
  }
55350
55702
  })]
55351
55703
  });
55352
55704
  };
55353
- const AssistantMessage = _ref13 => {
55705
+
55706
+ // Decision widget component for HITL (Human-in-the-Loop)
55707
+ const DecisionWidget = _ref14 => {
55708
+ let {
55709
+ question,
55710
+ options,
55711
+ onDecision,
55712
+ disabled,
55713
+ isDark = false
55714
+ } = _ref14;
55715
+ return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55716
+ className: "mt-3 p-3 rounded-lg border ".concat(isDark ? "bg-blue-900/30 border-blue-700/50" : "bg-blue-50 border-blue-200"),
55717
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("p", {
55718
+ className: "text-sm font-medium mb-2 ".concat(isDark ? "text-gray-100" : "text-gray-900"),
55719
+ children: question
55720
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55721
+ className: "space-y-2",
55722
+ children: options.map(option => /*#__PURE__*/jsxRuntimeExports.jsx("button", {
55723
+ onClick: () => onDecision(option.id, option.label),
55724
+ disabled: disabled,
55725
+ className: "w-full text-left px-3 py-2 text-sm rounded transition-all ".concat(isDark ? "bg-gray-800 border border-gray-700 hover:bg-blue-900/50 hover:border-blue-600 disabled:opacity-50 disabled:cursor-not-allowed" : "bg-white border border-gray-300 hover:bg-blue-50 hover:border-blue-400 disabled:opacity-50 disabled:cursor-not-allowed"),
55726
+ children: option.label
55727
+ }, option.id))
55728
+ })]
55729
+ });
55730
+ };
55731
+ const AssistantMessage = _ref15 => {
55354
55732
  let {
55355
55733
  message,
55356
55734
  formatTime,
@@ -55358,28 +55736,47 @@
55358
55736
  primaryColor,
55359
55737
  agentMessageBubbleColor,
55360
55738
  textColor,
55739
+ assistantTextColor,
55361
55740
  fontFamily,
55362
55741
  fontSize,
55363
- isRtl
55364
- } = _ref13;
55742
+ isRtl,
55743
+ onDecision,
55744
+ companyLogo,
55745
+ conciergeName,
55746
+ companyName
55747
+ } = _ref15;
55365
55748
  const displayContent = typeof message.content === "string" ? message.content : "";
55366
55749
  const isTypingMessage = message.isProcessing && (!displayContent || displayContent.length === 0);
55367
- const bubbleColor = agentMessageBubbleColor || (isDark ? "#1f2937" : "#FFFFFF");
55368
- const messageTextColor = textColor || (isDark ? "#f3f4f6" : "#1f2937");
55369
- const messageFontFamily = fontFamily || "Inter";
55370
- const messageFontSize = fontSize || "14px";
55750
+ const bubbleColor = agentMessageBubbleColor !== undefined && agentMessageBubbleColor !== null ? agentMessageBubbleColor : isDark ? "#1f2937" : "#FFFFFF";
55751
+ const messageTextColor = assistantTextColor !== undefined && assistantTextColor !== null ? assistantTextColor : textColor !== undefined && textColor !== null ? textColor : isDark ? "#f3f4f6" : "#1A1A1A";
55752
+ const messageFontFamily = fontFamily !== undefined && fontFamily !== null ? fontFamily : "Inter";
55753
+ const messageFontSize = fontSize !== undefined && fontSize !== null ? fontSize : "14px";
55371
55754
  useGoogleFont(messageFontFamily);
55372
55755
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55373
55756
  className: "flex gap-3 flex-row animate-in",
55374
55757
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
55375
55758
  className: "flex-shrink-0 mt-1",
55376
55759
  children: /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55377
- className: "w-8 h-8 rounded-full flex items-center justify-center ".concat(isDark ? "bg-gray-700" : "bg-gray-200"),
55378
- children: /*#__PURE__*/jsxRuntimeExports.jsx(Bot, {
55379
- className: "w-4 h-4",
55760
+ className: "w-8 h-8 rounded-full flex items-center justify-center overflow-hidden ".concat(isDark ? "bg-gray-700" : "bg-gray-200"),
55761
+ children: companyLogo ? /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55762
+ className: "w-full h-full flex items-center justify-center",
55763
+ children: /*#__PURE__*/jsxRuntimeExports.jsx("img", {
55764
+ src: companyLogo,
55765
+ alt: "Company logo",
55766
+ className: "w-full h-full object-cover rounded-full",
55767
+ style: {
55768
+ maxWidth: "100%",
55769
+ maxHeight: "100%"
55770
+ }
55771
+ })
55772
+ }) : /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55773
+ className: "w-full h-full flex items-center justify-center text-white font-semibold",
55380
55774
  style: {
55381
- color: primaryColor
55382
- }
55775
+ backgroundColor: primaryColor,
55776
+ fontSize: "14px",
55777
+ fontFamily: fontFamily || "Inter"
55778
+ },
55779
+ children: (conciergeName || companyName || "A").charAt(0).toUpperCase()
55383
55780
  })
55384
55781
  })
55385
55782
  }), /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
@@ -55396,35 +55793,64 @@
55396
55793
  },
55397
55794
  children: isTypingMessage ? /*#__PURE__*/jsxRuntimeExports.jsx(TypingIndicator, {
55398
55795
  isDark: isDark
55399
- }) : displayContent ? /*#__PURE__*/jsxRuntimeExports.jsx(StreamingMarkdown, {
55400
- content: displayContent,
55401
- isStreaming: message.isProcessing,
55402
- isDark: isDark,
55403
- isRtl: isRtl
55404
- }) : null
55796
+ }) : /*#__PURE__*/jsxRuntimeExports.jsx(jsxRuntimeExports.Fragment, {
55797
+ children: message.requiresDecision && message.decisionData && onDecision ? /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55798
+ children: [displayContent && /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55799
+ className: "mb-3",
55800
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(StreamingMarkdown, {
55801
+ content: displayContent,
55802
+ isStreaming: message.isProcessing,
55803
+ isDark: isDark,
55804
+ isRtl: isRtl,
55805
+ textColor: messageTextColor,
55806
+ fontFamily: messageFontFamily,
55807
+ fontSize: messageFontSize
55808
+ })
55809
+ }), /*#__PURE__*/jsxRuntimeExports.jsx(DecisionWidget, {
55810
+ question: message.decisionData.question,
55811
+ options: message.decisionData.options,
55812
+ onDecision: (optionId, optionValue) => onDecision(message.decisionData.decisionId, optionId, optionValue),
55813
+ disabled: !!message.decisionResponse,
55814
+ isDark: isDark
55815
+ }), message.decisionResponse && /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55816
+ className: "mt-2 text-xs ".concat(isDark ? "text-gray-400" : "text-gray-500"),
55817
+ children: ["Selected: ", message.decisionResponse.selectedOption]
55818
+ })]
55819
+ }) : displayContent ? /*#__PURE__*/jsxRuntimeExports.jsx(StreamingMarkdown, {
55820
+ content: displayContent,
55821
+ isStreaming: message.isProcessing,
55822
+ isDark: isDark,
55823
+ isRtl: isRtl,
55824
+ textColor: messageTextColor,
55825
+ fontFamily: messageFontFamily,
55826
+ fontSize: messageFontSize
55827
+ }) : null
55828
+ })
55405
55829
  }), /*#__PURE__*/jsxRuntimeExports.jsx("p", {
55406
55830
  className: "text-[11px] mt-1.5 px-1 ".concat(isDark ? "text-gray-500" : "text-gray-400"),
55831
+ dir: "ltr",
55407
55832
  children: formatTime(message.timestamp)
55408
55833
  })]
55409
55834
  })]
55410
55835
  });
55411
55836
  };
55412
- const UserMessage = _ref14 => {
55837
+ const UserMessage = _ref16 => {
55413
55838
  let {
55414
55839
  message,
55415
55840
  formatTime,
55416
55841
  userMessageBoxColor,
55417
55842
  primaryColor,
55418
55843
  textColor,
55844
+ userTextColor,
55419
55845
  fontFamily,
55420
55846
  fontSize,
55421
55847
  isDark,
55422
55848
  isRtl
55423
- } = _ref14;
55424
- const primary = userMessageBoxColor || primaryColor || "#2563eb";
55425
- const messageTextColor = textColor || "#FFFFFF";
55426
- const messageFontFamily = fontFamily || "Inter";
55427
- const messageFontSize = fontSize || "14px";
55849
+ } = _ref16;
55850
+ const primary = userMessageBoxColor !== undefined && userMessageBoxColor !== null ? userMessageBoxColor : primaryColor !== undefined && primaryColor !== null ? primaryColor : "#2563eb";
55851
+ const messageTextColor = userTextColor !== undefined && userTextColor !== null ? userTextColor : textColor !== undefined && textColor !== null ? textColor : "#FFFFFF";
55852
+ const messageFontFamily = fontFamily !== undefined && fontFamily !== null ? fontFamily : "Inter";
55853
+ const messageFontSize = fontSize !== undefined && fontSize !== null ? fontSize : "14px";
55428
55854
  useGoogleFont(messageFontFamily);
55429
55855
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55430
55856
  className: "flex gap-3 flex-row-reverse animate-in",
@@ -55443,26 +55869,31 @@
55443
55869
  style: {
55444
55870
  background: "linear-gradient(135deg, ".concat(primary, " 0%, ").concat(primary, "dd 100%)"),
55445
55871
  fontFamily: messageFontFamily,
55446
- fontSize: messageFontSize,
55447
55872
  textAlign: isRtl ? "right" : "left",
55448
55873
  direction: isRtl ? "rtl" : "ltr"
55449
55874
  },
55450
55875
  children: /*#__PURE__*/jsxRuntimeExports.jsx("p", {
55451
- className: "leading-relaxed whitespace-pre-wrap break-words",
55876
+ className: "whitespace-pre-wrap break-words",
55452
55877
  style: {
55453
55878
  color: messageTextColor,
55454
- textAlign: isRtl ? "right" : "left"
55879
+ fontFamily: messageFontFamily,
55880
+ fontSize: messageFontSize,
55881
+ lineHeight: "1.5",
55882
+ textAlign: isRtl ? "right" : "left",
55883
+ margin: 0,
55884
+ padding: 0
55455
55885
  },
55456
55886
  children: message.content
55457
55887
  })
55458
55888
  }), /*#__PURE__*/jsxRuntimeExports.jsx("p", {
55459
55889
  className: "text-[11px] mt-1.5 px-1 ".concat(isDark ? "text-gray-500" : "text-gray-400"),
55890
+ dir: "ltr",
55460
55891
  children: formatTime(message.timestamp)
55461
55892
  })]
55462
55893
  })]
55463
55894
  });
55464
55895
  };
55465
- const MessageBubble = _ref15 => {
55896
+ const MessageBubble = _ref17 => {
55466
55897
  let {
55467
55898
  message,
55468
55899
  isDark,
@@ -55470,11 +55901,17 @@
55470
55901
  agentMessageBubbleColor,
55471
55902
  userMessageBoxColor,
55472
55903
  textColor,
55904
+ assistantTextColor,
55905
+ userTextColor,
55473
55906
  fontFamily,
55474
55907
  fontSize,
55475
55908
  formatTime,
55476
- isRtl
55477
- } = _ref15;
55909
+ isRtl,
55910
+ onDecision,
55911
+ companyLogo,
55912
+ conciergeName,
55913
+ companyName
55914
+ } = _ref17;
55478
55915
  if (!message) {
55479
55916
  return null;
55480
55917
  }
@@ -55485,6 +55922,7 @@
55485
55922
  userMessageBoxColor: userMessageBoxColor,
55486
55923
  primaryColor: primaryColor,
55487
55924
  textColor: textColor,
55925
+ userTextColor: userTextColor,
55488
55926
  fontFamily: fontFamily,
55489
55927
  fontSize: fontSize,
55490
55928
  isDark: isDark,
@@ -55498,184 +55936,14 @@
55498
55936
  primaryColor: primaryColor,
55499
55937
  agentMessageBubbleColor: agentMessageBubbleColor,
55500
55938
  textColor: textColor,
55939
+ assistantTextColor: assistantTextColor,
55501
55940
  fontFamily: fontFamily,
55502
55941
  fontSize: fontSize,
55503
- isRtl: isRtl
55504
- });
55505
- };
55506
-
55507
- const MessageList = _ref => {
55508
- let {
55509
- messages = [],
55510
- isDark,
55511
- primaryColor,
55512
- isLoading,
55513
- agentMessageBubbleColor,
55514
- userMessageBoxColor,
55515
- textColor,
55516
- fontFamily,
55517
- fontSize,
55518
- formatTime,
55519
- isRtl = false
55520
- } = _ref;
55521
- const containerRef = reactExports.useRef(null);
55522
- const messagesEndRef = reactExports.useRef(null);
55523
- const shouldAutoScrollRef = reactExports.useRef(true);
55524
- const autoScrollKey = reactExports.useMemo(() => messages.map(message => {
55525
- var _message$content$leng, _message$content;
55526
- return "".concat(message.id, ":").concat((_message$content$leng = (_message$content = message.content) === null || _message$content === void 0 ? void 0 : _message$content.length) !== null && _message$content$leng !== void 0 ? _message$content$leng : 0, ":").concat(message.isProcessing ? 1 : 0);
55527
- }).join("|"), [messages]);
55528
- const updateAutoScrollIntent = reactExports.useCallback(() => {
55529
- const container = containerRef.current;
55530
- if (!container) {
55531
- return;
55532
- }
55533
- const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
55534
- shouldAutoScrollRef.current = distanceFromBottom < 160;
55535
- }, []);
55536
- reactExports.useEffect(() => {
55537
- const container = containerRef.current;
55538
- if (!container) {
55539
- return undefined;
55540
- }
55541
- container.addEventListener("scroll", updateAutoScrollIntent, {
55542
- passive: true
55543
- });
55544
- updateAutoScrollIntent();
55545
- return () => {
55546
- container.removeEventListener("scroll", updateAutoScrollIntent);
55547
- };
55548
- }, [updateAutoScrollIntent]);
55549
- reactExports.useEffect(() => {
55550
- if (!shouldAutoScrollRef.current) {
55551
- return undefined;
55552
- }
55553
- if (typeof window === "undefined") {
55554
- return undefined;
55555
- }
55556
- const frame = window.requestAnimationFrame(() => {
55557
- var _messagesEndRef$curre;
55558
- (_messagesEndRef$curre = messagesEndRef.current) === null || _messagesEndRef$curre === void 0 || _messagesEndRef$curre.scrollIntoView({
55559
- behavior: "smooth",
55560
- block: "end"
55561
- });
55562
- });
55563
- return () => {
55564
- window.cancelAnimationFrame(frame);
55565
- };
55566
- }, [autoScrollKey, isLoading]);
55567
- return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55568
- ref: containerRef,
55569
- className: "chat-messages flex-1 overflow-y-auto px-5 py-4 space-y-4 min-h-0 ".concat(isDark ? "bg-gray-900" : "bg-gradient-to-b from-gray-50/50 to-white", " break-words"),
55570
- children: [messages.map(message => /*#__PURE__*/jsxRuntimeExports.jsx(MessageBubble, {
55571
- message: message,
55572
- isDark: isDark,
55573
- primaryColor: primaryColor,
55574
- agentMessageBubbleColor: agentMessageBubbleColor,
55575
- userMessageBoxColor: userMessageBoxColor,
55576
- textColor: textColor,
55577
- fontFamily: fontFamily,
55578
- fontSize: fontSize,
55579
- formatTime: formatTime,
55580
- isRtl: isRtl
55581
- }, message.id)), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55582
- ref: messagesEndRef,
55583
- className: "h-px"
55584
- })]
55585
- });
55586
- };
55587
-
55588
- const ChatInput = _ref => {
55589
- let {
55590
- inputValue,
55591
- setInputValue,
55592
- onSend,
55593
- isDark,
55594
- primaryColor,
55595
- isOpen,
55596
- isMinimized,
55597
- isLoading,
55598
- placeholder,
55599
- textColor,
55600
- fontFamily,
55601
- fontSize,
55602
- isRtl = false
55603
- } = _ref;
55604
- const textareaRef = reactExports.useRef(null);
55605
- useGoogleFont(fontFamily);
55606
- reactExports.useEffect(() => {
55607
- if (isOpen && !isMinimized) {
55608
- var _textareaRef$current;
55609
- (_textareaRef$current = textareaRef.current) === null || _textareaRef$current === void 0 || _textareaRef$current.focus();
55610
- }
55611
- }, [isOpen, isMinimized]);
55612
- reactExports.useEffect(() => {
55613
- const textarea = textareaRef.current;
55614
- if (textarea) {
55615
- textarea.style.height = "auto";
55616
- textarea.style.height = "".concat(Math.min(textarea.scrollHeight, 120), "px");
55617
- }
55618
- }, [inputValue]);
55619
- const handleKeyPress = event => {
55620
- if (event.key === "Enter" && !event.shiftKey) {
55621
- event.preventDefault();
55622
- onSend();
55623
- }
55624
- };
55625
- const handleChange = event => {
55626
- setInputValue(event.target.value);
55627
- };
55628
- const isDisabled = !inputValue.trim() || isLoading;
55629
- const separatorColor = isDark ? "#374151" : "#e5e7eb";
55630
- return /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55631
- className: "px-5 py-4 border-t flex-shrink-0 ".concat(isDark ? "border-gray-700 bg-gray-900" : "border-gray-200 bg-white"),
55632
- style: {
55633
- borderTopColor: separatorColor,
55634
- borderTopWidth: "1px",
55635
- borderTopStyle: "solid"
55636
- },
55637
- children: /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
55638
- className: "flex gap-2.5 items-end",
55639
- children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
55640
- className: "flex-1 flex items-center min-h-[48px] ".concat(isDark ? "bg-gray-800" : "bg-gray-50", " rounded-2xl border transition-all duration-200"),
55641
- style: {
55642
- borderColor: inputValue ? primaryColor : isDark ? "#374151" : "#e5e7eb",
55643
- borderWidth: inputValue ? "2px" : "1px"
55644
- },
55645
- children: /*#__PURE__*/jsxRuntimeExports.jsx("textarea", {
55646
- id: "chat-widget-input",
55647
- ref: textareaRef,
55648
- value: inputValue,
55649
- onChange: handleChange,
55650
- onKeyDown: handleKeyPress,
55651
- placeholder: placeholder || "Type your message...",
55652
- rows: 1,
55653
- disabled: isLoading,
55654
- dir: isRtl ? "rtl" : "ltr",
55655
- className: "w-full px-4 py-0 bg-transparent rounded-xl leading-normal ".concat(isDark ? "text-gray-100" : "text-gray-900", " placeholder-gray-400 resize-none outline-none scrollbar-hide"),
55656
- style: {
55657
- maxHeight: "120px",
55658
- lineHeight: "48px",
55659
- fontFamily: fontFamily || "Inter",
55660
- fontSize: fontSize || "14px",
55661
- color: textColor || (isDark ? "#f3f4f6" : "#111827"),
55662
- textAlign: isRtl ? "right" : "left"
55663
- }
55664
- })
55665
- }), /*#__PURE__*/jsxRuntimeExports.jsx("button", {
55666
- onClick: onSend,
55667
- disabled: isDisabled,
55668
- className: "h-12 w-12 p-0 rounded-xl transition-all duration-200 flex items-center justify-center shadow-lg disabled:opacity-40 disabled:cursor-not-allowed disabled:shadow-none ".concat(!isDisabled ? "scale-100" : "scale-95"),
55669
- style: {
55670
- backgroundColor: !isDisabled ? primaryColor : isDark ? "#374151" : "#e5e7eb",
55671
- color: !isDisabled ? "white" : "#9ca3af"
55672
- },
55673
- "aria-label": "Send message",
55674
- children: /*#__PURE__*/jsxRuntimeExports.jsx(Send, {
55675
- className: "w-5 h-5"
55676
- })
55677
- })]
55678
- })
55942
+ isRtl: isRtl,
55943
+ onDecision: onDecision,
55944
+ companyLogo: companyLogo,
55945
+ conciergeName: conciergeName,
55946
+ companyName: companyName
55679
55947
  });
55680
55948
  };
55681
55949
 
@@ -55856,6 +56124,7 @@
55856
56124
  };
55857
56125
  };
55858
56126
  const QuickQuestions = _ref3 => {
56127
+ var _ref4;
55859
56128
  let {
55860
56129
  questions = [],
55861
56130
  isDark = false,
@@ -55866,12 +56135,15 @@
55866
56135
  fontFamily = "Inter",
55867
56136
  fontSize = "14px",
55868
56137
  textColor,
55869
- isRtl = false
56138
+ isRtl = false,
56139
+ questionTextColor,
56140
+ questionBoxColor
55870
56141
  } = _ref3;
55871
56142
  if (!questions.length || isTyping) {
55872
56143
  return null;
55873
56144
  }
55874
- const resolvedTextColor = textColor !== null && textColor !== void 0 ? textColor : isDark ? "#f3f4f6" : "#111827";
56145
+ const resolvedTextColor = (_ref4 = questionTextColor !== null && questionTextColor !== void 0 ? questionTextColor : textColor) !== null && _ref4 !== void 0 ? _ref4 : isDark ? "#f3f4f6" : "#111827";
56146
+ const resolvedQuestionBoxColor = questionBoxColor !== null && questionBoxColor !== void 0 ? questionBoxColor : isDark ? "#374151" : "#e5e7eb";
55875
56147
  const palette = reactExports.useMemo(() => getQuickQuestionPalette(resolvedTextColor, isDark), [resolvedTextColor, isDark]);
55876
56148
  const applyHoverState = (element, isHover) => {
55877
56149
  element.style.backgroundColor = isHover ? palette.hoverBackground : palette.background;
@@ -55920,11 +56192,19 @@
55920
56192
  })]
55921
56193
  });
55922
56194
  }
55923
- return /*#__PURE__*/jsxRuntimeExports.jsx("div", {
55924
- className: "space-y-3 flex flex-col items-start",
56195
+
56196
+ // Vertical layout (default)
56197
+ return /*#__PURE__*/jsxRuntimeExports.jsx("div", _objectSpread2(_objectSpread2({
56198
+ className: "space-y-3 flex flex-col items-start pl-[50px]"
56199
+ }, isRtl && {
56200
+ style: {
56201
+ paddingLeft: "0",
56202
+ paddingRight: "50px"
56203
+ }
56204
+ }), {}, {
55925
56205
  children: questions.map((question, index) => /*#__PURE__*/jsxRuntimeExports.jsxs("button", {
55926
56206
  onClick: () => onQuestionClick === null || onQuestionClick === void 0 ? void 0 : onQuestionClick(question),
55927
- className: "group relative text-sm px-5 py-3 rounded-2xl font-medium transition-all duration-300 hover:scale-[1.02] active:scale-[0.98] whitespace-normal max-w-[85%] overflow-hidden backdrop-blur-sm ".concat(isRtl ? "text-right" : "text-left"),
56207
+ className: "group relative text-sm px-3 py-3 rounded-2xl font-medium transition-all duration-300 hover:scale-[1.02] active:scale-[0.98] whitespace-normal max-w-[85%] overflow-hidden backdrop-blur-sm ".concat(isRtl ? "text-right" : "text-left"),
55928
56208
  style: {
55929
56209
  direction: isRtl ? "rtl" : "ltr",
55930
56210
  backgroundColor: palette.background,
@@ -55938,29 +56218,608 @@
55938
56218
  applyHoverState(e.currentTarget, false);
55939
56219
  },
55940
56220
  children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
55941
- className: "absolute inset-0 bg-gradient-to-br from-white/0 via-white/0 to-white/5 opacity-0 group-hover:opacity-100 transition-opacity duration-300 rounded-2xl"
55942
- }), /*#__PURE__*/jsxRuntimeExports.jsxs("span", {
56221
+ style: {
56222
+ backgroundColor: "#F5F5F5",
56223
+ border: "1px solid ".concat(resolvedQuestionBoxColor)
56224
+ },
56225
+ className: "absolute inset-0 transition-opacity duration-300 rounded-2xl"
56226
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("span", {
55943
56227
  className: "relative z-10 flex items-center gap-2.5 text-left",
55944
- children: [/*#__PURE__*/jsxRuntimeExports.jsx(MessageCircleQuestion, {
55945
- className: "h-4 w-4 opacity-60 group-hover:opacity-100 transition-opacity duration-300 flex-shrink-0",
55946
- style: {
55947
- color: primaryColor
55948
- }
55949
- }), /*#__PURE__*/jsxRuntimeExports.jsx("span", {
56228
+ children: /*#__PURE__*/jsxRuntimeExports.jsx("span", {
55950
56229
  className: "flex-1 leading-snug ".concat(isRtl ? "text-right" : "text-left"),
55951
56230
  style: {
55952
56231
  direction: isRtl ? "rtl" : "ltr",
55953
- color: resolvedTextColor,
56232
+ color: "#1A1A1A",
55954
56233
  fontFamily,
55955
56234
  fontSize
55956
56235
  },
55957
56236
  children: question
55958
- })]
56237
+ })
55959
56238
  })]
55960
56239
  }, index))
56240
+ }));
56241
+ };
56242
+
56243
+ const LoadingState = _ref => {
56244
+ let {
56245
+ title,
56246
+ description,
56247
+ isAnimated = false
56248
+ } = _ref;
56249
+ return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56250
+ className: "flex flex-col items-center justify-center py-12 px-4",
56251
+ children: [isAnimated && /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56252
+ className: "flex gap-1.5 mb-4",
56253
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
56254
+ className: "w-2 h-2 rounded-full bg-gray-400 animate-bounce",
56255
+ style: {
56256
+ animationDelay: "0ms"
56257
+ }
56258
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56259
+ className: "w-2 h-2 rounded-full bg-gray-400 animate-bounce",
56260
+ style: {
56261
+ animationDelay: "150ms"
56262
+ }
56263
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56264
+ className: "w-2 h-2 rounded-full bg-gray-400 animate-bounce",
56265
+ style: {
56266
+ animationDelay: "300ms"
56267
+ }
56268
+ })]
56269
+ }), title && /*#__PURE__*/jsxRuntimeExports.jsx("h3", {
56270
+ className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-1",
56271
+ children: title
56272
+ }), description && /*#__PURE__*/jsxRuntimeExports.jsx("p", {
56273
+ className: "text-xs text-gray-500 dark:text-gray-400 text-center max-w-xs",
56274
+ children: description
56275
+ })]
55961
56276
  });
55962
56277
  };
55963
56278
 
56279
+ const MessageList = _ref => {
56280
+ let {
56281
+ messages = [],
56282
+ isDark,
56283
+ primaryColor,
56284
+ isLoading,
56285
+ agentMessageBubbleColor,
56286
+ userMessageBoxColor,
56287
+ textColor,
56288
+ assistantTextColor,
56289
+ userTextColor,
56290
+ fontFamily,
56291
+ fontSize,
56292
+ formatTime,
56293
+ isRtl = false,
56294
+ quickQuestions = [],
56295
+ shouldShowQuickQuestions = false,
56296
+ onQuickQuestion,
56297
+ quickQuestionsLayout = "vertical",
56298
+ onDecision,
56299
+ disclaimerText,
56300
+ disclaimerPosition = "top",
56301
+ resolvedAgentBubbleColor,
56302
+ resolvedUserMessageColor,
56303
+ resolvedTextColor,
56304
+ isVoiceSessionActive = false,
56305
+ voiceStatus = "idle",
56306
+ messagesContainerRef,
56307
+ companyLogo,
56308
+ conciergeName,
56309
+ companyName
56310
+ } = _ref;
56311
+ const messagesEndRef = reactExports.useRef(null);
56312
+ const shouldAutoScrollRef = reactExports.useRef(true);
56313
+ const autoScrollKey = reactExports.useMemo(() => messages.map(message => {
56314
+ var _message$content$leng, _message$content;
56315
+ return "".concat(message.id, ":").concat((_message$content$leng = (_message$content = message.content) === null || _message$content === void 0 ? void 0 : _message$content.length) !== null && _message$content$leng !== void 0 ? _message$content$leng : 0, ":").concat(message.isProcessing ? 1 : 0);
56316
+ }).join("|"), [messages]);
56317
+ const updateAutoScrollIntent = reactExports.useCallback(() => {
56318
+ const container = messagesContainerRef === null || messagesContainerRef === void 0 ? void 0 : messagesContainerRef.current;
56319
+ if (!container) {
56320
+ return;
56321
+ }
56322
+ const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
56323
+ shouldAutoScrollRef.current = distanceFromBottom < 160;
56324
+ }, [messagesContainerRef]);
56325
+ reactExports.useEffect(() => {
56326
+ const container = messagesContainerRef === null || messagesContainerRef === void 0 ? void 0 : messagesContainerRef.current;
56327
+ if (!container) {
56328
+ return undefined;
56329
+ }
56330
+ container.addEventListener("scroll", updateAutoScrollIntent, {
56331
+ passive: true
56332
+ });
56333
+ updateAutoScrollIntent();
56334
+ return () => {
56335
+ container.removeEventListener("scroll", updateAutoScrollIntent);
56336
+ };
56337
+ }, [updateAutoScrollIntent, messagesContainerRef]);
56338
+ reactExports.useEffect(() => {
56339
+ if (!shouldAutoScrollRef.current) {
56340
+ return undefined;
56341
+ }
56342
+ if (typeof window === "undefined") {
56343
+ return undefined;
56344
+ }
56345
+ const frame = window.requestAnimationFrame(() => {
56346
+ var _messagesEndRef$curre;
56347
+ (_messagesEndRef$curre = messagesEndRef.current) === null || _messagesEndRef$curre === void 0 || _messagesEndRef$curre.scrollIntoView({
56348
+ behavior: "smooth",
56349
+ block: "end"
56350
+ });
56351
+ });
56352
+ return () => {
56353
+ window.cancelAnimationFrame(frame);
56354
+ };
56355
+ }, [autoScrollKey, isLoading]);
56356
+ return /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
56357
+ children: [isVoiceSessionActive && messages.length === 0 && /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56358
+ className: "flex flex-col items-center justify-center h-full",
56359
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(LoadingState, {
56360
+ title: "Starting voice session...",
56361
+ description: "Allow microphone access or begin speaking to continue.",
56362
+ isAnimated: true
56363
+ })
56364
+ }), disclaimerText && disclaimerPosition === "top" && /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56365
+ className: "flex justify-center",
56366
+ children: /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56367
+ className: "rounded-lg px-2 py-1 max-w-[85%] border",
56368
+ style: {
56369
+ backgroundColor: agentMessageBubbleColor !== undefined && agentMessageBubbleColor !== null ? agentMessageBubbleColor : resolvedAgentBubbleColor || "#F5F5F5",
56370
+ color: assistantTextColor !== undefined && assistantTextColor !== null ? assistantTextColor : "#1A1A1A",
56371
+ fontFamily: fontFamily !== undefined && fontFamily !== null ? fontFamily : "Inter",
56372
+ fontSize: fontSize !== undefined && fontSize !== null ? "calc(".concat(fontSize, " - 2px)") : "12px",
56373
+ direction: isRtl ? "rtl" : "ltr",
56374
+ textAlign: "center",
56375
+ borderColor: isDark ? "#374151" : "#e5e7eb"
56376
+ },
56377
+ children: /*#__PURE__*/jsxRuntimeExports.jsx("p", {
56378
+ className: "leading-relaxed whitespace-pre-wrap break-words",
56379
+ dir: isRtl ? "rtl" : "ltr",
56380
+ style: {
56381
+ color: assistantTextColor !== undefined && assistantTextColor !== null ? assistantTextColor : "#1A1A1A",
56382
+ fontFamily: fontFamily !== undefined && fontFamily !== null ? fontFamily : "Inter",
56383
+ fontSize: fontSize !== undefined && fontSize !== null ? "calc(".concat(fontSize, " - 2px)") : "12px"
56384
+ },
56385
+ children: disclaimerText
56386
+ })
56387
+ })
56388
+ }), messages.map(message => /*#__PURE__*/jsxRuntimeExports.jsx(MessageBubble, {
56389
+ message: message,
56390
+ isDark: isDark,
56391
+ primaryColor: primaryColor,
56392
+ agentMessageBubbleColor: agentMessageBubbleColor,
56393
+ userMessageBoxColor: userMessageBoxColor,
56394
+ textColor: textColor,
56395
+ assistantTextColor: assistantTextColor,
56396
+ userTextColor: userTextColor,
56397
+ fontFamily: fontFamily,
56398
+ fontSize: fontSize,
56399
+ formatTime: formatTime,
56400
+ isRtl: isRtl,
56401
+ onDecision: onDecision,
56402
+ companyLogo: companyLogo,
56403
+ conciergeName: conciergeName,
56404
+ companyName: companyName
56405
+ }, message.id)), shouldShowQuickQuestions && quickQuestions.length > 0 && /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56406
+ className: "pt-1",
56407
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(QuickQuestions, {
56408
+ questions: quickQuestions,
56409
+ isDark: isDark,
56410
+ primaryColor: primaryColor,
56411
+ onQuestionClick: onQuickQuestion,
56412
+ isTyping: isLoading,
56413
+ layout: quickQuestionsLayout,
56414
+ fontFamily: fontFamily,
56415
+ fontSize: fontSize,
56416
+ textColor: textColor,
56417
+ isRtl: isRtl,
56418
+ questionTextColor: assistantTextColor,
56419
+ questionBoxColor: agentMessageBubbleColor
56420
+ })
56421
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56422
+ ref: messagesEndRef,
56423
+ className: "h-px"
56424
+ })]
56425
+ });
56426
+ };
56427
+
56428
+ const ChatInput = _ref => {
56429
+ let {
56430
+ inputValue,
56431
+ setInputValue,
56432
+ onSend,
56433
+ isDark,
56434
+ primaryColor,
56435
+ isOpen,
56436
+ isMinimized,
56437
+ isLoading,
56438
+ placeholder,
56439
+ textColor,
56440
+ fontFamily,
56441
+ fontSize,
56442
+ isRtl = false,
56443
+ enableVoiceInteraction = false,
56444
+ isVoiceSessionActive = false,
56445
+ voiceStatus = "idle",
56446
+ voiceError = null,
56447
+ onVoiceToggle,
56448
+ onBidiSubmit,
56449
+ disclaimerText,
56450
+ disclaimerPosition = "top"
56451
+ } = _ref;
56452
+ const textareaRef = reactExports.useRef(null);
56453
+ useGoogleFont(fontFamily);
56454
+ reactExports.useEffect(() => {
56455
+ if (isOpen && !isMinimized) {
56456
+ var _textareaRef$current;
56457
+ (_textareaRef$current = textareaRef.current) === null || _textareaRef$current === void 0 || _textareaRef$current.focus();
56458
+ }
56459
+ }, [isOpen, isMinimized]);
56460
+
56461
+ // Auto-resize textarea
56462
+ reactExports.useEffect(() => {
56463
+ const textarea = textareaRef.current;
56464
+ if (textarea) {
56465
+ // Reset height to auto to get accurate scrollHeight
56466
+ textarea.style.height = "auto";
56467
+ // Calculate new height based on scrollHeight
56468
+ const scrollHeight = textarea.scrollHeight;
56469
+ // Ensure minimum height when there's text (lineHeight 1.4 means we need at least ~20px per line)
56470
+ const minHeightWithText = inputValue.trim().length > 0 ? 20 : 48;
56471
+ const newHeight = Math.max(minHeightWithText, Math.min(scrollHeight, 120));
56472
+ textarea.style.height = "".concat(newHeight, "px");
56473
+ }
56474
+ }, [inputValue]);
56475
+ const handleKeyPress = event => {
56476
+ if (event.key === "Enter" && !event.shiftKey) {
56477
+ event.preventDefault();
56478
+ if (isVoiceSessionActive && onBidiSubmit) {
56479
+ onBidiSubmit();
56480
+ } else {
56481
+ onSend();
56482
+ }
56483
+ }
56484
+ };
56485
+ const handleChange = event => {
56486
+ setInputValue(event.target.value);
56487
+ };
56488
+ const isDisabled = !inputValue.trim() || isLoading;
56489
+ const separatorColor = isDark ? "#374151" : "#e5e7eb";
56490
+ return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56491
+ className: "px-5 py-4 border-t flex-shrink-0 ".concat(isDark ? "border-gray-700 bg-gray-900" : "border-gray-200 bg-white"),
56492
+ style: {
56493
+ borderTopColor: separatorColor,
56494
+ borderTopWidth: "1px",
56495
+ borderTopStyle: "solid"
56496
+ },
56497
+ children: [/*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56498
+ className: "flex items-center gap-1.5",
56499
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
56500
+ className: "flex-1 flex items-center min-h-[48px] ".concat(isDark ? "bg-gray-800" : "bg-gray-50", " rounded-2xl border transition-all duration-200"),
56501
+ style: {
56502
+ borderColor: inputValue ? primaryColor : isDark ? "#374151" : "#e5e7eb",
56503
+ borderWidth: inputValue ? "2px" : "1px"
56504
+ },
56505
+ children: /*#__PURE__*/jsxRuntimeExports.jsx("textarea", {
56506
+ id: "chat-widget-input",
56507
+ ref: textareaRef,
56508
+ value: inputValue,
56509
+ onChange: handleChange,
56510
+ onKeyDown: handleKeyPress,
56511
+ placeholder: placeholder || "Type your message...",
56512
+ rows: 1,
56513
+ disabled: isLoading || isVoiceSessionActive && voiceStatus !== "connected",
56514
+ dir: isRtl ? "rtl" : "ltr",
56515
+ className: "w-full px-4 bg-transparent rounded-xl leading-normal ".concat(isDark ? "text-gray-100" : "text-gray-900", " placeholder-gray-400 resize-none outline-none scrollbar-hide"),
56516
+ style: {
56517
+ height: "auto",
56518
+ maxHeight: "120px",
56519
+ minHeight: "48px",
56520
+ paddingTop: inputValue.trim().length > 0 ? "12px" : "0px",
56521
+ paddingBottom: inputValue.trim().length > 0 ? "12px" : "0px",
56522
+ lineHeight: inputValue.trim().length > 0 ? "1.4" : "48px",
56523
+ fontFamily: fontFamily || "Inter",
56524
+ fontSize: fontSize || "14px",
56525
+ color: textColor || (isDark ? "#F5F5F5" : "#1A1A1A"),
56526
+ textAlign: isRtl ? "right" : "left",
56527
+ direction: isRtl ? "rtl" : "ltr",
56528
+ wordSpacing: "0",
56529
+ letterSpacing: "0"
56530
+ }
56531
+ })
56532
+ }), enableVoiceInteraction && /*#__PURE__*/jsxRuntimeExports.jsx("button", {
56533
+ type: "button",
56534
+ onClick: onVoiceToggle,
56535
+ className: "h-10 w-10 rounded-xl flex items-center justify-center transition-all duration-200 shadow-md ".concat(isVoiceSessionActive ? "bg-red-500 text-white hover:bg-red-600" : isDark ? "bg-gray-800 text-gray-200 hover:bg-gray-700" : "bg-white text-gray-600 hover:bg-gray-100", " ").concat(voiceStatus === "connecting" ? "opacity-60 cursor-not-allowed shadow-none" : ""),
56536
+ disabled: voiceStatus === "connecting",
56537
+ children: voiceStatus === "connecting" ? /*#__PURE__*/jsxRuntimeExports.jsx(Loader2, {
56538
+ className: "w-4 h-4 animate-spin"
56539
+ }) : /*#__PURE__*/jsxRuntimeExports.jsx(AudioLines, {
56540
+ className: "w-4 h-4"
56541
+ })
56542
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("button", {
56543
+ onClick: isVoiceSessionActive && onBidiSubmit ? onBidiSubmit : onSend,
56544
+ disabled: isDisabled || isVoiceSessionActive && voiceStatus !== "connected",
56545
+ className: "h-10 w-10 rounded-xl transition-all duration-200 flex items-center justify-center shadow-md disabled:opacity-40 disabled:cursor-not-allowed disabled:shadow-none ".concat(inputValue.trim() ? "scale-100" : "scale-95"),
56546
+ style: {
56547
+ backgroundColor: inputValue.trim() ? primaryColor : isDark ? "#374151" : "#e5e7eb",
56548
+ color: inputValue.trim() ? "white" : "#9ca3af"
56549
+ },
56550
+ "aria-label": "Send message",
56551
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(Send, {
56552
+ className: "w-4 h-4 transition-transform",
56553
+ style: isRtl ? {
56554
+ transform: "rotate(-90deg)"
56555
+ } : undefined
56556
+ })
56557
+ })]
56558
+ }), isVoiceSessionActive && /*#__PURE__*/jsxRuntimeExports.jsxs("p", {
56559
+ className: "text-[11px] mt-2 px-1 ".concat(voiceStatus === "connected" ? "text-green-500" : "text-gray-500", " flex items-center gap-1"),
56560
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("span", {
56561
+ className: "w-1.5 h-1.5 rounded-full bg-current inline-flex"
56562
+ }), voiceStatus === "connected" ? "Voice session live — speak or type to converse." : "Connecting to voice session..."]
56563
+ }), voiceError && /*#__PURE__*/jsxRuntimeExports.jsx("p", {
56564
+ className: "text-[11px] mt-1 px-1 text-red-500",
56565
+ children: voiceError
56566
+ }), disclaimerText && disclaimerPosition === "footer" && /*#__PURE__*/jsxRuntimeExports.jsx("p", {
56567
+ className: "text-xs mt-2 text-center",
56568
+ style: {
56569
+ color: isDark ? "#FFFFFF" : "#000000",
56570
+ fontFamily: fontFamily || "Inter",
56571
+ fontSize: fontSize ? "calc(".concat(fontSize, " - 2px)") : "12px",
56572
+ direction: isRtl ? "rtl" : "ltr"
56573
+ },
56574
+ dir: isRtl ? "rtl" : "ltr",
56575
+ children: disclaimerText
56576
+ })]
56577
+ });
56578
+ };
56579
+
56580
+ const LOGO_MAX_HEIGHT = "3.5rem";
56581
+ const LOGO_MAX_HEIGHT_COMPACT = "2.5rem";
56582
+ const LOGO_MAX_WIDTH_DEFAULT = "4.5rem";
56583
+ const LOGO_MAX_WIDTH_DEFAULT_COMPACT = "3rem";
56584
+ const LOGO_MAX_WIDTH_WIDE = "9rem";
56585
+ const LOGO_MAX_WIDTH_WIDE_COMPACT = "5.5rem";
56586
+ const STATUS_DOT_SIZE = "0.8rem";
56587
+ const STATUS_DOT_SIZE_COMPACT = "0.65rem";
56588
+ const STATUS_DOT_BORDER_WIDTH = "2px";
56589
+ const STATUS_DOT_BORDER_WIDTH_COMPACT = "1.5px";
56590
+ const STATUS_DOT_DEFAULT_COLOR = "#34d399";
56591
+ const STATUS_DOT_OFFSET = "38%";
56592
+ const STATUS_DOT_OFFSET_COMPACT = "22%";
56593
+ const LogoImage = _ref => {
56594
+ let {
56595
+ src,
56596
+ showStatusDot = false,
56597
+ statusColor = STATUS_DOT_DEFAULT_COLOR,
56598
+ size = "default"
56599
+ } = _ref;
56600
+ const [aspect, setAspect] = reactExports.useState("square");
56601
+ const isCompact = size === "compact";
56602
+ reactExports.useEffect(() => {
56603
+ if (typeof window === "undefined" || !src) {
56604
+ return;
56605
+ }
56606
+ const img = new Image();
56607
+ img.src = src;
56608
+ img.onload = () => {
56609
+ if (img.width > img.height) {
56610
+ setAspect("wide");
56611
+ } else if (img.height > img.width) {
56612
+ setAspect("tall");
56613
+ } else {
56614
+ setAspect("square");
56615
+ }
56616
+ };
56617
+ }, [src]);
56618
+ const containerStyle = {
56619
+ position: "relative",
56620
+ display: "inline-flex"
56621
+ };
56622
+ const imageStyle = {
56623
+ display: "block",
56624
+ objectFit: "cover",
56625
+ height: "100%",
56626
+ width: "100%",
56627
+ // borderRadius:'50%',
56628
+ maxHeight: isCompact ? LOGO_MAX_HEIGHT_COMPACT : LOGO_MAX_HEIGHT,
56629
+ maxWidth: isCompact ? LOGO_MAX_WIDTH_DEFAULT_COMPACT : LOGO_MAX_WIDTH_DEFAULT
56630
+ };
56631
+ if (aspect === "wide") {
56632
+ imageStyle.maxWidth = isCompact ? LOGO_MAX_WIDTH_WIDE_COMPACT : LOGO_MAX_WIDTH_WIDE;
56633
+ }
56634
+ if (aspect === "tall") {
56635
+ imageStyle.maxHeight = isCompact ? "3rem" : "4rem";
56636
+ imageStyle.maxWidth = isCompact ? "3rem" : "4rem";
56637
+ }
56638
+ return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56639
+ style: containerStyle,
56640
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("img", {
56641
+ src: src,
56642
+ alt: "Logo",
56643
+ style: imageStyle,
56644
+ loading: "lazy"
56645
+ }), showStatusDot && /*#__PURE__*/jsxRuntimeExports.jsx("span", {
56646
+ style: {
56647
+ position: "absolute",
56648
+ bottom: 0,
56649
+ right: 0,
56650
+ width: isCompact ? STATUS_DOT_SIZE_COMPACT : STATUS_DOT_SIZE,
56651
+ height: isCompact ? STATUS_DOT_SIZE_COMPACT : STATUS_DOT_SIZE,
56652
+ borderRadius: "9999px",
56653
+ backgroundColor: statusColor,
56654
+ border: "".concat(isCompact ? STATUS_DOT_BORDER_WIDTH_COMPACT : STATUS_DOT_BORDER_WIDTH, " solid #ffffff"),
56655
+ transform: "translate(".concat(isCompact ? STATUS_DOT_OFFSET_COMPACT : STATUS_DOT_OFFSET, ", ").concat(isCompact ? STATUS_DOT_OFFSET_COMPACT : STATUS_DOT_OFFSET, ")"),
56656
+ pointerEvents: "none"
56657
+ }
56658
+ })]
56659
+ });
56660
+ };
56661
+
56662
+ const ChatHeader = _ref => {
56663
+ let {
56664
+ conciergeName,
56665
+ companyName,
56666
+ companyLogo,
56667
+ primaryColor,
56668
+ isDark,
56669
+ isMinimized = false,
56670
+ textColor,
56671
+ fontFamily,
56672
+ fontSize,
56673
+ onToggleTheme,
56674
+ onMinimize,
56675
+ onClose,
56676
+ headerTextBold = false,
56677
+ headerTextItalic = false
56678
+ } = _ref;
56679
+ useGoogleFont(fontFamily);
56680
+ const agentData = {
56681
+ fontFamily,
56682
+ fontSize,
56683
+ headerTextBold,
56684
+ headerTextItalic,
56685
+ textColor
56686
+ };
56687
+ return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56688
+ className: "text-white ".concat(isMinimized ? "px-4 py-2.5" : "px-5 py-4", " flex items-center justify-between relative overflow-hidden rounded-t-2xl"),
56689
+ style: {
56690
+ background: "linear-gradient(135deg, ".concat(primaryColor, " 0%, ").concat(primaryColor, "dd 100%)"),
56691
+ fontFamily
56692
+ },
56693
+ children: [/*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56694
+ className: "flex gap-2 w-96",
56695
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
56696
+ className: "absolute inset-0 bg-gradient-to-br from-white/10 to-transparent pointer-events-none"
56697
+ }), companyLogo && /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56698
+ className: "w-[48px] h-[48px] rounded-full overflow-hidden flex items-center justify-center bg-white/10",
56699
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(LogoImage, {
56700
+ src: companyLogo
56701
+ })
56702
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56703
+ className: "flex items-center gap-2 ".concat(!companyLogo ? "px-3" : ""),
56704
+ style: {
56705
+ fontFamily: (agentData === null || agentData === void 0 ? void 0 : agentData.fontFamily) || "Inter"
56706
+ },
56707
+ children: /*#__PURE__*/jsxRuntimeExports.jsx("h3", {
56708
+ className: cn("font-semibold leading-tight", (agentData === null || agentData === void 0 ? void 0 : agentData.headerTextBold) && "font-bold", (agentData === null || agentData === void 0 ? void 0 : agentData.headerTextItalic) && "italic"),
56709
+ style: {
56710
+ fontSize: agentData !== null && agentData !== void 0 && agentData.fontSize ? "calc(".concat(agentData.fontSize, " + 2px)") : "15px",
56711
+ color: (agentData === null || agentData === void 0 ? void 0 : agentData.textColor) || "#FFFFFF"
56712
+ },
56713
+ children: companyName
56714
+ })
56715
+ })]
56716
+ }), /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56717
+ className: "flex items-center gap-0 relative z-10 text-white flex-shrink-0 ml-1",
56718
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("span", {
56719
+ role: "button",
56720
+ tabIndex: 0,
56721
+ "aria-label": "Toggle theme",
56722
+ onClick: onToggleTheme,
56723
+ onKeyDown: event => {
56724
+ if (event.key === "Enter" || event.key === " ") {
56725
+ event.preventDefault();
56726
+ onToggleTheme === null || onToggleTheme === void 0 || onToggleTheme(event);
56727
+ }
56728
+ },
56729
+ className: "inline-flex h-7 w-7 items-center justify-center cursor-pointer transition-opacity duration-200 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 rounded-full",
56730
+ children: isDark ? /*#__PURE__*/jsxRuntimeExports.jsx(Sun, {
56731
+ className: "w-3.5 h-3.5",
56732
+ strokeWidth: 1.6
56733
+ }) : /*#__PURE__*/jsxRuntimeExports.jsx(Moon, {
56734
+ className: "w-3.5 h-3.5",
56735
+ strokeWidth: 1.6
56736
+ })
56737
+ }), onMinimize && /*#__PURE__*/jsxRuntimeExports.jsx("span", {
56738
+ role: "button",
56739
+ tabIndex: 0,
56740
+ "aria-label": isMinimized ? "Expand" : "Minimize",
56741
+ onClick: onMinimize,
56742
+ onKeyDown: event => {
56743
+ if (event.key === "Enter" || event.key === " ") {
56744
+ event.preventDefault();
56745
+ onMinimize === null || onMinimize === void 0 || onMinimize(event);
56746
+ }
56747
+ },
56748
+ className: "inline-flex h-7 w-7 items-center justify-center cursor-pointer transition-opacity duration-200 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 rounded-full",
56749
+ children: isMinimized ? /*#__PURE__*/jsxRuntimeExports.jsx(Maximize2, {
56750
+ className: "w-3.5 h-3.5",
56751
+ strokeWidth: 1.6
56752
+ }) : /*#__PURE__*/jsxRuntimeExports.jsx(Minimize2, {
56753
+ className: "w-3.5 h-3.5",
56754
+ strokeWidth: 1.6
56755
+ })
56756
+ }), /*#__PURE__*/jsxRuntimeExports.jsx("span", {
56757
+ role: "button",
56758
+ tabIndex: 0,
56759
+ "aria-label": "Close",
56760
+ onClick: onClose,
56761
+ onKeyDown: event => {
56762
+ if (event.key === "Enter" || event.key === " ") {
56763
+ event.preventDefault();
56764
+ onClose === null || onClose === void 0 || onClose(event);
56765
+ }
56766
+ },
56767
+ className: "inline-flex h-7 w-7 items-center justify-center cursor-pointer transition-opacity duration-200 hover:opacity-80 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/40 rounded-full",
56768
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(X, {
56769
+ className: "w-3.5 h-3.5",
56770
+ strokeWidth: 1.6
56771
+ })
56772
+ })]
56773
+ })]
56774
+ });
56775
+ };
56776
+
56777
+ /**
56778
+ * Auto-scroll hook that scrolls to bottom when dependencies change
56779
+ */
56780
+ function useAutoScroll(containerRef) {
56781
+ let dependencies = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
56782
+ let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
56783
+ const {
56784
+ enabled = true,
56785
+ threshold = 30,
56786
+ behavior = "smooth",
56787
+ delay = 0
56788
+ } = options;
56789
+ const shouldScrollRef = reactExports.useRef(true);
56790
+ reactExports.useRef(0);
56791
+ reactExports.useEffect(() => {
56792
+ if (!enabled || !containerRef.current) {
56793
+ return;
56794
+ }
56795
+ const container = containerRef.current;
56796
+ const checkScrollPosition = () => {
56797
+ const distanceFromBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
56798
+ shouldScrollRef.current = distanceFromBottom < threshold;
56799
+ };
56800
+ const handleScroll = () => {
56801
+ checkScrollPosition();
56802
+ };
56803
+ container.addEventListener("scroll", handleScroll, {
56804
+ passive: true
56805
+ });
56806
+ checkScrollPosition();
56807
+ const scrollToBottom = () => {
56808
+ if (shouldScrollRef.current && container) {
56809
+ container.scrollTo({
56810
+ top: container.scrollHeight,
56811
+ behavior
56812
+ });
56813
+ }
56814
+ };
56815
+ const timeoutId = setTimeout(scrollToBottom, delay);
56816
+ return () => {
56817
+ container.removeEventListener("scroll", handleScroll);
56818
+ clearTimeout(timeoutId);
56819
+ };
56820
+ }, [containerRef, enabled, threshold, behavior, delay, ...dependencies]);
56821
+ }
56822
+
55964
56823
  const DEFAULT_AGENT_BUBBLE_COLOR = "#ffffff";
55965
56824
  const DEFAULT_USER_MESSAGE_COLOR = "#f5f5f5";
55966
56825
  const DEFAULT_TEXT_COLOR = "#000000";
@@ -55989,6 +56848,7 @@
55989
56848
  isDark,
55990
56849
  primaryColor,
55991
56850
  textColor,
56851
+ conciergeName,
55992
56852
  companyName,
55993
56853
  companyLogo,
55994
56854
  messages,
@@ -56005,23 +56865,38 @@
56005
56865
  quickQuestionsLayout = "vertical",
56006
56866
  agentMessageBubbleColor,
56007
56867
  userMessageBoxColor,
56868
+ assistantTextColor,
56869
+ userTextColor,
56008
56870
  fontFamily = "Inter",
56009
56871
  fontSize = "14px",
56010
- defaultLanguage = "en"
56872
+ defaultLanguage = "en",
56873
+ onDecision,
56874
+ headerTextBold = false,
56875
+ headerTextItalic = false,
56876
+ enableVoiceInteraction = false,
56877
+ isVoiceSessionActive = false,
56878
+ voiceStatus = "idle",
56879
+ voiceError = null,
56880
+ onVoiceToggle,
56881
+ onBidiSubmit,
56882
+ disclaimerText,
56883
+ disclaimerPosition = "top",
56884
+ apiBaseUrl,
56885
+ apiKey,
56886
+ organizationId
56011
56887
  } = _ref;
56012
56888
  useGoogleFont(fontFamily);
56889
+ const messagesContainerRef = reactExports.useRef(null);
56890
+
56891
+ // Auto-scroll hook
56892
+ useAutoScroll(messagesContainerRef, [messages, isLoading], {
56893
+ enabled: true,
56894
+ threshold: 30,
56895
+ behavior: "smooth",
56896
+ delay: 0
56897
+ });
56013
56898
  const defaultLanguageCode = defaultLanguage === null || defaultLanguage === void 0 || (_defaultLanguage$toLo = defaultLanguage.toLowerCase) === null || _defaultLanguage$toLo === void 0 ? void 0 : _defaultLanguage$toLo.call(defaultLanguage);
56014
56899
  const isRtlLanguage = Boolean(defaultLanguageCode && defaultLanguageCode.startsWith("ar"));
56015
- const basePrimaryColor = primaryColor || "#2563eb";
56016
- const containerStyle = reactExports.useMemo(() => {
56017
- if (isDark) {
56018
- return undefined;
56019
- }
56020
- return {
56021
- backgroundColor: basePrimaryColor,
56022
- borderColor: basePrimaryColor
56023
- };
56024
- }, [basePrimaryColor, isDark]);
56025
56900
  const resolvedAgentBubbleColor = reactExports.useMemo(() => {
56026
56901
  if (isDark) {
56027
56902
  if (isDefaultColor(agentMessageBubbleColor, DEFAULT_AGENT_BUBBLE_COLOR)) {
@@ -56056,50 +56931,56 @@
56056
56931
  return null;
56057
56932
  }
56058
56933
  return /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56059
- className: "mb-4 ".concat(isDark ? "bg-gray-900 border-gray-700" : "border-transparent", " rounded-2xl shadow-2xl border transition-all duration-300 ").concat(isMinimized ? "w-80" : "w-[380px] h-[650px]", " flex flex-col overflow-hidden min-h-0"),
56060
- style: containerStyle,
56061
- children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
56062
- className: "flex-shrink-0",
56063
- children: /*#__PURE__*/jsxRuntimeExports.jsx(ChatHeader, {
56064
- companyName: companyName,
56065
- companyLogo: companyLogo,
56066
- primaryColor: primaryColor,
56067
- isDark: isDark,
56068
- isMinimized: isMinimized,
56069
- textColor: resolvedTextColor,
56070
- fontFamily: fontFamily,
56071
- fontSize: fontSize,
56072
- onToggleTheme: onToggleTheme,
56073
- onMinimize: onMinimize,
56074
- onClose: onClose
56075
- })
56076
- }), !isMinimized && /*#__PURE__*/jsxRuntimeExports.jsxs("div", {
56077
- className: "flex-1 flex flex-col ".concat(isDark ? "bg-gray-900" : "bg-white", " min-h-0"),
56078
- children: [/*#__PURE__*/jsxRuntimeExports.jsx(MessageList, {
56079
- messages: messages,
56080
- isDark: isDark,
56081
- primaryColor: primaryColor,
56082
- isLoading: isLoading,
56083
- agentMessageBubbleColor: resolvedAgentBubbleColor,
56084
- userMessageBoxColor: resolvedUserMessageColor,
56085
- textColor: resolvedTextColor,
56086
- fontFamily: fontFamily,
56087
- fontSize: fontSize,
56088
- formatTime: formatTime,
56089
- isRtl: isRtlLanguage
56090
- }), shouldShowQuickQuestions && /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56091
- className: "px-5 pb-4 flex-shrink-0",
56092
- children: /*#__PURE__*/jsxRuntimeExports.jsx(QuickQuestions, {
56093
- questions: quickQuestions,
56934
+ className: "".concat(isMinimized ? "w-80" : "w-[380px]", " max-w-[92vw] flex-shrink-0 border rounded-2xl shadow-2xl bg-white border-gray-200 flex flex-col ").concat(isMinimized ? "h-auto" : "h-[650px] max-h-[85vh]", " overflow-hidden z-50 ").concat(isDark ? "bg-gray-900 border-gray-700" : ""),
56935
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx(ChatHeader, {
56936
+ conciergeName: conciergeName,
56937
+ companyName: companyName,
56938
+ companyLogo: companyLogo,
56939
+ primaryColor: primaryColor,
56940
+ isDark: isDark,
56941
+ isMinimized: isMinimized,
56942
+ textColor: resolvedTextColor,
56943
+ fontFamily: fontFamily,
56944
+ fontSize: fontSize,
56945
+ onToggleTheme: onToggleTheme,
56946
+ onMinimize: onMinimize,
56947
+ onClose: onClose,
56948
+ headerTextBold: headerTextBold,
56949
+ headerTextItalic: headerTextItalic
56950
+ }), !isMinimized && /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
56951
+ children: [/*#__PURE__*/jsxRuntimeExports.jsx("div", {
56952
+ ref: messagesContainerRef,
56953
+ className: "chat-messages flex-1 overflow-y-auto px-5 py-4 space-y-4 ".concat(isDark ? "bg-gray-900" : "bg-gradient-to-b from-gray-50/50 to-white", " break-words"),
56954
+ children: /*#__PURE__*/jsxRuntimeExports.jsx(MessageList, {
56955
+ messages: messages,
56094
56956
  isDark: isDark,
56095
56957
  primaryColor: primaryColor,
56096
- onQuestionClick: onQuickQuestion !== null && onQuickQuestion !== void 0 ? onQuickQuestion : onDirectSend,
56097
- isTyping: isLoading,
56098
- layout: quickQuestionsLayout,
56958
+ isLoading: isLoading,
56959
+ agentMessageBubbleColor: agentMessageBubbleColor,
56960
+ userMessageBoxColor: userMessageBoxColor,
56961
+ textColor: textColor,
56962
+ assistantTextColor: assistantTextColor,
56963
+ userTextColor: userTextColor,
56099
56964
  fontFamily: fontFamily,
56100
56965
  fontSize: fontSize,
56101
- textColor: resolvedTextColor,
56102
- isRtl: isRtlLanguage
56966
+ formatTime: formatTime,
56967
+ isRtl: isRtlLanguage,
56968
+ quickQuestions: quickQuestions,
56969
+ shouldShowQuickQuestions: shouldShowQuickQuestions,
56970
+ onQuickQuestion: onQuickQuestion !== null && onQuickQuestion !== void 0 ? onQuickQuestion : onDirectSend,
56971
+ quickQuestionsLayout: quickQuestionsLayout,
56972
+ onDecision: onDecision,
56973
+ disclaimerText: disclaimerText,
56974
+ disclaimerPosition: disclaimerPosition,
56975
+ resolvedAgentBubbleColor: resolvedAgentBubbleColor,
56976
+ resolvedUserMessageColor: resolvedUserMessageColor,
56977
+ resolvedTextColor: resolvedTextColor,
56978
+ isVoiceSessionActive: isVoiceSessionActive,
56979
+ voiceStatus: voiceStatus,
56980
+ messagesContainerRef: messagesContainerRef,
56981
+ companyLogo: companyLogo,
56982
+ conciergeName: conciergeName,
56983
+ companyName: companyName
56103
56984
  })
56104
56985
  }), /*#__PURE__*/jsxRuntimeExports.jsx(ChatInput, {
56105
56986
  inputValue: inputValue,
@@ -56114,7 +56995,15 @@
56114
56995
  textColor: resolvedTextColor,
56115
56996
  fontFamily: fontFamily,
56116
56997
  fontSize: fontSize,
56117
- isRtl: isRtlLanguage
56998
+ isRtl: isRtlLanguage,
56999
+ enableVoiceInteraction: enableVoiceInteraction,
57000
+ isVoiceSessionActive: isVoiceSessionActive,
57001
+ voiceStatus: voiceStatus,
57002
+ voiceError: voiceError,
57003
+ onVoiceToggle: onVoiceToggle,
57004
+ onBidiSubmit: onBidiSubmit,
57005
+ disclaimerText: disclaimerText,
57006
+ disclaimerPosition: disclaimerPosition
56118
57007
  })]
56119
57008
  })]
56120
57009
  });
@@ -56125,7 +57014,8 @@
56125
57014
  isOpen,
56126
57015
  isDark,
56127
57016
  primaryColor,
56128
- onToggle
57017
+ onToggle,
57018
+ textColor
56129
57019
  } = _ref;
56130
57020
  return /*#__PURE__*/jsxRuntimeExports.jsxs("button", {
56131
57021
  onClick: onToggle,
@@ -56143,7 +57033,10 @@
56143
57033
  className: "w-7 h-7"
56144
57034
  }) : /*#__PURE__*/jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, {
56145
57035
  children: [/*#__PURE__*/jsxRuntimeExports.jsx(MessageCircle, {
56146
- className: "w-7 h-7"
57036
+ className: "w-7 h-7",
57037
+ style: {
57038
+ color: textColor
57039
+ }
56147
57040
  }), /*#__PURE__*/jsxRuntimeExports.jsx("div", {
56148
57041
  className: "absolute -top-1 -right-1 w-5 h-5 bg-red-500 rounded-full flex items-center justify-center text-[10px] font-bold border-2 border-white animate-pulse",
56149
57042
  children: "1"
@@ -56159,6 +57052,7 @@
56159
57052
  primaryColor = "#2563eb",
56160
57053
  position = "bottom-right",
56161
57054
  companyName = "Support Team",
57055
+ conciergeName,
56162
57056
  companyLogo = null,
56163
57057
  welcomeMessage = "Hi! How can we help?",
56164
57058
  quickQuestions = [],
@@ -56166,6 +57060,8 @@
56166
57060
  textColor = "#000000",
56167
57061
  agentMessageBubbleColor,
56168
57062
  userMessageBoxColor,
57063
+ assistantTextColor,
57064
+ userTextColor,
56169
57065
  fontFamily = "Inter",
56170
57066
  fontSize = "14px",
56171
57067
  defaultLanguage = "en",
@@ -56175,7 +57071,12 @@
56175
57071
  autoOpen = false,
56176
57072
  openDelay = 0,
56177
57073
  locale = "en",
56178
- className = ""
57074
+ className = "",
57075
+ headerTextBold = false,
57076
+ headerTextItalic = false,
57077
+ enableVoiceInteraction = false,
57078
+ disclaimerText,
57079
+ disclaimerPosition = "top"
56179
57080
  } = _ref;
56180
57081
  const {
56181
57082
  isOpen,
@@ -56192,7 +57093,13 @@
56192
57093
  toggleChat,
56193
57094
  toggleTheme,
56194
57095
  toggleMinimize,
56195
- closeChat
57096
+ closeChat,
57097
+ handleDecision,
57098
+ isVoiceSessionActive,
57099
+ voiceStatus,
57100
+ voiceError,
57101
+ handleVoiceToggle,
57102
+ sendBidiTextMessage
56196
57103
  } = useChatState({
56197
57104
  welcomeMessage,
56198
57105
  quickQuestions,
@@ -56201,7 +57108,8 @@
56201
57108
  organizationId,
56202
57109
  autoOpen,
56203
57110
  openDelay,
56204
- darkMode
57111
+ darkMode,
57112
+ enableVoiceInteraction
56205
57113
  });
56206
57114
 
56207
57115
  // Set CSS variables for theming
@@ -56232,6 +57140,7 @@
56232
57140
  primaryColor: primaryColor,
56233
57141
  textColor: textColor,
56234
57142
  companyName: companyName,
57143
+ conciergeName: conciergeName,
56235
57144
  companyLogo: companyLogo,
56236
57145
  messages: messages,
56237
57146
  isLoading: isLoading,
@@ -56247,14 +57156,31 @@
56247
57156
  quickQuestionsLayout: quickQuestionsLayout,
56248
57157
  agentMessageBubbleColor: agentMessageBubbleColor,
56249
57158
  userMessageBoxColor: userMessageBoxColor,
57159
+ assistantTextColor: assistantTextColor,
57160
+ userTextColor: userTextColor,
56250
57161
  fontFamily: fontFamily,
56251
57162
  fontSize: fontSize,
56252
- defaultLanguage: defaultLanguage || locale
56253
- }), /*#__PURE__*/jsxRuntimeExports.jsx(ToggleButton, {
57163
+ defaultLanguage: defaultLanguage || locale,
57164
+ onDecision: handleDecision,
57165
+ headerTextBold: headerTextBold,
57166
+ headerTextItalic: headerTextItalic,
57167
+ enableVoiceInteraction: enableVoiceInteraction,
57168
+ isVoiceSessionActive: isVoiceSessionActive,
57169
+ voiceStatus: voiceStatus,
57170
+ voiceError: voiceError,
57171
+ onVoiceToggle: handleVoiceToggle,
57172
+ onBidiSubmit: () => sendBidiTextMessage(inputValue),
57173
+ disclaimerText: disclaimerText,
57174
+ disclaimerPosition: disclaimerPosition,
57175
+ apiBaseUrl: apiBaseUrl,
57176
+ apiKey: apiKey,
57177
+ organizationId: organizationId
57178
+ }), !isOpen && /*#__PURE__*/jsxRuntimeExports.jsx(ToggleButton, {
56254
57179
  isOpen: isOpen,
56255
57180
  isDark: isDark,
56256
57181
  primaryColor: primaryColor,
56257
- onToggle: toggleChat
57182
+ onToggle: toggleChat,
57183
+ textColor: textColor
56258
57184
  })]
56259
57185
  });
56260
57186
  };
@@ -56269,13 +57195,16 @@
56269
57195
  primaryColor: '#2563eb',
56270
57196
  position: 'bottom-right',
56271
57197
  companyName: 'Support Team',
57198
+ conciergeName: null,
56272
57199
  companyLogo: null,
56273
57200
  welcomeMessage: 'Hi! How can we help?',
56274
57201
  quickQuestions: [],
56275
57202
  quickQuestionsLayout: 'vertical',
56276
- textColor: '#000000',
57203
+ textColor: '#fcfcfc',
56277
57204
  agentMessageBubbleColor: undefined,
56278
57205
  userMessageBoxColor: undefined,
57206
+ assistantTextColor: undefined,
57207
+ userTextColor: undefined,
56279
57208
  fontFamily: 'Inter',
56280
57209
  fontSize: '14px',
56281
57210
  defaultLanguage: 'en',
@@ -56283,18 +57212,24 @@
56283
57212
  openDelay: 0,
56284
57213
  apiBaseUrl: null,
56285
57214
  apiKey: null,
57215
+ organizationId: null,
56286
57216
  sessionUrl: null,
56287
57217
  userId: null,
56288
57218
  userName: null,
56289
57219
  userEmail: null,
56290
57220
  locale: 'en',
57221
+ headerTextBold: false,
57222
+ headerTextItalic: false,
57223
+ enableVoiceInteraction: false,
57224
+ disclaimerText: undefined,
57225
+ disclaimerPosition: 'top',
56291
57226
  onOpen: null,
56292
57227
  onClose: null
56293
57228
  };
56294
57229
  const API = {
56295
- /**
56296
- * Initialize the chat widget
56297
- * @param {Object} options - Configuration options
57230
+ /**
57231
+ * Initialize the chat widget
57232
+ * @param {Object} options - Configuration options
56298
57233
  */
56299
57234
  init: async function () {
56300
57235
  let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
@@ -56334,17 +57269,17 @@
56334
57269
  }
56335
57270
  return API;
56336
57271
  },
56337
- /**
56338
- * Update configuration
56339
- * @param {Object} newConfig - New configuration options
57272
+ /**
57273
+ * Update configuration
57274
+ * @param {Object} newConfig - New configuration options
56340
57275
  */
56341
57276
  updateConfig: function (newConfig) {
56342
57277
  config = _objectSpread2(_objectSpread2({}, config), newConfig);
56343
57278
  this.init(config);
56344
57279
  return API;
56345
57280
  },
56346
- /**
56347
- * Open the chat widget
57281
+ /**
57282
+ * Open the chat widget
56348
57283
  */
56349
57284
  open: function () {
56350
57285
  // Trigger open by updating config
@@ -56355,8 +57290,8 @@
56355
57290
  if (config.onOpen) config.onOpen();
56356
57291
  return API;
56357
57292
  },
56358
- /**
56359
- * Close the chat widget
57293
+ /**
57294
+ * Close the chat widget
56360
57295
  */
56361
57296
  close: function () {
56362
57297
  this.updateConfig({
@@ -56365,8 +57300,8 @@
56365
57300
  if (config.onClose) config.onClose();
56366
57301
  return API;
56367
57302
  },
56368
- /**
56369
- * Toggle dark mode
57303
+ /**
57304
+ * Toggle dark mode
56370
57305
  */
56371
57306
  toggleTheme: function () {
56372
57307
  config.darkMode = !config.darkMode;
@@ -56375,8 +57310,8 @@
56375
57310
  });
56376
57311
  return API;
56377
57312
  },
56378
- /**
56379
- * Destroy the widget
57313
+ /**
57314
+ * Destroy the widget
56380
57315
  */
56381
57316
  destroy: function () {
56382
57317
  if (root) {
@@ -56391,8 +57326,8 @@
56391
57326
  config = {};
56392
57327
  return API;
56393
57328
  },
56394
- /**
56395
- * Get current configuration
57329
+ /**
57330
+ * Get current configuration
56396
57331
  */
56397
57332
  getConfig: function () {
56398
57333
  return _objectSpread2({}, config);