@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.
@@ -429,6 +429,42 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
429
429
  const [isTranscribing, setIsTranscribing] = (0, import_react5.useState)(false);
430
430
  const [voiceError, setVoiceError] = (0, import_react5.useState)(null);
431
431
  const [isFocused, setIsFocused] = (0, import_react5.useState)(false);
432
+ const [showDebug, setShowDebug] = (0, import_react5.useState)(false);
433
+ const [logs, setLogs] = (0, import_react5.useState)([]);
434
+ const tapCountRef = (0, import_react5.useRef)({ count: 0, lastTap: 0 });
435
+ (0, import_react5.useEffect)(() => {
436
+ const originalLog = console.log;
437
+ const originalWarn = console.warn;
438
+ const originalError = console.error;
439
+ const addLog = (type, args) => {
440
+ try {
441
+ const msg = args.map((arg) => {
442
+ if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
443
+ if (typeof arg === "object") return JSON.stringify(arg);
444
+ return String(arg);
445
+ }).join(" ");
446
+ setLogs((prev) => [`[${type}] ${msg}`, ...prev].slice(0, 50));
447
+ } catch (e) {
448
+ }
449
+ };
450
+ console.log = (...args) => {
451
+ originalLog(...args);
452
+ addLog("LOG", args);
453
+ };
454
+ console.warn = (...args) => {
455
+ originalWarn(...args);
456
+ addLog("WRN", args);
457
+ };
458
+ console.error = (...args) => {
459
+ originalError(...args);
460
+ addLog("ERR", args);
461
+ };
462
+ return () => {
463
+ console.log = originalLog;
464
+ console.warn = originalWarn;
465
+ console.error = originalError;
466
+ };
467
+ }, []);
432
468
  const textareaRef = (0, import_react5.useRef)(null);
433
469
  const measurementRef = (0, import_react5.useRef)(null);
434
470
  const pendingSelectionRef = (0, import_react5.useRef)(null);
@@ -498,6 +534,12 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
498
534
  (_b2 = (_a2 = voiceConfigRef.current) == null ? void 0 : _a2.onVoiceEnd) == null ? void 0 : _b2.call(_a2);
499
535
  }, []);
500
536
  const nativeSpeech = useSpeechRecognition(handleVoiceResult, handleVoiceEnd, voiceConfig == null ? void 0 : voiceConfig.language);
537
+ (0, import_react5.useEffect)(() => {
538
+ if (nativeSpeech.error) {
539
+ setVoiceError(nativeSpeech.error);
540
+ console.error("[ChatInputArea] Native Speech Error:", nativeSpeech.error);
541
+ }
542
+ }, [nativeSpeech.error]);
501
543
  const customRecorder = useAudioRecorder(async (blob) => {
502
544
  var _a2, _b2, _c2;
503
545
  setVoiceTrigger(null);
@@ -611,20 +653,40 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
611
653
  if (!showInputForm) {
612
654
  return null;
613
655
  }
614
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col w-full", children: [
656
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex flex-col w-full relative", children: [
657
+ showDebug && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("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: [
658
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex justify-between items-center bg-gray-800 p-1 mb-1", children: [
659
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Debug Logs" }),
660
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: () => setShowDebug(false), className: "text-white hover:text-red-400", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_outline.XMarkIcon, { className: "w-4 h-4" }) })
661
+ ] }),
662
+ logs.map((log, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "mb-0.5 border-b border-gray-700/50 pb-0.5 break-all", children: log }, i)),
663
+ logs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { children: "No logs yet..." })
664
+ ] }),
615
665
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
616
666
  voiceConfig && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
617
667
  "button",
