@contentgrowth/llm-service 0.9.3 → 0.9.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.
|
@@ -149,7 +149,7 @@ function ChatHeader({
|
|
|
149
149
|
|
|
150
150
|
// src/ui/react/components/ChatInputArea.tsx
|
|
151
151
|
import { useState as useState3, useRef as useRef3, useImperativeHandle, forwardRef, useEffect as useEffect3, useCallback as useCallback3, useLayoutEffect } from "react";
|
|
152
|
-
import { MicrophoneIcon, StopIcon, PaperAirplaneIcon } from "@heroicons/react/24/outline";
|
|
152
|
+
import { MicrophoneIcon, StopIcon, PaperAirplaneIcon, XMarkIcon } from "@heroicons/react/24/outline";
|
|
153
153
|
|
|
154
154
|
// src/ui/react/hooks/useSpeechRecognition.ts
|
|
155
155
|
import { useState, useEffect, useCallback, useRef } from "react";
|
|
@@ -387,6 +387,42 @@ var ChatInputArea = forwardRef(({
|
|
|
387
387
|
const [isTranscribing, setIsTranscribing] = useState3(false);
|
|
388
388
|
const [voiceError, setVoiceError] = useState3(null);
|
|
389
389
|
const [isFocused, setIsFocused] = useState3(false);
|
|
390
|
+
const [showDebug, setShowDebug] = useState3(false);
|
|
391
|
+
const [logs, setLogs] = useState3([]);
|
|
392
|
+
const tapCountRef = useRef3({ count: 0, lastTap: 0 });
|
|
393
|
+
useEffect3(() => {
|
|
394
|
+
const originalLog = console.log;
|
|
395
|
+
const originalWarn = console.warn;
|
|
396
|
+
const originalError = console.error;
|
|
397
|
+
const addLog = (type, args) => {
|
|
398
|
+
try {
|
|
399
|
+
const msg = args.map((arg) => {
|
|
400
|
+
if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
|
|
401
|
+
if (typeof arg === "object") return JSON.stringify(arg);
|
|
402
|
+
return String(arg);
|
|
403
|
+
}).join(" ");
|
|
404
|
+
setLogs((prev) => [`[${type}] ${msg}`, ...prev].slice(0, 50));
|
|
405
|
+
} catch (e) {
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
console.log = (...args) => {
|
|
409
|
+
originalLog(...args);
|
|
410
|
+
addLog("LOG", args);
|
|
411
|
+
};
|
|
412
|
+
console.warn = (...args) => {
|
|
413
|
+
originalWarn(...args);
|
|
414
|
+
addLog("WRN", args);
|
|
415
|
+
};
|
|
416
|
+
console.error = (...args) => {
|
|
417
|
+
originalError(...args);
|
|
418
|
+
addLog("ERR", args);
|
|
419
|
+
};
|
|
420
|
+
return () => {
|
|
421
|
+
console.log = originalLog;
|
|
422
|
+
console.warn = originalWarn;
|
|
423
|
+
console.error = originalError;
|
|
424
|
+
};
|
|
425
|
+
}, []);
|
|
390
426
|
const textareaRef = useRef3(null);
|
|
391
427
|
const measurementRef = useRef3(null);
|
|
392
428
|
const pendingSelectionRef = useRef3(null);
|
|
@@ -456,6 +492,12 @@ var ChatInputArea = forwardRef(({
|
|
|
456
492
|
(_b2 = (_a2 = voiceConfigRef.current) == null ? void 0 : _a2.onVoiceEnd) == null ? void 0 : _b2.call(_a2);
|
|
457
493
|
}, []);
|
|
458
494
|
const nativeSpeech = useSpeechRecognition(handleVoiceResult, handleVoiceEnd, voiceConfig == null ? void 0 : voiceConfig.language);
|
|
495
|
+
useEffect3(() => {
|
|
496
|
+
if (nativeSpeech.error) {
|
|
497
|
+
setVoiceError(nativeSpeech.error);
|
|
498
|
+
console.error("[ChatInputArea] Native Speech Error:", nativeSpeech.error);
|
|
499
|
+
}
|
|
500
|
+
}, [nativeSpeech.error]);
|
|
459
501
|
const customRecorder = useAudioRecorder(async (blob) => {
|
|
460
502
|
var _a2, _b2, _c2;
|
|
461
503
|
setVoiceTrigger(null);
|
|
@@ -569,20 +611,40 @@ var ChatInputArea = forwardRef(({
|
|
|
569
611
|
if (!showInputForm) {
|
|
570
612
|
return null;
|
|
571
613
|
}
|
|
572
|
-
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col w-full", children: [
|
|
614
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col w-full relative", children: [
|
|
615
|
+
showDebug && /* @__PURE__ */ jsxs3("div", { className: "absolute bottom-full left-0 right-0 mb-2 p-2 bg-black/80 text-green-400 text-xs font-mono h-48 overflow-y-auto rounded z-50 pointer-events-auto", children: [
|
|
616
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center bg-gray-800 p-1 mb-1", children: [
|
|
617
|
+
/* @__PURE__ */ jsx5("span", { children: "Debug Logs" }),
|
|
618
|
+
/* @__PURE__ */ jsx5("button", { onClick: () => setShowDebug(false), className: "text-white hover:text-red-400", children: /* @__PURE__ */ jsx5(XMarkIcon, { className: "w-4 h-4" }) })
|
|
619
|
+
] }),
|
|
620
|
+
logs.map((log, i) => /* @__PURE__ */ jsx5("div", { className: "mb-0.5 border-b border-gray-700/50 pb-0.5 break-all", children: log }, i)),
|
|
621
|
+
logs.length === 0 && /* @__PURE__ */ jsx5("div", { children: "No logs yet..." })
|
|
622
|
+
] }),
|
|
573
623
|
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
|
|
574
624
|
voiceConfig && /* @__PURE__ */ jsx5(
|
|
575
625
|
"button",
|
|
576
626
|
{
|
|
577
627
|
type: "button",
|
|
578
628
|
onClick: () => {
|
|
629
|
+
const now = Date.now();
|
|
630
|
+
if (now - tapCountRef.current.lastTap < 500) {
|
|
631
|
+
tapCountRef.current.count++;
|
|
632
|
+
} else {
|
|
633
|
+
tapCountRef.current.count = 1;
|
|
634
|
+
}
|
|
635
|
+
tapCountRef.current.lastTap = now;
|
|
636
|
+
if (tapCountRef.current.count >= 5) {
|
|
637
|
+
setShowDebug((prev) => !prev);
|
|
638
|
+
tapCountRef.current.count = 0;
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
579
641
|
if (voiceTrigger) {
|
|
580
642
|
stopRecording();
|
|
581
643
|
} else if (!isTranscribing) {
|
|
582
644
|
startRecording("click");
|
|
583
645
|
}
|
|
584
646
|
},
|
|
585
|
-
className: `mb-1 p-2 rounded-full transition-all duration-300 flex-shrink-0 border ${
|
|
647
|
+
className: `mb-1 p-2 rounded-full transition-all duration-300 flex-shrink-0 border ${isTranscribing ? "text-white border-indigo-500 bg-indigo-600 scale-110 shadow-lg" : voiceTrigger ? "text-white border-orange-400 bg-orange-500 scale-110 shadow-lg" : "text-gray-500 border-gray-300 bg-white hover:text-gray-700 hover:bg-gray-100"} ${voiceTrigger ? "animate-pulse" : ""} ${isTranscribing ? "cursor-wait" : ""}`,
|
|
586
648
|
disabled: isTranscribing,
|
|
587
649
|
title: isTranscribing ? "Transcribing..." : voiceTrigger ? "Stop Recording" : "Start Voice Input",
|
|
588
650
|
children: isTranscribing ? /* @__PURE__ */ jsx5("div", { className: "animate-spin w-5 h-5 flex items-center justify-center", children: /* @__PURE__ */ jsxs3("svg", { className: "w-5 h-5 text-white", viewBox: "0 0 24 24", children: [
|
|
@@ -685,17 +747,17 @@ var ChatInputArea = forwardRef(({
|
|
|
685
747
|
)
|
|
686
748
|
] }),
|
|
687
749
|
inputHint && /* @__PURE__ */ jsx5("div", { className: "text-sm text-red-500 bg-red-50 py-1 px-4 rounded-lg mt-1", children: inputHint }),
|
|
688
|
-
/* @__PURE__ */ jsx5("div", { className: "ml-[46px] mb-2 mt-0.5 min-h-[0.75rem]", style: { marginLeft: "48px" }, children: /* @__PURE__ */ jsx5("p", { className: `text-[10px] leading-tight transition-all duration-200 ${voiceError ? "text-red-500" :
|
|
750
|
+
/* @__PURE__ */ jsx5("div", { className: "ml-[46px] mb-2 mt-0.5 min-h-[0.75rem]", style: { marginLeft: "48px" }, children: /* @__PURE__ */ jsx5("p", { className: `text-[10px] leading-tight transition-all duration-200 ${voiceError ? "text-red-500" : isTranscribing ? "text-indigo-600 font-bold" : voiceTrigger ? "text-orange-600 font-medium" : "text-gray-400"}`, children: voiceError ? /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1 font-semibold italic", children: [
|
|
689
751
|
"Error: ",
|
|
690
752
|
voiceError
|
|
691
|
-
] }) : isTranscribing ? "Transcribing, please wait..." : voiceTrigger ? "Listening... tap mic icon again to stop" : hintText || (voiceConfig ? "Type in text or tap mic icon to talk" : "Type your message...") }) })
|
|
753
|
+
] }) : isTranscribing ? "Transcribing... please wait" : voiceTrigger ? "Transcribing, please wait..." : voiceTrigger ? "Listening... tap mic icon again to stop" : hintText || (voiceConfig ? "Type in text or tap mic icon to talk" : "Type your message...") }) })
|
|
692
754
|
] });
|
|
693
755
|
});
|
|
694
756
|
ChatInputArea.displayName = "ChatInputArea";
|
|
695
757
|
|
|
696
758
|
// src/ui/react/components/TapToTalk.tsx
|
|
697
|
-
import { useState as useState4, useCallback as useCallback4 } from "react";
|
|
698
|
-
import { MicrophoneIcon as MicrophoneIcon2 } from "@heroicons/react/24/outline";
|
|
759
|
+
import React3, { useState as useState4, useCallback as useCallback4, useRef as useRef4 } from "react";
|
|
760
|
+
import { MicrophoneIcon as MicrophoneIcon2, XMarkIcon as XMarkIcon2 } from "@heroicons/react/24/outline";
|
|
699
761
|
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
700
762
|
var TapToTalk = ({
|
|
701
763
|
onResult,
|
|
@@ -712,6 +774,42 @@ var TapToTalk = ({
|
|
|
712
774
|
const [isTranscribing, setIsTranscribing] = useState4(false);
|
|
713
775
|
const [voiceTrigger, setVoiceTrigger] = useState4(null);
|
|
714
776
|
const [errorMsg, setErrorMsg] = useState4(null);
|
|
777
|
+
const [showDebug, setShowDebug] = useState4(false);
|
|
778
|
+
const [logs, setLogs] = useState4([]);
|
|
779
|
+
const tapCountRef = useRef4({ count: 0, lastTap: 0 });
|
|
780
|
+
React3.useEffect(() => {
|
|
781
|
+
const originalLog = console.log;
|
|
782
|
+
const originalWarn = console.warn;
|
|
783
|
+
const originalError = console.error;
|
|
784
|
+
const addLog = (type, args) => {
|
|
785
|
+
try {
|
|
786
|
+
const msg = args.map((arg) => {
|
|
787
|
+
if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
|
|
788
|
+
if (typeof arg === "object") return JSON.stringify(arg);
|
|
789
|
+
return String(arg);
|
|
790
|
+
}).join(" ");
|
|
791
|
+
setLogs((prev) => [`[${type}] ${msg}`, ...prev].slice(0, 50));
|
|
792
|
+
} catch (e) {
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
console.log = (...args) => {
|
|
796
|
+
originalLog(...args);
|
|
797
|
+
addLog("LOG", args);
|
|
798
|
+
};
|
|
799
|
+
console.warn = (...args) => {
|
|
800
|
+
originalWarn(...args);
|
|
801
|
+
addLog("WRN", args);
|
|
802
|
+
};
|
|
803
|
+
console.error = (...args) => {
|
|
804
|
+
originalError(...args);
|
|
805
|
+
addLog("ERR", args);
|
|
806
|
+
};
|
|
807
|
+
return () => {
|
|
808
|
+
console.log = originalLog;
|
|
809
|
+
console.warn = originalWarn;
|
|
810
|
+
console.error = originalError;
|
|
811
|
+
};
|
|
812
|
+
}, []);
|
|
715
813
|
const handleVoiceResult = useCallback4((text, isFinal) => {
|
|
716
814
|
if (isFinal) {
|
|
717
815
|
onResult(text);
|
|
@@ -723,6 +821,12 @@ var TapToTalk = ({
|
|
|
723
821
|
setVoiceTrigger(null);
|
|
724
822
|
}, []);
|
|
725
823
|
const nativeSpeech = useSpeechRecognition(handleVoiceResult, handleVoiceEnd, voiceConfig == null ? void 0 : voiceConfig.language);
|
|
824
|
+
React3.useEffect(() => {
|
|
825
|
+
if (nativeSpeech.error) {
|
|
826
|
+
setErrorMsg(nativeSpeech.error);
|
|
827
|
+
console.error("[TapToTalk] Native Speech Error:", nativeSpeech.error);
|
|
828
|
+
}
|
|
829
|
+
}, [nativeSpeech.error]);
|
|
726
830
|
const customRecorder = useAudioRecorder(async (blob) => {
|
|
727
831
|
setVoiceTrigger(null);
|
|
728
832
|
setIsTranscribing(true);
|
|
@@ -751,6 +855,18 @@ var TapToTalk = ({
|
|
|
751
855
|
const isListening = !!voiceTrigger || nativeSpeech.isListening || customRecorder.isRecording;
|
|
752
856
|
const isActive = isListening || isTranscribing;
|
|
753
857
|
const toggleVoice = async () => {
|
|
858
|
+
const now = Date.now();
|
|
859
|
+
if (now - tapCountRef.current.lastTap < 500) {
|
|
860
|
+
tapCountRef.current.count++;
|
|
861
|
+
} else {
|
|
862
|
+
tapCountRef.current.count = 1;
|
|
863
|
+
}
|
|
864
|
+
tapCountRef.current.lastTap = now;
|
|
865
|
+
if (tapCountRef.current.count >= 5) {
|
|
866
|
+
setShowDebug((prev) => !prev);
|
|
867
|
+
tapCountRef.current.count = 0;
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
754
870
|
if (isActive) {
|
|
755
871
|
if (isTranscribing && !isListening) return;
|
|
756
872
|
if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
|
|
@@ -788,30 +904,43 @@ var TapToTalk = ({
|
|
|
788
904
|
label = "Listening ... Tap to stop";
|
|
789
905
|
Icon = /* @__PURE__ */ jsx6(MicrophoneIcon2, { className: "h-5 w-5 animate-pulse" });
|
|
790
906
|
} else if (isTranscribing) {
|
|
791
|
-
bgColor = "bg-
|
|
907
|
+
bgColor = "bg-indigo-600";
|
|
792
908
|
label = "Transcribing ...";
|
|
793
909
|
Icon = /* @__PURE__ */ jsxs4("svg", { className: "animate-spin h-5 w-5 text-white", xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", children: [
|
|
794
910
|
/* @__PURE__ */ jsx6("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
|
|
795
911
|
/* @__PURE__ */ jsx6("path", { className: "opacity-75", fill: "currentColor", d: "M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" })
|
|
796
912
|
] });
|
|
797
913
|
}
|
|
798
|
-
return /* @__PURE__ */ jsxs4(
|
|
799
|
-
"
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
914
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col w-full relative", children: [
|
|
915
|
+
showDebug && /* @__PURE__ */ jsxs4("div", { className: "absolute bottom-full left-0 right-0 mb-2 p-2 bg-black/80 text-green-400 text-xs font-mono h-48 overflow-y-auto rounded z-50 pointer-events-auto", children: [
|
|
916
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex justify-between items-center bg-gray-800 p-1 mb-1", children: [
|
|
917
|
+
/* @__PURE__ */ jsx6("span", { children: "Debug Logs" }),
|
|
918
|
+
/* @__PURE__ */ jsx6("button", { onClick: (e) => {
|
|
919
|
+
e.stopPropagation();
|
|
920
|
+
setShowDebug(false);
|
|
921
|
+
}, className: "text-white hover:text-red-400", children: /* @__PURE__ */ jsx6(XMarkIcon2, { className: "w-4 h-4" }) })
|
|
922
|
+
] }),
|
|
923
|
+
logs.map((log, i) => /* @__PURE__ */ jsx6("div", { className: "mb-0.5 border-b border-gray-700/50 pb-0.5 break-all", children: log }, i)),
|
|
924
|
+
logs.length === 0 && /* @__PURE__ */ jsx6("div", { children: "No logs yet..." })
|
|
925
|
+
] }),
|
|
926
|
+
/* @__PURE__ */ jsxs4(
|
|
927
|
+
"button",
|
|
928
|
+
{
|
|
929
|
+
onClick: toggleVoice,
|
|
930
|
+
disabled: disabled || isTranscribing && !isListening,
|
|
931
|
+
className: `flex items-center justify-center gap-3 px-6 py-3 rounded-xl transition-all duration-300 w-full font-medium shadow-md active:scale-[0.98]
|
|
804
932
|
${bgColor} text-white
|
|
805
933
|
${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
|
|
806
934
|
${className}`,
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
935
|
+
title: label,
|
|
936
|
+
children: [
|
|
937
|
+
/* @__PURE__ */ jsx6("div", { className: "flex items-center justify-center shrink-0", children: Icon }),
|
|
938
|
+
/* @__PURE__ */ jsx6("span", { className: "truncate", children: label }),
|
|
939
|
+
errorMsg && /* @__PURE__ */ jsx6("span", { className: "text-[10px] bg-white/20 px-1.5 py-0.5 rounded text-red-100 animate-in fade-in slide-in-from-right-1", children: errorMsg })
|
|
940
|
+
]
|
|
941
|
+
}
|
|
942
|
+
)
|
|
943
|
+
] });
|
|
815
944
|
};
|
|
816
945
|
|
|
817
946
|
// src/ui/react/components/ChatMessageList.tsx
|