|
|
|
|
@ -5,7 +5,7 @@ import SpeechRecognition, { useSpeechRecognition } from 'react-speech-recognitio |
|
|
|
|
import { textToSpeech } from './util/tts'; |
|
|
|
|
import { gsap } from "gsap"; |
|
|
|
|
import { SplitText } from 'gsap/SplitText'; |
|
|
|
|
import { set } from 'zod'; |
|
|
|
|
import { invoke } from '@tauri-apps/api/core'; |
|
|
|
|
gsap.registerPlugin(SplitText); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -28,6 +28,8 @@ function App() { |
|
|
|
|
finalTranscript, |
|
|
|
|
listening, |
|
|
|
|
resetTranscript, |
|
|
|
|
browserSupportsSpeechRecognition, |
|
|
|
|
isMicrophoneAvailable, |
|
|
|
|
}=useSpeechRecognition(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -52,12 +54,11 @@ function App() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// add to history |
|
|
|
|
setHistory(prev => [...prev, { |
|
|
|
|
setHistory(() => [{ |
|
|
|
|
role: 'assistant', |
|
|
|
|
content: data.output_text, |
|
|
|
|
}]); |
|
|
|
|
setPrompt([ |
|
|
|
|
...prompt, |
|
|
|
|
setPrompt(()=>[ |
|
|
|
|
data.prompt, |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
@ -81,9 +82,22 @@ function App() { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function toggleAudio() { |
|
|
|
|
// console.log("onclickAudio"); |
|
|
|
|
console.log("onclickAudio", listening, browserSupportsSpeechRecognition, isMicrophoneAvailable); |
|
|
|
|
if(!browserSupportsSpeechRecognition) { |
|
|
|
|
console.warn("Browser does not support speech recognition."); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
if(!isMicrophoneAvailable) { |
|
|
|
|
console.warn("Microphone is not available."); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if(!listening){ |
|
|
|
|
SpeechRecognition.startListening({ continuous: true, language: 'zh-TW' }); |
|
|
|
|
SpeechRecognition.startListening({ continuous: true, language: 'zh-TW' }).then(() => { |
|
|
|
|
console.log("Speech recognition started."); |
|
|
|
|
}).catch(error => { |
|
|
|
|
console.error("Error starting speech recognition:", error); |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
}else{ |
|
|
|
|
SpeechRecognition.stopListening(); |
|
|
|
|
@ -129,17 +143,14 @@ function App() { |
|
|
|
|
console.log('get reply: ', data, new Date(Date.now()-startTime).toISOString().slice(11, 19)); |
|
|
|
|
|
|
|
|
|
// add to history |
|
|
|
|
setHistory(prev => [...prev, { |
|
|
|
|
role: 'assistant', |
|
|
|
|
content: data.output_text, |
|
|
|
|
}]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
setPrompt([ |
|
|
|
|
...prompt, |
|
|
|
|
data.prompt, |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
setShowProcessing(false); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// tts |
|
|
|
|
console.log('create speech:', data.output_text); |
|
|
|
|
@ -147,6 +158,11 @@ function App() { |
|
|
|
|
const audio = new Audio(audioUrl); |
|
|
|
|
|
|
|
|
|
console.log('play audio...', new Date(Date.now()-startTime).toISOString().slice(11, 19)); |
|
|
|
|
setShowProcessing(false); |
|
|
|
|
setHistory(prev => [...prev, { |
|
|
|
|
role: 'assistant', |
|
|
|
|
content: data.output_text, |
|
|
|
|
}]); |
|
|
|
|
|
|
|
|
|
audio.play().catch(error => { |
|
|
|
|
console.error('Audio playback failed:', error); |
|
|
|
|
@ -182,8 +198,10 @@ function App() { |
|
|
|
|
if(history.length === 0) return; |
|
|
|
|
|
|
|
|
|
let last_item=document.querySelector('.last_history'); |
|
|
|
|
console.log('last_item', last_item); |
|
|
|
|
|
|
|
|
|
if(!last_item) return; |
|
|
|
|
if(last_item.classList.contains('user')) return; |
|
|
|
|
console.log('last_item', last_item); |
|
|
|
|
|
|
|
|
|
let split=SplitText.create(last_item, { |
|
|
|
|
type: "chars", |
|
|
|
|
@ -195,9 +213,9 @@ function App() { |
|
|
|
|
}, { |
|
|
|
|
opacity: 1, |
|
|
|
|
y: 0, |
|
|
|
|
duration: 1, |
|
|
|
|
duration: 0.5, |
|
|
|
|
ease: "steps(1)", |
|
|
|
|
stagger: 0.05 |
|
|
|
|
stagger: 0.1 |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -239,6 +257,27 @@ function App() { |
|
|
|
|
} |
|
|
|
|
},[finalTranscript]); |
|
|
|
|
|
|
|
|
|
useEffect(()=>{ |
|
|
|
|
|
|
|
|
|
console.log('window.SpeechRecognition=', window.SpeechRecognition || window.webkitSpeechRecognition); |
|
|
|
|
|
|
|
|
|
// if (navigator.getUserMedia){ |
|
|
|
|
|
|
|
|
|
// navigator.getUserMedia({audio:true}, |
|
|
|
|
// function(stream) { |
|
|
|
|
// // start_microphone(stream); |
|
|
|
|
// console.log('Microphone access granted.'); |
|
|
|
|
// }, |
|
|
|
|
// function(e) { |
|
|
|
|
// alert('Error capturing audio.'); |
|
|
|
|
// } |
|
|
|
|
// ); |
|
|
|
|
|
|
|
|
|
// } else { alert('getUserMedia not supported in this browser.'); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
},[]); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
|
@ -261,7 +300,7 @@ function App() { |
|
|
|
|
):( |
|
|
|
|
history.map((item, index) => ( |
|
|
|
|
<div key={index} className={`p-2 rounded border-4 ${item.role === 'user' ? 'bg-gray-100' : 'bg-yellow-100'}`}> |
|
|
|
|
<p className={`text-lg whitespace-pre-wrap history_item ${index==history?.length-1 && 'last_history'}`}>{item.content}</p> |
|
|
|
|
<p className={`text-lg whitespace-pre-wrap history_item ${index==history?.length-1 && item.role!='user' && 'last_history'}`}>{item.content}</p> |
|
|
|
|
</div> |
|
|
|
|
)) |
|
|
|
|
)} |
|
|
|
|
|