@schoolio/player 1.4.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +258 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +258 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -80,6 +80,23 @@ var QuizApiClient = class {
|
|
|
80
80
|
`/api/external/question-chat/${questionId}/${childId}`
|
|
81
81
|
);
|
|
82
82
|
}
|
|
83
|
+
async getTextToSpeech(text, voice = "nova") {
|
|
84
|
+
const headers = {
|
|
85
|
+
"Content-Type": "application/json"
|
|
86
|
+
};
|
|
87
|
+
if (this.authToken) {
|
|
88
|
+
headers["Authorization"] = `Bearer ${this.authToken}`;
|
|
89
|
+
}
|
|
90
|
+
const response = await fetch(`${this.baseUrl}/api/external/tts`, {
|
|
91
|
+
method: "POST",
|
|
92
|
+
headers,
|
|
93
|
+
body: JSON.stringify({ text, voice })
|
|
94
|
+
});
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
throw new Error(`TTS request failed: HTTP ${response.status}`);
|
|
97
|
+
}
|
|
98
|
+
return response.blob();
|
|
99
|
+
}
|
|
83
100
|
};
|
|
84
101
|
|
|
85
102
|
// src/utils.ts
|
|
@@ -482,9 +499,13 @@ var panelStyles = {
|
|
|
482
499
|
messageRow: {
|
|
483
500
|
display: "flex"
|
|
484
501
|
},
|
|
502
|
+
messageContent: {
|
|
503
|
+
display: "flex",
|
|
504
|
+
alignItems: "flex-end",
|
|
505
|
+
gap: "4px",
|
|
506
|
+
maxWidth: "85%"
|
|
507
|
+
},
|
|
485
508
|
userMessage: {
|
|
486
|
-
maxWidth: "85%",
|
|
487
|
-
marginLeft: "auto",
|
|
488
509
|
padding: "10px 14px",
|
|
489
510
|
borderRadius: "16px 16px 4px 16px",
|
|
490
511
|
backgroundColor: "#6721b0",
|
|
@@ -493,8 +514,6 @@ var panelStyles = {
|
|
|
493
514
|
lineHeight: 1.4
|
|
494
515
|
},
|
|
495
516
|
assistantMessage: {
|
|
496
|
-
maxWidth: "85%",
|
|
497
|
-
marginRight: "auto",
|
|
498
517
|
padding: "10px 14px",
|
|
499
518
|
borderRadius: "16px 16px 16px 4px",
|
|
500
519
|
backgroundColor: "#ffffff",
|
|
@@ -508,7 +527,8 @@ var panelStyles = {
|
|
|
508
527
|
borderTop: "1px solid #e2e8f0",
|
|
509
528
|
backgroundColor: "#ffffff",
|
|
510
529
|
display: "flex",
|
|
511
|
-
gap: "8px"
|
|
530
|
+
gap: "8px",
|
|
531
|
+
alignItems: "center"
|
|
512
532
|
},
|
|
513
533
|
input: {
|
|
514
534
|
flex: 1,
|
|
@@ -518,13 +538,11 @@ var panelStyles = {
|
|
|
518
538
|
fontSize: "14px",
|
|
519
539
|
outline: "none"
|
|
520
540
|
},
|
|
521
|
-
|
|
541
|
+
buttonBase: {
|
|
522
542
|
width: "40px",
|
|
523
543
|
height: "40px",
|
|
524
544
|
borderRadius: "50%",
|
|
525
545
|
border: "none",
|
|
526
|
-
backgroundColor: "#6721b0",
|
|
527
|
-
color: "#ffffff",
|
|
528
546
|
cursor: "pointer",
|
|
529
547
|
display: "flex",
|
|
530
548
|
alignItems: "center",
|
|
@@ -532,10 +550,47 @@ var panelStyles = {
|
|
|
532
550
|
fontSize: "16px",
|
|
533
551
|
transition: "all 0.2s ease"
|
|
534
552
|
},
|
|
553
|
+
sendButton: {
|
|
554
|
+
backgroundColor: "#6721b0",
|
|
555
|
+
color: "#ffffff"
|
|
556
|
+
},
|
|
535
557
|
sendButtonDisabled: {
|
|
536
558
|
backgroundColor: "#d1d5db",
|
|
537
559
|
cursor: "not-allowed"
|
|
538
560
|
},
|
|
561
|
+
micButton: {
|
|
562
|
+
backgroundColor: "#ffffff",
|
|
563
|
+
border: "1px solid #e2e8f0",
|
|
564
|
+
color: "#374151"
|
|
565
|
+
},
|
|
566
|
+
micButtonActive: {
|
|
567
|
+
backgroundColor: "#ef4444",
|
|
568
|
+
color: "#ffffff",
|
|
569
|
+
border: "none"
|
|
570
|
+
},
|
|
571
|
+
speakButton: {
|
|
572
|
+
width: "28px",
|
|
573
|
+
height: "28px",
|
|
574
|
+
borderRadius: "50%",
|
|
575
|
+
border: "none",
|
|
576
|
+
backgroundColor: "transparent",
|
|
577
|
+
color: "#9ca3af",
|
|
578
|
+
cursor: "pointer",
|
|
579
|
+
display: "flex",
|
|
580
|
+
alignItems: "center",
|
|
581
|
+
justifyContent: "center",
|
|
582
|
+
transition: "all 0.2s ease",
|
|
583
|
+
flexShrink: 0
|
|
584
|
+
},
|
|
585
|
+
speakButtonReady: {
|
|
586
|
+
color: "#22c55e"
|
|
587
|
+
},
|
|
588
|
+
speakButtonPlaying: {
|
|
589
|
+
color: "#6721b0"
|
|
590
|
+
},
|
|
591
|
+
speakButtonLoading: {
|
|
592
|
+
color: "#f97316"
|
|
593
|
+
},
|
|
539
594
|
loadingDots: {
|
|
540
595
|
display: "flex",
|
|
541
596
|
alignItems: "center",
|
|
@@ -573,6 +628,33 @@ var STARTER_PROMPTS = [
|
|
|
573
628
|
{ id: "word_help", label: "I don't understand a word", message: "I don't understand a word in this question. Can you help?" },
|
|
574
629
|
{ id: "explain_again", label: "Explain this again", message: "Can you explain this question to me again in a different way?" }
|
|
575
630
|
];
|
|
631
|
+
var MicIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
632
|
+
/* @__PURE__ */ jsx2("path", { d: "M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" }),
|
|
633
|
+
/* @__PURE__ */ jsx2("path", { d: "M19 10v2a7 7 0 0 1-14 0v-2" }),
|
|
634
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
635
|
+
] });
|
|
636
|
+
var MicOffIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
637
|
+
/* @__PURE__ */ jsx2("line", { x1: "2", x2: "22", y1: "2", y2: "22" }),
|
|
638
|
+
/* @__PURE__ */ jsx2("path", { d: "M18.89 13.23A7.12 7.12 0 0 0 19 12v-2" }),
|
|
639
|
+
/* @__PURE__ */ jsx2("path", { d: "M5 10v2a7 7 0 0 0 12 5" }),
|
|
640
|
+
/* @__PURE__ */ jsx2("path", { d: "M15 9.34V5a3 3 0 0 0-5.68-1.33" }),
|
|
641
|
+
/* @__PURE__ */ jsx2("path", { d: "M9 9v3a3 3 0 0 0 5.12 2.12" }),
|
|
642
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", x2: "12", y1: "19", y2: "22" })
|
|
643
|
+
] });
|
|
644
|
+
var VolumeIcon2 = () => /* @__PURE__ */ jsxs2("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
645
|
+
/* @__PURE__ */ jsx2("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
|
|
646
|
+
/* @__PURE__ */ jsx2("path", { d: "M15.54 8.46a5 5 0 0 1 0 7.07" }),
|
|
647
|
+
/* @__PURE__ */ jsx2("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14" })
|
|
648
|
+
] });
|
|
649
|
+
var SendIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
|
|
650
|
+
/* @__PURE__ */ jsx2("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
651
|
+
/* @__PURE__ */ jsx2("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
652
|
+
] });
|
|
653
|
+
var HelpIcon = () => /* @__PURE__ */ jsxs2("svg", { width: "48", height: "48", viewBox: "0 0 24 24", fill: "none", stroke: "#6721b0", strokeWidth: "2", children: [
|
|
654
|
+
/* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
|
|
655
|
+
/* @__PURE__ */ jsx2("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
656
|
+
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
657
|
+
] });
|
|
576
658
|
function QuestionChatPanel({
|
|
577
659
|
apiClient,
|
|
578
660
|
question,
|
|
@@ -587,13 +669,121 @@ function QuestionChatPanel({
|
|
|
587
669
|
const [isLoading, setIsLoading] = useState2(false);
|
|
588
670
|
const [chatId, setChatId] = useState2(null);
|
|
589
671
|
const [hoveredButton, setHoveredButton] = useState2(null);
|
|
672
|
+
const [isListening, setIsListening] = useState2(false);
|
|
673
|
+
const [speakingIndex, setSpeakingIndex] = useState2(null);
|
|
674
|
+
const [audioReadyMap, setAudioReadyMap] = useState2(/* @__PURE__ */ new Map());
|
|
590
675
|
const messagesContainerRef = useRef2(null);
|
|
591
676
|
const messagesEndRef = useRef2(null);
|
|
677
|
+
const recognitionRef = useRef2(null);
|
|
678
|
+
const audioRef = useRef2(null);
|
|
679
|
+
const audioCacheRef = useRef2(/* @__PURE__ */ new Map());
|
|
680
|
+
const isSpeechSupported = typeof window !== "undefined" && (window.SpeechRecognition || window.webkitSpeechRecognition);
|
|
592
681
|
const scrollToBottom = useCallback(() => {
|
|
593
682
|
if (messagesContainerRef.current) {
|
|
594
683
|
messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight;
|
|
595
684
|
}
|
|
596
685
|
}, []);
|
|
686
|
+
const preCacheAudio = useCallback(async (text) => {
|
|
687
|
+
if (audioCacheRef.current.has(text)) {
|
|
688
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, true));
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, false));
|
|
692
|
+
try {
|
|
693
|
+
const audioBlob = await apiClient.getTextToSpeech(text, "nova");
|
|
694
|
+
audioCacheRef.current.set(text, audioBlob);
|
|
695
|
+
setAudioReadyMap((prev) => new Map(prev).set(text, true));
|
|
696
|
+
} catch (error) {
|
|
697
|
+
console.error("Pre-cache TTS error:", error);
|
|
698
|
+
}
|
|
699
|
+
}, [apiClient]);
|
|
700
|
+
const startListening = useCallback(() => {
|
|
701
|
+
const SpeechRecognitionAPI = window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
702
|
+
if (!SpeechRecognitionAPI) {
|
|
703
|
+
console.log("Speech recognition not supported");
|
|
704
|
+
return;
|
|
705
|
+
}
|
|
706
|
+
const recognition = new SpeechRecognitionAPI();
|
|
707
|
+
recognition.continuous = true;
|
|
708
|
+
recognition.interimResults = true;
|
|
709
|
+
recognition.lang = "en-US";
|
|
710
|
+
recognition.onstart = () => {
|
|
711
|
+
console.log("Speech recognition started");
|
|
712
|
+
setIsListening(true);
|
|
713
|
+
};
|
|
714
|
+
recognition.onresult = (event) => {
|
|
715
|
+
let finalTranscript = "";
|
|
716
|
+
for (let i = 0; i < Object.keys(event.results).length; i++) {
|
|
717
|
+
const result = event.results[i];
|
|
718
|
+
if (result && result[0]) {
|
|
719
|
+
finalTranscript += result[0].transcript;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
if (finalTranscript) {
|
|
723
|
+
setInputValue(finalTranscript);
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
recognition.onerror = (event) => {
|
|
727
|
+
console.log("Speech recognition error:", event.error);
|
|
728
|
+
if (event.error === "not-allowed") {
|
|
729
|
+
alert("Microphone access was denied. Please allow microphone access and try again.");
|
|
730
|
+
}
|
|
731
|
+
setIsListening(false);
|
|
732
|
+
};
|
|
733
|
+
recognition.onend = () => {
|
|
734
|
+
console.log("Speech recognition ended");
|
|
735
|
+
setIsListening(false);
|
|
736
|
+
};
|
|
737
|
+
recognitionRef.current = recognition;
|
|
738
|
+
try {
|
|
739
|
+
recognition.start();
|
|
740
|
+
} catch (e) {
|
|
741
|
+
console.log("Failed to start recognition:", e);
|
|
742
|
+
setIsListening(false);
|
|
743
|
+
}
|
|
744
|
+
}, []);
|
|
745
|
+
const stopListening = useCallback(() => {
|
|
746
|
+
if (recognitionRef.current) {
|
|
747
|
+
recognitionRef.current.stop();
|
|
748
|
+
setIsListening(false);
|
|
749
|
+
}
|
|
750
|
+
}, []);
|
|
751
|
+
const speakMessage = useCallback(async (text, index) => {
|
|
752
|
+
if (audioRef.current) {
|
|
753
|
+
audioRef.current.pause();
|
|
754
|
+
audioRef.current = null;
|
|
755
|
+
}
|
|
756
|
+
if (speakingIndex === index) {
|
|
757
|
+
setSpeakingIndex(null);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
setSpeakingIndex(index);
|
|
761
|
+
try {
|
|
762
|
+
let audioBlob;
|
|
763
|
+
const cachedBlob = audioCacheRef.current.get(text);
|
|
764
|
+
if (cachedBlob) {
|
|
765
|
+
audioBlob = cachedBlob;
|
|
766
|
+
} else {
|
|
767
|
+
audioBlob = await apiClient.getTextToSpeech(text, "nova");
|
|
768
|
+
audioCacheRef.current.set(text, audioBlob);
|
|
769
|
+
}
|
|
770
|
+
const audioUrl = URL.createObjectURL(audioBlob);
|
|
771
|
+
const audio = new Audio(audioUrl);
|
|
772
|
+
audioRef.current = audio;
|
|
773
|
+
audio.onended = () => {
|
|
774
|
+
setSpeakingIndex(null);
|
|
775
|
+
URL.revokeObjectURL(audioUrl);
|
|
776
|
+
};
|
|
777
|
+
audio.onerror = () => {
|
|
778
|
+
setSpeakingIndex(null);
|
|
779
|
+
URL.revokeObjectURL(audioUrl);
|
|
780
|
+
};
|
|
781
|
+
await audio.play();
|
|
782
|
+
} catch (error) {
|
|
783
|
+
console.error("TTS error:", error);
|
|
784
|
+
setSpeakingIndex(null);
|
|
785
|
+
}
|
|
786
|
+
}, [speakingIndex, apiClient]);
|
|
597
787
|
useEffect2(() => {
|
|
598
788
|
scrollToBottom();
|
|
599
789
|
}, [messages, scrollToBottom]);
|
|
@@ -607,13 +797,14 @@ function QuestionChatPanel({
|
|
|
607
797
|
if (history.chatId && history.messages.length > 0) {
|
|
608
798
|
setChatId(history.chatId);
|
|
609
799
|
setMessages(history.messages);
|
|
800
|
+
history.messages.filter((m) => m.role === "assistant").forEach((m) => preCacheAudio(m.content));
|
|
610
801
|
}
|
|
611
802
|
} catch (err) {
|
|
612
803
|
console.error("Failed to load chat history:", err);
|
|
613
804
|
}
|
|
614
805
|
};
|
|
615
806
|
loadHistory();
|
|
616
|
-
}, [question.id, childId, apiClient]);
|
|
807
|
+
}, [question.id, childId, apiClient, preCacheAudio]);
|
|
617
808
|
const initializeChat = async () => {
|
|
618
809
|
if (chatId) return chatId;
|
|
619
810
|
try {
|
|
@@ -635,6 +826,9 @@ function QuestionChatPanel({
|
|
|
635
826
|
};
|
|
636
827
|
const sendMessage = async (messageText) => {
|
|
637
828
|
if (!messageText.trim() || isLoading) return;
|
|
829
|
+
if (isListening) {
|
|
830
|
+
stopListening();
|
|
831
|
+
}
|
|
638
832
|
setIsLoading(true);
|
|
639
833
|
const userMsg = {
|
|
640
834
|
role: "user",
|
|
@@ -654,15 +848,23 @@ function QuestionChatPanel({
|
|
|
654
848
|
questionContext: question,
|
|
655
849
|
childId
|
|
656
850
|
});
|
|
657
|
-
|
|
851
|
+
const assistantMessage = response.assistantMessage || {
|
|
852
|
+
role: "assistant",
|
|
853
|
+
content: "I'm here to help!",
|
|
854
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
855
|
+
};
|
|
856
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
857
|
+
preCacheAudio(assistantMessage.content);
|
|
658
858
|
} catch (err) {
|
|
659
859
|
console.error("Failed to send message:", err);
|
|
860
|
+
const content = "Sorry, I'm having trouble right now. Please try again!";
|
|
660
861
|
const errorMsg = {
|
|
661
862
|
role: "assistant",
|
|
662
|
-
content
|
|
863
|
+
content,
|
|
663
864
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
664
865
|
};
|
|
665
866
|
setMessages((prev) => [...prev, errorMsg]);
|
|
867
|
+
preCacheAudio(content);
|
|
666
868
|
} finally {
|
|
667
869
|
setIsLoading(false);
|
|
668
870
|
}
|
|
@@ -679,14 +881,14 @@ function QuestionChatPanel({
|
|
|
679
881
|
0%, 60%, 100% { transform: translateY(0); }
|
|
680
882
|
30% { transform: translateY(-4px); }
|
|
681
883
|
}
|
|
884
|
+
@keyframes pulse {
|
|
885
|
+
0%, 100% { opacity: 1; }
|
|
886
|
+
50% { opacity: 0.5; }
|
|
887
|
+
}
|
|
682
888
|
` }),
|
|
683
889
|
/* @__PURE__ */ jsx2("div", { style: panelStyles.header, children: /* @__PURE__ */ jsx2("span", { children: "Need Help?" }) }),
|
|
684
890
|
/* @__PURE__ */ jsx2("div", { ref: messagesContainerRef, style: panelStyles.messagesContainer, children: messages.length === 0 ? /* @__PURE__ */ jsxs2("div", { style: panelStyles.emptyState, children: [
|
|
685
|
-
/* @__PURE__ */ jsx2("div", { style: panelStyles.helperIcon, children: /* @__PURE__ */
|
|
686
|
-
/* @__PURE__ */ jsx2("circle", { cx: "12", cy: "12", r: "10" }),
|
|
687
|
-
/* @__PURE__ */ jsx2("path", { d: "M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" }),
|
|
688
|
-
/* @__PURE__ */ jsx2("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
|
|
689
|
-
] }) }),
|
|
891
|
+
/* @__PURE__ */ jsx2("div", { style: panelStyles.helperIcon, children: /* @__PURE__ */ jsx2(HelpIcon, {}) }),
|
|
690
892
|
/* @__PURE__ */ jsx2("div", { style: { fontSize: "14px", fontWeight: "500", marginBottom: "8px" }, children: "Hi! I'm your question helper" }),
|
|
691
893
|
/* @__PURE__ */ jsx2("div", { style: { fontSize: "13px", color: "#9ca3af" }, children: "Ask me if you need help understanding this question" }),
|
|
692
894
|
/* @__PURE__ */ jsx2("div", { style: { ...panelStyles.starterPrompts, marginTop: "16px" }, children: STARTER_PROMPTS.map((prompt) => /* @__PURE__ */ jsx2(
|
|
@@ -713,7 +915,26 @@ function QuestionChatPanel({
|
|
|
713
915
|
...panelStyles.messageRow,
|
|
714
916
|
justifyContent: msg.role === "user" ? "flex-end" : "flex-start"
|
|
715
917
|
},
|
|
716
|
-
children: /* @__PURE__ */
|
|
918
|
+
children: /* @__PURE__ */ jsxs2("div", { style: {
|
|
919
|
+
...panelStyles.messageContent,
|
|
920
|
+
flexDirection: msg.role === "user" ? "row-reverse" : "row"
|
|
921
|
+
}, children: [
|
|
922
|
+
/* @__PURE__ */ jsx2("div", { style: msg.role === "user" ? panelStyles.userMessage : panelStyles.assistantMessage, children: msg.content }),
|
|
923
|
+
msg.role === "assistant" && /* @__PURE__ */ jsx2(
|
|
924
|
+
"button",
|
|
925
|
+
{
|
|
926
|
+
style: {
|
|
927
|
+
...panelStyles.speakButton,
|
|
928
|
+
...speakingIndex === idx ? panelStyles.speakButtonPlaying : audioReadyMap.get(msg.content) === true ? panelStyles.speakButtonReady : audioReadyMap.get(msg.content) === false ? panelStyles.speakButtonLoading : {},
|
|
929
|
+
...audioReadyMap.get(msg.content) === false ? { animation: "pulse 1.5s ease-in-out infinite" } : {}
|
|
930
|
+
},
|
|
931
|
+
onClick: () => speakMessage(msg.content, idx),
|
|
932
|
+
"data-testid": `button-speak-${idx}`,
|
|
933
|
+
title: "Listen to this message",
|
|
934
|
+
children: /* @__PURE__ */ jsx2(VolumeIcon2, {})
|
|
935
|
+
}
|
|
936
|
+
)
|
|
937
|
+
] })
|
|
717
938
|
},
|
|
718
939
|
idx
|
|
719
940
|
)),
|
|
@@ -732,26 +953,38 @@ function QuestionChatPanel({
|
|
|
732
953
|
value: inputValue,
|
|
733
954
|
onChange: (e) => setInputValue(e.target.value),
|
|
734
955
|
onKeyPress: handleKeyPress,
|
|
735
|
-
placeholder: "Ask about this question...",
|
|
956
|
+
placeholder: isListening ? "Listening..." : "Ask about this question...",
|
|
736
957
|
style: panelStyles.input,
|
|
737
|
-
disabled: isLoading,
|
|
958
|
+
disabled: isLoading || isListening,
|
|
738
959
|
"data-testid": "input-chat-message"
|
|
739
960
|
}
|
|
740
961
|
),
|
|
962
|
+
isSpeechSupported && /* @__PURE__ */ jsx2(
|
|
963
|
+
"button",
|
|
964
|
+
{
|
|
965
|
+
onClick: isListening ? stopListening : startListening,
|
|
966
|
+
disabled: isLoading,
|
|
967
|
+
style: {
|
|
968
|
+
...panelStyles.buttonBase,
|
|
969
|
+
...isListening ? panelStyles.micButtonActive : panelStyles.micButton
|
|
970
|
+
},
|
|
971
|
+
"data-testid": "button-voice-input",
|
|
972
|
+
title: isListening ? "Stop listening" : "Speak your question",
|
|
973
|
+
children: isListening ? /* @__PURE__ */ jsx2(MicOffIcon, {}) : /* @__PURE__ */ jsx2(MicIcon, {})
|
|
974
|
+
}
|
|
975
|
+
),
|
|
741
976
|
/* @__PURE__ */ jsx2(
|
|
742
977
|
"button",
|
|
743
978
|
{
|
|
744
979
|
onClick: () => sendMessage(inputValue),
|
|
745
980
|
disabled: isLoading || !inputValue.trim(),
|
|
746
981
|
style: {
|
|
982
|
+
...panelStyles.buttonBase,
|
|
747
983
|
...panelStyles.sendButton,
|
|
748
984
|
...isLoading || !inputValue.trim() ? panelStyles.sendButtonDisabled : {}
|
|
749
985
|
},
|
|
750
986
|
"data-testid": "button-send-chat",
|
|
751
|
-
children: /* @__PURE__ */
|
|
752
|
-
/* @__PURE__ */ jsx2("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
753
|
-
/* @__PURE__ */ jsx2("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })
|
|
754
|
-
] })
|
|
987
|
+
children: /* @__PURE__ */ jsx2(SendIcon, {})
|
|
755
988
|
}
|
|
756
989
|
)
|
|
757
990
|
] })
|
|
@@ -1741,7 +1974,7 @@ function QuizPlayer({
|
|
|
1741
1974
|
/* @__PURE__ */ jsxs3("div", { style: defaultStyles.quizContent, children: [
|
|
1742
1975
|
/* @__PURE__ */ jsxs3("div", { style: { ...defaultStyles.question, position: "relative", paddingBottom: "40px" }, children: [
|
|
1743
1976
|
/* @__PURE__ */ jsx3("div", { style: defaultStyles.questionText, children: /* @__PURE__ */ jsx3(TextToSpeech, { text: currentQuestion.question, inline: true, size: "md" }) }),
|
|
1744
|
-
isExtraQuestion &&
|
|
1977
|
+
isExtraQuestion && /* @__PURE__ */ jsxs3(
|
|
1745
1978
|
"button",
|
|
1746
1979
|
{
|
|
1747
1980
|
onClick: () => setShowSkipModal(true),
|
|
@@ -1782,7 +2015,7 @@ function QuizPlayer({
|
|
|
1782
2015
|
]
|
|
1783
2016
|
}
|
|
1784
2017
|
),
|
|
1785
|
-
!isExtraQuestion &&
|
|
2018
|
+
!isExtraQuestion && /* @__PURE__ */ jsxs3(
|
|
1786
2019
|
"button",
|
|
1787
2020
|
{
|
|
1788
2021
|
onClick: () => setShowReportModal(true),
|