vikramronavrsc commited on
Commit
6d9e7a8
·
verified ·
1 Parent(s): 9845176

Update voice_component.py

Browse files
Files changed (1) hide show
  1. voice_component.py +137 -48
voice_component.py CHANGED
@@ -4,81 +4,159 @@ import streamlit.components.v1 as components
4
 
5
  def voice_input_component():
6
  """
7
- Creates a voice input component that uses the browser's Web Speech API.
8
  Returns the transcribed text from voice input.
9
  """
10
- # HTML component for voice input
11
  voice_html = """
12
  <div class="voice-input-container">
13
- <button id="start-voice" class="voice-button">
14
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="mic-icon"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>
15
- Start Voice Input
16
- </button>
17
- <div id="voice-status" class="voice-status">Click to start speaking</div>
18
- <div id="transcript" class="transcript"></div>
19
- <button id="send-transcript" class="send-button" style="display:none;">
20
- Send
21
  </button>
 
 
 
 
 
 
 
 
22
  </div>
23
 
24
  <style>
25
  .voice-input-container {
 
 
 
 
26
  padding: 15px;
27
- border-radius: 8px;
28
- background-color: #f8f9fa;
29
  margin-bottom: 15px;
30
- box-shadow: 0 1px 3px rgba(0,0,0,0.1);
31
  }
32
  .voice-button {
33
- display: flex;
34
- align-items: center;
35
- justify-content: center;
36
- gap: 8px;
37
  background-color: #ff4b4b;
38
  color: white;
39
  border: none;
40
- padding: 10px 15px;
41
- border-radius: 20px;
42
  cursor: pointer;
43
- font-weight: 500;
44
- transition: background-color 0.3s;
 
 
 
45
  }
46
  .voice-button:hover {
47
- background-color: #e63e3e;
 
48
  }
49
  .voice-button.listening {
50
  background-color: #ff7a7a;
 
 
 
 
 
 
51
  animation: pulse 1.5s infinite;
52
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53
  @keyframes pulse {
54
- 0% { box-shadow: 0 0 0 0 rgba(255, 75, 75, 0.7); }
55
- 70% { box-shadow: 0 0 0 10px rgba(255, 75, 75, 0); }
56
- 100% { box-shadow: 0 0 0 0 rgba(255, 75, 75, 0); }
57
  }
58
  .voice-status {
59
- margin-top: 10px;
60
- font-size: 14px;
61
  color: #6c757d;
 
 
 
 
 
 
 
 
 
 
62
  }
63
  .transcript {
64
- margin-top: 15px;
65
- min-height: 60px;
66
- padding: 10px;
67
- border-radius: 4px;
68
  border: 1px solid #dee2e6;
69
- display: none;
 
 
 
 
70
  }
71
  .send-button {
72
- margin-top: 10px;
 
 
 
 
73
  background-color: #4CAF50;
74
  color: white;
75
  border: none;
76
  padding: 8px 16px;
77
- border-radius: 4px;
78
  cursor: pointer;
 
 
 
79
  }
80
  .send-button:hover {
81
  background-color: #45a049;
 
 
 
 
 
 
82
  }
83
  </style>
84
 
@@ -97,7 +175,7 @@ def voice_input_component():
97
  function initSpeechRecognition() {
98
  if (!checkSpeechRecognitionSupport()) {
99
  document.getElementById('voice-status').textContent = 'Speech recognition not supported in this browser';
100
- document.getElementById('start-voice').disabled = true;
101
  return;
102
  }
103
 
@@ -122,11 +200,10 @@ def voice_input_component():
122
 
123
  const transcriptElement = document.getElementById('transcript');
124
  transcriptElement.textContent = finalTranscript + interimTranscript;
125
- transcriptElement.style.display = 'block';
126
 
127
- // Show send button if we have text
128
- if (finalTranscript.trim() !== '') {
129
- document.getElementById('send-transcript').style.display = 'block';
130
  }
131
 
132
  // Send transcript to Streamlit component
@@ -140,8 +217,10 @@ def voice_input_component():
140
  recognition.onerror = function(event) {
141
  if (event.error === 'no-speech') {
142
  document.getElementById('voice-status').textContent = 'No speech detected. Try again.';
 
143
  } else {
144
  document.getElementById('voice-status').textContent = 'Error: ' + event.error;
 
145
  console.error('Speech recognition error:', event.error);
146
  }
147
  stopRecognition();
@@ -162,11 +241,14 @@ def voice_input_component():
162
  recognizing = true;
163
  finalTranscript = '';
164
  document.getElementById('voice-status').textContent = 'Listening...';
165
- document.getElementById('start-voice').textContent = 'Stop Listening';
166
- document.getElementById('start-voice').classList.add('listening');
 
 
167
  } catch (e) {
168
  console.error('Error starting speech recognition:', e);
169
  document.getElementById('voice-status').textContent = 'Error starting speech recognition';
 
170
  }
171
  }
172
 
@@ -176,9 +258,9 @@ def voice_input_component():
176
 
177
  recognition.stop();
178
  recognizing = false;
179
- document.getElementById('voice-status').textContent = 'Click to start speaking';
180
- document.getElementById('start-voice').textContent = 'Start Voice Input';
181
- document.getElementById('start-voice').classList.remove('listening');
182
  }
183
 
184
  // Toggle recognition
@@ -203,8 +285,15 @@ def voice_input_component():
203
  // Reset UI
204
  finalTranscript = '';
205
  document.getElementById('transcript').textContent = '';
206
- document.getElementById('transcript').style.display = 'none';
207
- document.getElementById('send-transcript').style.display = 'none';
 
 
 
 
 
 
 
208
  }
209
 
210
  // Initialize after DOM loaded
@@ -213,7 +302,7 @@ def voice_input_component():
213
  initSpeechRecognition();
214
 
215
  // Add event listeners
216
- document.getElementById('start-voice').addEventListener('click', toggleRecognition);
217
  document.getElementById('send-transcript').addEventListener('click', sendTranscript);
218
  });
219
  </script>
@@ -227,7 +316,7 @@ def voice_input_component():
227
  st.session_state[key] = ""
228
 
229
  # Render the component
230
- components.html(voice_html, height=200)
231
 
232
  # Return the last transcript
233
  return st.session_state.get(key, "")
 
4
 
5
  def voice_input_component():
6
  """
