|
1 | 1 | "use client"; |
2 | 2 |
|
3 | | -import { useMicVAD, utils } from "@/lib/hooks/use-mic-vad"; |
4 | | -import { BaseLLM } from "@/lib/llm/llm"; |
5 | | -import { BaseSTT } from "@/lib/stt/stt"; |
6 | | -import { useContext, useEffect, useRef } from "react"; |
7 | | -import toast from "react-hot-toast"; |
8 | | -import { CodeEditorAgent } from "@/lib/agents/code-editor-agent"; |
9 | | -import { BaseTTS } from "@/lib/tts/tts"; |
10 | 3 | import EditorToolbar from "@/components/interface/editor-toolbar"; |
11 | 4 | import { EditorContext } from "@/components/providers/editor-context-provider"; |
| 5 | +import Voice from "@/components/tools/voice"; |
12 | 6 | import ViewDisplayArea from "@/components/views/file-view-display-area"; |
13 | | -import { useViewManager } from "@/lib/hooks/use-view-manager"; |
| 7 | +import { useContext } from "react"; |
14 | 8 |
|
15 | 9 | export default function Home() { |
16 | 10 | const editorContext = useContext(EditorContext); |
17 | | - const { updateFileView, activeFileView } = useViewManager(); |
18 | | - |
19 | | - // TODO: Use a timer to stop recorder if no speech is detected for more than 30 seconds |
20 | | - const isProcessingRef = useRef(false); |
21 | | - const vad = useMicVAD({ |
22 | | - startOnLoad: false, |
23 | | - baseAssetPath: "/vad/", |
24 | | - onnxWASMBasePath: "/vad/", |
25 | | - positiveSpeechThreshold: 0.75, |
26 | | - onSpeechStart: () => { |
27 | | - if (!isProcessingRef.current) { |
28 | | - const sttModel = editorContext?.aiModelConfig.getSTTModel(); |
29 | | - const llmModel = editorContext?.aiModelConfig.getLLMModel(); |
30 | | - const ttsModel = editorContext?.aiModelConfig.getTTSModel(); |
31 | | - let isConfigured = true; |
32 | | - if (!sttModel) { |
33 | | - toast.error("STT model is not configured in settings."); |
34 | | - isConfigured = false; |
35 | | - } |
36 | | - if (!llmModel) { |
37 | | - toast.error("LLM model is not configured in settings."); |
38 | | - isConfigured = false; |
39 | | - } |
40 | | - if (!ttsModel) { |
41 | | - toast.error("TTS model is not configured in settings."); |
42 | | - isConfigured = false; |
43 | | - } |
44 | | - |
45 | | - if (!isConfigured) { |
46 | | - return; |
47 | | - } |
48 | | - |
49 | | - editorContext?.setEditorStates((prev) => ({ |
50 | | - ...prev, |
51 | | - isListening: true, |
52 | | - })); |
53 | | - } |
54 | | - }, |
55 | | - onSpeechEnd: (audio) => { |
56 | | - if (!isProcessingRef.current) { |
57 | | - isProcessingRef.current = true; |
58 | | - const wavBuffer = utils.encodeWAV(audio); |
59 | | - const blob = new Blob([wavBuffer], { type: "audio/wav" }); |
60 | | - console.log("Speech end\n", blob); |
61 | | - |
62 | | - const sttModel = editorContext?.aiModelConfig.getSTTModel() as BaseSTT; |
63 | | - const llmModel = editorContext?.aiModelConfig.getLLMModel() as BaseLLM; |
64 | | - const ttsModel = editorContext?.aiModelConfig.getTTSModel() as BaseTTS; |
65 | | - |
66 | | - const agent = new CodeEditorAgent(sttModel, llmModel, ttsModel); |
67 | | - editorContext?.setEditorStates((prev) => ({ |
68 | | - ...prev, |
69 | | - isListening: false, |
70 | | - isThinking: true, |
71 | | - })); |
72 | | - agent |
73 | | - .generateAgentCompletion( |
74 | | - activeFileView?.fileContent || "", |
75 | | - activeFileView?.selections || [], |
76 | | - { |
77 | | - audio: blob, |
78 | | - }, |
79 | | - ) |
80 | | - .then((result) => { |
81 | | - const changes = agent.getLineChanges(result.text.codeCompletion); |
82 | | - editorContext?.setEditorStates((prev) => ({ |
83 | | - ...prev, |
84 | | - isThinking: false, |
85 | | - })); |
86 | | - |
87 | | - // Apply changes |
88 | | - updateFileView({ |
89 | | - filePath: activeFileView?.filePath || "", |
90 | | - // suggestedLines: changes, |
91 | | - fileContent: result.text.codeCompletion, |
92 | | - isActive: true, |
93 | | - }); |
94 | | - |
95 | | - // Play the audio in the blob |
96 | | - if (result.audio) { |
97 | | - const audio = new Audio(URL.createObjectURL(result.audio)); |
98 | | - audio.onended = () => { |
99 | | - console.log("Audio ended"); |
100 | | - editorContext?.setEditorStates((prev) => ({ |
101 | | - ...prev, |
102 | | - isSpeaking: false, |
103 | | - })); |
104 | | - isProcessingRef.current = false; |
105 | | - }; |
106 | | - editorContext?.setEditorStates((prev) => ({ |
107 | | - ...prev, |
108 | | - isSpeaking: true, |
109 | | - })); |
110 | | - audio.play(); |
111 | | - return; |
112 | | - } |
113 | | - isProcessingRef.current = false; |
114 | | - }); |
115 | | - } |
116 | | - }, |
117 | | - }); |
118 | | - |
119 | | - // Toggle recording |
120 | | - useEffect(() => { |
121 | | - if (editorContext?.editorStates?.isRecording) { |
122 | | - vad.start(); |
123 | | - } else { |
124 | | - vad.stop(); |
125 | | - } |
126 | | - }, [editorContext?.editorStates, vad]); |
127 | 11 |
|
128 | 12 | return ( |
129 | 13 | <div className="flex h-full w-full flex-col"> |
130 | 14 | <EditorToolbar /> |
131 | 15 | <ViewDisplayArea /> |
| 16 | + |
| 17 | + {editorContext?.editorStates.isRecording && <Voice />} |
132 | 18 | </div> |
133 | 19 | ); |
134 | 20 | } |
0 commit comments