@contentgrowth/llm-service 0.9.4 → 0.9.6

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, XMarkIcon } from "@heroicons/react/24/outline";
152
+ import { MicrophoneIcon, StopIcon, PaperAirplaneIcon, XMarkIcon, Square2StackIcon } from "@heroicons/react/24/outline";
153
153
 
154
154
  // src/ui/react/hooks/useSpeechRecognition.ts
155
155
  import { useState, useEffect, useCallback, useRef } from "react";
@@ -161,6 +161,12 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
161
161
  const recognitionRef = useRef(null);
162
162
  const isSimulatingRef = useRef(false);
163
163
  const simulationTimeoutRef = useRef(null);
164
+ const onResultRef = useRef(onResult);
165
+ const onEndRef = useRef(onEnd);
166
+ useEffect(() => {
167
+ onResultRef.current = onResult;
168
+ onEndRef.current = onEnd;
169
+ }, [onResult, onEnd]);
164
170
  useEffect(() => {
165
171
  if (typeof window !== "undefined") {
166
172
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
@@ -179,7 +185,7 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
179
185
  return;
180
186
  }
181
187
  setIsListening(false);
182
- if (onEnd) onEnd();
188
+ if (onEndRef.current) onEndRef.current();
183
189
  };
184
190
  recognition.onresult = (event) => {
185
191
  let interimTranscript = "";
@@ -188,10 +194,10 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
188
194
  const result = event.results[i];
189
195
  if (result.isFinal) {
190
196
  finalTranscript += result[0].transcript;
191
- if (onResult) onResult(finalTranscript, true);
197
+ if (onResultRef.current) onResultRef.current(finalTranscript, true);
192
198
  } else {
193
199
  interimTranscript += result[0].transcript;
194
- if (onResult) onResult(interimTranscript, false);
200
+ if (onResultRef.current) onResultRef.current(interimTranscript, false);
195
201
  }
196
202
  }
197
203
  setTranscript((prev) => prev + finalTranscript);
@@ -206,10 +212,10 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
206
212
  simulationTimeoutRef.current = setTimeout(() => {
207
213
  const mockText = "This is a simulated voice input for testing.";
208
214
  setTranscript((prev) => prev + (prev ? " " : "") + mockText);
209
- if (onResult) onResult(mockText, true);
215
+ if (onResultRef.current) onResultRef.current(mockText, true);
210
216
  isSimulatingRef.current = false;
211
217
  setIsListening(false);
212
- if (onEnd) onEnd();
218
+ if (onEndRef.current) onEndRef.current();
213
219
  simulationTimeoutRef.current = null;
214
220
  }, 3e3);
215
221
  return;
@@ -226,9 +232,11 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
226
232
  clearTimeout(simulationTimeoutRef.current);
227
233
  simulationTimeoutRef.current = null;
228
234
  }
229
- recognitionRef.current.stop();
235
+ if (recognitionRef.current) {
236
+ recognitionRef.current.stop();
237
+ }
230
238
  };