7
+ Creates a streamlined voice input component with animations.
8
  Returns the transcribed text from voice input.
9
  """
10
+ # HTML component for voice input with animations
11
  voice_html = """
12
  <div class="voice-input-container">
13
+ <button id="voice-button" class="voice-button">
14
+ <div class="mic-icon">
15
+ <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" y1="19" x2="12" y2="22"></line></svg>
16
+ </div>
17
+ <div class="ripple-container">
18
+ <div class="ripple"></div>
19
+ </div>
 
20
  </button>
21
+ <div id="voice-status" class="voice-status">Ask a question</div>
22
+ <div id="transcript-container" class="transcript-container" style="display:none;">
23
+ <div id="transcript" class="transcript"></div>
24
+ <button id="send-transcript" class="send-button">
25
+ <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="22" y1="2" x2="11" y2="13"></line><polygon points="22 2 15 22 11 13 2 9 22 2"></polygon></svg>
26
+ Send
27
+ </button>
28
+ </div>
29
  </div>
30
 
31
  <style>
32
  .voice-input-container {
33
+ display: flex;
34
+ flex-direction: column;
35
+ align-items: center;
36
+ justify-content: center;
37
  padding: 15px;
 
 
38
  margin-bottom: 15px;
 
39
  }
40
  .voice-button {
41
+ position: relative;
42
+ width: 60px;
43
+ height: 60px;
44
+ border-radius: 50%;
45
  background-color: #ff4b4b;
46
  color: white;
47
  border: none;
 
 
48
  cursor: pointer;
49
+ display: flex;
50
+ align-items: center;
51
+ justify-content: center;
52
+ transition: all 0.3s ease;
53
+ box-shadow: 0 4px 10px rgba(255, 75, 75, 0.3);
54
  }
55
  .voice-button:hover {
56
+ transform: scale(1.05);
57
+ box-shadow: 0 6px 15px rgba(255, 75, 75, 0.4);
58
  }
59
  .voice-button.listening {
60
  background-color: #ff7a7a;
61
+ }
62
+ .mic-icon {
63
+ z-index: 2;
64
+ transition: all 0.3s ease;
65
+ }
66
+ .voice-button.listening .mic-icon {
67
  animation: pulse 1.5s infinite;
68
  }
69
+ .ripple-container {
70
+ position: absolute;
71
+ width: 100%;
72
+ height: 100%;
73
+ border-radius: 50%;
74
+ z-index: 1;
75
+ display: flex;
76
+ align-items: center;
77
+ justify-content: center;
78
+ overflow: hidden;
79
+ }
80
+ .ripple {
81
+ position: absolute;
82
+ width: 100%;
83
+ height: 100%;
84
+ border-radius: 50%;
85
+ background-color: rgba(255, 255, 255, 0.3);
86
+ transform: scale(0);
87
+ opacity: 1;
88
+ }
89
+ .voice-button.listening .ripple {
90
+ animation: ripple 2s linear infinite;
91
+ }
92
+ @keyframes ripple {
93
+ 0% {
94
+ transform: scale(0);
95
+ opacity: 1;
96
+ }
97
+ 50% {
98
+ transform: scale(1.5);
99
+ opacity: 0.3;
100
+ }
101
+ 100% {
102
+ transform: scale(2);
103
+ opacity: 0;
104
+ }
105
+ }
106
  @keyframes pulse {
107
+ 0% { transform: scale(1); }
108
+ 50% { transform: scale(1.1); }
109
+ 100% { transform: scale(1); }
110
  }