618
668
  {
619
669
  type: "button",
620
670
  onClick: () => {
671
+ const now = Date.now();
672
+ if (now - tapCountRef.current.lastTap < 500) {
673
+ tapCountRef.current.count++;
674
+ } else {
675
+ tapCountRef.current.count = 1;
676
+ }
677
+ tapCountRef.current.lastTap = now;
678
+ if (tapCountRef.current.count >= 5) {
679
+ setShowDebug((prev) => !prev);
680
+ tapCountRef.current.count = 0;
681
+ return;
682
+ }
621
683
  if (voiceTrigger) {
622
684
  stopRecording();
623
685
  } else if (!isTranscribing) {
624
686
  startRecording("click");
625
687
  }
626
688
  },
627
- className: `mb-1 p-2 rounded-full transition-all duration-300 flex-shrink-0 border ${voiceTrigger || isTranscribing ? "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" : ""}`,
689
+ 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" : ""}`,
628
690
  disabled: isTranscribing,
629
691
  title: isTranscribing ? "Transcribing..." : voiceTrigger ? "Stop Recording" : "Start Voice Input",
630
692
  children: isTranscribing ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "animate-spin w-5 h-5 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("svg", { className: "w-5 h-5 text-white", viewBox: "0 0 24 24", children: [
@@ -727,16 +789,16 @@ var ChatInputArea = (0, import_react5.forwardRef)(({
727
789
  )
728
790
  ] }),
729
791
  inputHint && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "text-sm text-red-500 bg-red-50 py-1 px-4 rounded-lg mt-1", children: inputHint }),
730
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ml-[46px] mb-2 mt-0.5 min-h-[0.75rem]", style: { marginLeft: "48px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { className: `text-[10px] leading-tight transition-all duration-200 ${voiceError ? "text-red-500" : voiceTrigger || isTranscribing ? "text-orange-600 font-medium" : "text-gray-400"}`, children: voiceError ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-1 font-semibold italic", children: [
792
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "ml-[46px] mb-2 mt-0.5 min-h-[0.75rem]", style: { marginLeft: "48px" }, children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "flex items-center gap-1 font-semibold italic", children: [
731
793
  "Error: ",
732
794
  voiceError
733
- ] }) : 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...") }) })
795
+ ] }) : 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...") }) })
734
796
  ] });
735
797
  });
736
798
  ChatInputArea.displayName = "ChatInputArea";
737
799
 
738
800
  // src/ui/react/components/TapToTalk.tsx
739
- var import_react6 = require("react");
801
+ var import_react6 = __toESM(require("react"), 1);
740
802
  var import_outline2 = require("@heroicons/react/24/outline");
741
803
  var import_jsx_runtime6 = require("react/jsx-runtime");