231
- }, [onResult, onEnd, language]);
239
+ }, [language]);
232
240
  const start = useCallback(() => {
233
241
  if (recognitionRef.current && !isListening) {
234
242
  try {
@@ -248,10 +256,10 @@ var useSpeechRecognition = (onResult, onEnd, language = "en-US") => {
248
256
  }
249
257
  const mockText = "This is a simulated voice input for testing.";
250
258
  setTranscript((prev) => prev + (prev ? " " : "") + mockText);
251
- if (onResult) onResult(mockText, true);
259
+ if (onResultRef.current) onResultRef.current(mockText, true);
252
260
  isSimulatingRef.current = false;
253
261
  setIsListening(false);
254
- if (onEnd) onEnd();
262
+ if (onEndRef.current) onEndRef.current();
255
263
  return;
256
264
  }
257
265
  if (recognitionRef.current && isListening) {
@@ -423,6 +431,9 @@ var ChatInputArea = forwardRef(({
423
431
  console.error = originalError;
424
432
  };
425
433
  }, []);
434
+ const copyLogs = useCallback3(() => {
435
+ navigator.clipboard.writeText(logs.join("\n")).then(() => alert("Logs copied to clipboard")).catch((err) => console.error("Failed to copy logs", err));
436
+ }, [logs]);
426
437
  const textareaRef = useRef3(null);
427
438
  const measurementRef = useRef3(null);
428
439
  const pendingSelectionRef = useRef3(null);
@@ -615,7 +626,13 @@ var ChatInputArea = forwardRef(({
615
626
  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
627
  /* @__PURE__ */ jsxs3("div", { className: "flex justify-between items-center bg-gray-800 p-1 mb-1", children: [
617
628
  /* @__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" }) })
629
+ /* @__PURE__ */ jsxs3("div", { className: "flex gap-2", children: [
630
+ /* @__PURE__ */ jsx5("button", { onClick: copyLogs, className: "text-white hover:text-blue-400", title: "Copy Logs", children: /* @__PURE__ */ jsx5(Square2StackIcon, { className: "w-4 h-4" }) }),
631
+ /* @__PURE__ */ jsx5("button", { onClick: () => {
632
+ copyLogs();
633
+ setShowDebug(false);
634
+ }, className: "text-white hover:text-red-400", title: "Copy & Close", children: /* @__PURE__ */ jsx5(XMarkIcon, { className: "w-4 h-4" }) })
635
+ ] })
619
636
  ] }),
620
637
  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
638
  logs.length === 0 && /* @__PURE__ */ jsx5("div", { children: "No logs yet..." })
@@ -636,6 +653,7 @@ var ChatInputArea = forwardRef(({
636
653
  if (tapCountRef.current.count >= 5) {
637
654
  setShowDebug((prev) => !prev);
638
655
  tapCountRef.current.count = 0;
656
+ stopRecording();
639
657
  return;
640
658
  }
641
659
  if (voiceTrigger) {
@@ -757,7 +775,7 @@ ChatInputArea.displayName = "ChatInputArea";
757
775
 
758
776
  // src/ui/react/components/TapToTalk.tsx
759
777
  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";
778
+ import { MicrophoneIcon as MicrophoneIcon2, XMarkIcon as XMarkIcon2, Square2StackIcon as Square2StackIcon2 } from "@heroicons/react/24/outline";
761
779
  import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
762
780
  var TapToTalk = ({
763
781
  onResult,
@@ -810,6 +828,9 @@ var TapToTalk = ({
810
828
  console.error = originalError;
811
829
  };
812
830
  }, []);
831
+ const copyLogs = useCallback4(() => {
832
+ navigator.clipboard.writeText(logs.join("\n")).then(() => alert("Logs copied to clipboard")).catch((err) => console.error("Failed to copy logs", err));
833
+ }, [logs]);
813
834
  const handleVoiceResult = useCallback4((text, isFinal) => {
814
835
  if (isFinal) {
815
836
  onResult(text);
@@ -865,6 +886,11 @@ var TapToTalk = ({
865
886
  if (tapCountRef.current.count >= 5) {
866
887
  setShowDebug((prev) => !prev);
867
888
  tapCountRef.current.count = 0;
889
+ if (isActive) {
890
+ if ((voiceConfig == null ? void 0 : voiceConfig.mode) === "native") nativeSpeech.stop();
891
+ else customRecorder.stop();
892
+ setVoiceTrigger(null);
893
+ }
868
894
  return;
869
895
  }
870
896
  if (isActive) {
@@ -915,10 +941,17 @@ var TapToTalk = ({
915
941
  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
942
  /* @__PURE__ */ jsxs4("div", { className: "flex justify-between items-center bg-gray-800 p-1 mb-1", children: [
917
943
  /* @__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" }) })
944
+ /* @__PURE__ */ jsxs4("div", { className: "flex gap-2", children: [
945
+ /* @__PURE__ */ jsx6("button", { onClick: (e) => {
946
+ e.stopPropagation();
947
+ copyLogs();
948
+ }, className: "text-white hover:text-blue-400", title: "Copy Logs", children: /* @__PURE__ */ jsx6(Square2StackIcon2, { className: "w-4 h-4" }) }),
949
+ /* @__PURE__ */ jsx6("button", { onClick: (e) => {
950
+ e.stopPropagation();
951
+ copyLogs();
952
+ setShowDebug(false);
953
+ }, className: "text-white hover:text-red-400", title: "Copy & Close", children: /* @__PURE__ */ jsx6(XMarkIcon2, { className: "w-4 h-4" }) })
954
+ ] })
922
955
  ] }),
923
956
  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
957
  logs.length === 0 && /* @__PURE__ */ jsx6("div", { children: "No logs yet..." })