111
  .voice-status {
112
+ margin-top: 15px;
113
+ font-size: 16px;
114
  color: #6c757d;
115
+ font-weight: 500;
116
+ transition: all 0.3s ease;
117
+ }
118
+ .transcript-container {
119
+ width: 100%;
120
+ max-width: 600px;
121
+ margin-top: 20px;
122
+ display: flex;
123
+ flex-direction: column;
124
+ animation: fadeIn 0.3s ease;
125
  }
126
  .transcript {
127
+ padding: 15px;
128
+ border-radius: 8px;
 
 
129
  border: 1px solid #dee2e6;
130
+ background-color: #f8f9fa;
131
+ font-size: 16px;
132
+ margin-bottom: 10px;
133
+ min-height: 60px;
134
+ width: 100%;
135
  }
136
  .send-button {
137
+ display: flex;
138
+ align-items: center;
139
+ justify-content: center;
140
+ gap: 8px;
141
+ align-self: flex-end;
142
  background-color: #4CAF50;
143
  color: white;
144
  border: none;
145
  padding: 8px 16px;
146
+ border-radius: 20px;
147
  cursor: pointer;
148
+ font-weight: 500;
149
+ transition: all 0.3s ease;
150
+ box-shadow: 0 2px 5px rgba(76, 175, 80, 0.3);
151
  }
152
  .send-button:hover {
153
  background-color: #45a049;
154
+ transform: translateY(-2px);
155
+ box-shadow: 0 4px 8px rgba(76, 175, 80, 0.4);
156
+ }
157
+ @keyframes fadeIn {
158
+ from { opacity: 0; transform: translateY(10px); }
159
+ to { opacity: 1; transform: translateY(0); }
160
  }
161
  </style>
162
 
 
175
  function initSpeechRecognition() {
176
  if (!checkSpeechRecognitionSupport()) {
177
  document.getElementById('voice-status').textContent = 'Speech recognition not supported in this browser';
178
+ document.getElementById('voice-button').disabled = true;
179
  return;
180
  }
181
 
 
200
 
201
  const transcriptElement = document.getElementById('transcript');
202
  transcriptElement.textContent = finalTranscript + interimTranscript;
 
203
 
204
+ // Show transcript container if we have text
205
+ if ((finalTranscript + interimTranscript).trim() !== '') {
206
+ document.getElementById('transcript-container').style.display = 'flex';
207
  }
208
 
209
  // Send transcript to Streamlit component
 
217
  recognition.onerror = function(event) {
218
  if (event.error === 'no-speech') {
219
  document.getElementById('voice-status').textContent = 'No speech detected. Try again.';
220
+ document.getElementById('voice-status').style.color = '#dc3545';
221
  } else {
222
  document.getElementById('voice-status').textContent = 'Error: ' + event.error;
223
+ document.getElementById('voice-status').style.color = '#dc3545';
224
  console.error('Speech recognition error:', event.error);
225
  }
226
  stopRecognition();
 
241
  recognizing = true;
242
  finalTranscript = '';
243
  document.getElementById('voice-status').textContent = 'Listening...';
244
+ document.getElementById('voice-status').style.color = '#4CAF50';
245
+ document.getElementById('voice-button').classList.add('listening');
246
+ document.getElementById('transcript-container').style.display = 'none';
247
+ document.getElementById('transcript').textContent = '';
248
  } catch (e) {
249
  console.error('Error starting speech recognition:', e);
250
  document.getElementById('voice-status').textContent = 'Error starting speech recognition';
251
+ document.getElementById('voice-status').style.color = '#dc3545';
252
  }
253
  }
254
 
 
258
 
259
  recognition.stop();
260
  recognizing = false;
261
+ document.getElementById('voice-status').textContent = 'Ask a question';
262
+ document.getElementById('voice-status').style.color = '#6c757d';
263
+ document.getElementById('voice-button').classList.remove('listening');
264
  }
265
 
266
  // Toggle recognition
 
285
  // Reset UI
286
  finalTranscript = '';
287
  document.getElementById('transcript').textContent = '';
288
+ document.getElementById('transcript-container').style.display = 'none';
289
+ document.getElementById('voice-status').textContent = 'Question sent!';
290
+ document.getElementById('voice-status').style.color = '#4CAF50';
291
+
292
+ // Reset to default state after a delay
293
+ setTimeout(() => {
294
+ document.getElementById('voice-status').textContent = 'Ask a question';
295
+ document.getElementById('voice-status').style.color = '#6c757d';
296
+ }, 2000);
297
  }
298
 
299
  // Initialize after DOM loaded
 
302
  initSpeechRecognition();
303
 
304
  // Add event listeners
305
+ document.getElementById('voice-button').addEventListener('click', toggleRecognition);
306
  document.getElementById('send-transcript').addEventListener('click', sendTranscript);
307
  });
308
  </script>
 
316
  st.session_state[key] = ""
317
 
318
  # Render the component
319
+ components.html(voice_html, height=250)
320
 
321
  # Return the last transcript
322
  return st.session_state.get(key, "")