742
804
  var TapToTalk = ({
@@ -754,6 +816,42 @@ var TapToTalk = ({
754
816
  const [isTranscribing, setIsTranscribing] = (0, import_react6.useState)(false);
755
817
  const [voiceTrigger, setVoiceTrigger] = (0, import_react6.useState)(null);
756
818
  const [errorMsg, setErrorMsg] = (0, import_react6.useState)(null);
819
+ const [showDebug, setShowDebug] = (0, import_react6.useState)(false);
820
+ const [logs, setLogs] = (0, import_react6.useState)([]);
821
+ const tapCountRef = (0, import_react6.useRef)({ count: 0, lastTap: 0 });
822
+ import_react6.default.useEffect(() => {
823
+ const originalLog = console.log;
824
+ const originalWarn = console.warn;
825
+ const originalError = console.error;
826
+ const addLog = (type, args) => {
827
+ try {
828
+ const msg = args.map((arg) => {
829
+ if (arg instanceof Error) return `${arg.name}: ${arg.message}`;
830
+ if (typeof arg === "object") return JSON.stringify(arg);
831
+ return String(arg);
832
+ }).join(" ");
833
+ setLogs((prev) => [`[${type}] ${msg}`, ...prev].slice(0, 50));
834
+ } catch (e) {
835
+ }
836
+ };
837
+ console.log = (...args) => {
838
+ originalLog(...args);
839
+ addLog("LOG", args);
840
+ };
841
+ console.warn = (...args) => {
842
+ originalWarn(...args);
843
+ addLog("WRN", args);
844
+ };
845
+ console.error = (...args) => {
846
+ originalError(...args);
847
+ addLog("ERR", args);
848
+ };
849
+ return () => {
850
+ console.log = originalLog;
851
+ console.warn = originalWarn;
852
+ console.error = originalError;
853
+ };
854
+ }, []);
757
855
  const handleVoiceResult = (0, import_react6.useCallback)((text, isFinal) => {
758
856
  if (isFinal) {
759
857
  onResult(text);
@@ -765,6 +863,12 @@ var TapToTalk = ({
765
863
  setVoiceTrigger(null);
766
864
  }, []);
767
865
  const nativeSpeech = useSpeechRecognition(handleVoiceResult, handleVoiceEnd, voiceConfig == null ? void 0 : voiceConfig.language);
866
+ import_react6.default.useEffect(() => {
867
+ if (nativeSpeech.error) {
868
+ setErrorMsg(nativeSpeech.error);
869
+ console.error("[TapToTalk] Native Speech Error:", nativeSpeech.error);
870
+ }
871
+ }, [nativeSpeech.error]);
768
872
  const customRecorder = useAudioRecorder(async (blob) => {
769
873
  setVoiceTrigger(null);
770
874
  setIsTranscribing(true);
@@ -793,6 +897,18 @@ var TapToTalk = ({
793
897
  const isListening = !!voiceTrigger || nativeSpeech.isListening || customRecorder.isRecording;
794
898
  const isActive = isListening || isTranscribing;
795
899
  const toggleVoice = async () => {
900
+ const now = Date.now();
901
+ if (now - tapCountRef.current.lastTap < 500) {
902
+ tapCountRef.current.count++;
903
+ } else {
904
+ tapCountRef.current.count = 1;
905
+ }
906
+ tapCountRef.current.lastTap = now;
907
+ if (tapCountRef.current.count >= 5) {
908
+ setShowDebug((prev) => !prev);
909
+ tapCountRef.current.count = 0;
910
+ return;
911
+ }
796
912
  if (isActive) {
797
913
  if (isTranscribing && !isListening) return;
798
914
  if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") {
@@ -830,30 +946,43 @@ var TapToTalk = ({
830
946
  label = "Listening ... Tap to stop";
831
947
  Icon = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_outline2.MicrophoneIcon, { className: "h-5 w-5 animate-pulse" });
832
948
  } else if (isTranscribing) {
833
- bgColor = "bg-orange-500";
949
+ bgColor = "bg-indigo-600";
834
950
  label = "Transcribing ...";
835
951
  Icon = /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
836
952
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
837
953
  /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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" })
838
954
  ] });
839
955
  }
840
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
841
- "button",
842
- {
843
- onClick: toggleVoice,
844
- disabled: disabled || isTranscribing && !isListening,
845
- 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]
956
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex flex-col w-full relative", children: [
957
+ showDebug && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("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: [
958
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "flex justify-between items-center bg-gray-800 p-1 mb-1", children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { children: "Debug Logs" }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("button", { onClick: (e) => {
961
+ e.stopPropagation();
962
+ setShowDebug(false);
963
+ }, className: "text-white hover:text-red-400", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_outline2.XMarkIcon, { className: "w-4 h-4" }) })
964
+ ] }),
965
+ logs.map((log, i) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "mb-0.5 border-b border-gray-700/50 pb-0.5 break-all", children: log }, i)),
966
+ logs.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { children: "No logs yet..." })
967
+ ] }),
968
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
969
+ "button",
970
+ {
971
+ onClick: toggleVoice,
972
+ disabled: disabled || isTranscribing && !isListening,
973
+ 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]
846
974
  ${bgColor} text-white
847
975
  ${disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer"}
848
976
  ${className}`,
849
- title: label,
850
- children: [
851
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-center justify-center shrink-0", children: Icon }),
852
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", children: label }),
853
- errorMsg && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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 })
854
- ]
855
- }
856
- );
977
+ title: label,
978
+ children: [
979
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "flex items-center justify-center shrink-0", children: Icon }),
980
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "truncate", children: label }),
981
+ errorMsg && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("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 })
982
+ ]
983
+ }
984
+ )
985
+ ] });
857
986
  };
858
987
 
859
988
  // src/ui/react/components/ChatMessageList.tsx