navyseal4000 commited on
Commit
36b7d94
·
1 Parent(s): 95776af

Added speech to text capability

Browse files
app/components/chat/BaseChat.tsx CHANGED
@@ -87,6 +87,35 @@ interface BaseChatProps {
87
  enhancePrompt?: () => void;
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
91
  (
92
  {
@@ -114,6 +143,8 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
114
  const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200;
115
  const [apiKeys, setApiKeys] = useState<Record<string, string>>({});
116
  const [modelList, setModelList] = useState(MODEL_LIST);
 
 
117
 
118
  useEffect(() => {
119
  // Load API keys from cookies on component mount
@@ -134,8 +165,49 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
134
  initializeModelList().then((modelList) => {
135
  setModelList(modelList);
136
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  }, []);
138
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  const updateApiKey = (provider: string, key: string) => {
140
  try {
141
  const updatedApiKeys = { ...apiKeys, [provider]: key };
@@ -284,6 +356,13 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
284
  </>
285
  )}
286
  </IconButton>
 
 
 
 
 
 
 
287
  </div>
288
  {input.length > 3 ? (
289
  <div className="text-xs text-bolt-elements-textTertiary">
 
87
  enhancePrompt?: () => void;
88
  }
89
 
90
+ const SpeechRecognitionButton = ({
91
+ isListening,
92
+ onStart,
93
+ onStop,
94
+ disabled
95
+ }: {
96
+ isListening: boolean;
97
+ onStart: () => void;
98
+ onStop: () => void;
99
+ disabled: boolean;
100
+ }) => {
101
+ return (
102
+ <IconButton
103
+ title={isListening ? "Stop listening" : "Start speech recognition"}
104
+ disabled={disabled}
105
+ className={classNames('transition-all', {
106
+ 'text-bolt-elements-item-contentAccent': isListening,
107
+ })}
108
+ onClick={isListening ? onStop : onStart}
109
+ >
110
+ {isListening ? (
111
+ <div className="i-ph:microphone-slash text-xl" />
112
+ ) : (
113
+ <div className="i-ph:microphone text-xl" />
114
+ )}
115
+ </IconButton>
116
+ );
117
+ };
118
+
119
  export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
120
  (
121
  {
 
143
  const TEXTAREA_MAX_HEIGHT = chatStarted ? 400 : 200;
144
  const [apiKeys, setApiKeys] = useState<Record<string, string>>({});
145
  const [modelList, setModelList] = useState(MODEL_LIST);
146
+ const [isListening, setIsListening] = useState(false);
147
+ const [recognition, setRecognition] = useState<SpeechRecognition | null>(null);
148
 
149
  useEffect(() => {
150
  // Load API keys from cookies on component mount
 
165
  initializeModelList().then((modelList) => {
166
  setModelList(modelList);
167
  });
168
+ if (typeof window !== 'undefined' && ('SpeechRecognition' in window || 'webkitSpeechRecognition' in window)) {
169
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
170
+ const recognition = new SpeechRecognition();
171
+ recognition.continuous = true;
172
+ recognition.interimResults = true;
173
+
174
+ recognition.onresult = (event) => {
175
+ const transcript = Array.from(event.results)
176
+ .map(result => result[0])
177
+ .map(result => result.transcript)
178
+ .join('');
179
+
180
+ if (handleInputChange) {
181
+ const syntheticEvent = {
182
+ target: { value: transcript },
183
+ } as React.ChangeEvent<HTMLTextAreaElement>;
184
+ handleInputChange(syntheticEvent);
185
+ }
186
+ };
187
+
188
+ recognition.onerror = (event) => {
189
+ console.error('Speech recognition error:', event.error);
190
+ setIsListening(false);
191
+ };
192
+
193
+ setRecognition(recognition);
194
+ }
195
  }, []);
196
 
197
+ const startListening = () => {
198
+ if (recognition) {
199
+ recognition.start();
200
+ setIsListening(true);
201
+ }
202
+ };
203
+
204
+ const stopListening = () => {
205
+ if (recognition) {
206
+ recognition.stop();
207
+ setIsListening(false);
208
+ }
209
+ };
210
+
211
  const updateApiKey = (provider: string, key: string) => {
212
  try {
213
  const updatedApiKeys = { ...apiKeys, [provider]: key };
 
356
  </>
357
  )}
358
  </IconButton>
359
+
360
+ <SpeechRecognitionButton
361
+ isListening={isListening}
362
+ onStart={startListening}
363
+ onStop={stopListening}
364
+ disabled={isStreaming}
365
+ />
366
  </div>
367
  {input.length > 3 ? (
368
  <div className="text-xs text-bolt-elements-textTertiary">
app/types/global.d.ts CHANGED
@@ -1,3 +1,5 @@
1
  interface Window {
2
  showDirectoryPicker(): Promise<FileSystemDirectoryHandle>;
 
 
3
  }
 
1
  interface Window {
2
  showDirectoryPicker(): Promise<FileSystemDirectoryHandle>;
3
+ webkitSpeechRecognition: typeof SpeechRecognition;
4
+ SpeechRecognition: typeof SpeechRecognition;
5
  }