pvanand commited on
Commit
3cbf391
·
verified ·
1 Parent(s): d78b8c4
Files changed (1) hide show
  1. static/followup-agent.html +136 -22
static/followup-agent.html CHANGED
@@ -8,9 +8,11 @@
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js"></script>
10
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
 
 
11
  <style>
12
  :root {
13
- --accent-color: #003366; /* Navy blue */
14
  }
15
  body {
16
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
@@ -27,9 +29,9 @@
27
  margin-top: 0;
28
  }
29
  #app {
30
- max-width: 900px;
31
  margin: 0 auto;
32
- padding: 20px;
33
  }
34
  .chat-container {
35
  background-color: #ffffff;
@@ -38,7 +40,7 @@
38
  overflow: hidden;
39
  display: flex;
40
  flex-direction: column;
41
- height: 98vh;
42
  }
43
  .messages {
44
  flex-grow: 1;
@@ -76,7 +78,7 @@
76
  }
77
  .input-area {
78
  display: flex;
79
- padding: 20px;
80
  background-color: #ffffff;
81
  border-top: 1px solid #e0e0e0;
82
  }
@@ -130,13 +132,13 @@
130
  margin-bottom: 15px;
131
  }
132
  .option-button {
133
- background-color: #f0f2f5;
134
- border: 1px solid #d0d0d0;
135
- border-radius: 20px;
136
- padding: 8px 16px;
137
- font-size: 14px;
138
- cursor: pointer;
139
- transition: background-color 0.3s, transform 0.1s;
140
  }
141
  .option-button:hover {
142
  background-color: #e4e6e9;
@@ -149,6 +151,35 @@
149
  background-color: #9fb4cd;
150
  border-color: #c9d9ec;
151
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
  @media (max-width: 600px) {
153
  #app {
154
  padding: 10px;
@@ -167,8 +198,17 @@
167
  <div class="chat-container">
168
  <div class="messages" ref="messageContainer">
169
  <div v-for="(message, index) in messages" :key="index"
170
- :class="['message', message.type === 'user' ? 'user-message' : 'bot-message']"
171
- v-html="message.content">
 
 
 
 
 
 
 
 
 
172
  </div>
173
  </div>
174
  <div class="input-area">
@@ -186,6 +226,13 @@
186
 
187
  <script>
188
  marked.setOptions({
 
 
 
 
 
 
 
189
  sanitize: false
190
  });
191
 
@@ -196,6 +243,7 @@
196
  userInput: '',
197
  selectedOptions: {},
198
  conversationId: '',
 
199
  },
200
  methods: {
201
  async sendMessage() {
@@ -234,7 +282,6 @@
234
  this.$set(this.messages, streamingIndex, { type: 'bot', content: marked.parse(rawResponse) });
235
  }
236
 
237
- // Remove the streaming message
238
  this.messages.splice(streamingIndex, 1);
239
 
240
  const jsonStart = rawResponse.lastIndexOf('\n\n');
@@ -249,11 +296,13 @@
249
  }
250
  this.$nextTick(() => this.scrollToBottom());
251
  },
252
- renderParsedResponse(parsedResponse) {
253
  let botResponse = '';
 
254
 
255
  if (parsedResponse.response) {
256
  botResponse += parsedResponse.response + '\n\n';
 
257
  }
258
 
259
  if (parsedResponse.clarification) {
@@ -269,12 +318,29 @@
269
  }
270
 
271
  if (botResponse) {
272
- this.messages.push({ type: 'bot', content: marked.parse(botResponse) });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
273
  }
274
- this.$nextTick(() => {
275
- this.scrollToBottom();
276
- this.updateButtonStates();
277
- });
278
  },
279
  toggleOption(option, questionIndex, optionIndex) {
280
  if (!this.selectedOptions[questionIndex]) {
@@ -319,12 +385,60 @@
319
  scrollToBottom() {
320
  const container = this.$refs.messageContainer;
321
  container.scrollTop = container.scrollHeight;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
322
  }
323
  },
324
  mounted() {
325
  this.resetConversation();
326
  }
327
  });
328
- </script>
329
  </body>
330
  </html>
 
8
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
9
  <script src="https://cdnjs.cloudflare.com/ajax/libs/uuid/8.3.2/uuid.min.js"></script>
10
  <link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
11
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/styles/default.min.css">
12
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.5.1/highlight.min.js"></script>
13
  <style>
14
  :root {
15
+ --accent-color: #003366; /* Navy blue */
16
  }
17
  body {
18
  font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
 
29
  margin-top: 0;
30
  }
31
  #app {
32
+ max-width: 800px;
33
  margin: 0 auto;
34
+ padding: 10px;
35
  }
36
  .chat-container {
37
  background-color: #ffffff;
 
40
  overflow: hidden;
41
  display: flex;
42
  flex-direction: column;
43
+ height: 99vh;
44
  }
45
  .messages {
46
  flex-grow: 1;
 
78
  }
79
  .input-area {
80
  display: flex;
81
+ padding: 10px;
82
  background-color: #ffffff;
83
  border-top: 1px solid #e0e0e0;
84
  }
 
132
  margin-bottom: 15px;
133
  }
134
  .option-button {
135
+ background-color: #f0f2f5;
136
+ border: 1px solid #d0d0d0;
137
+ border-radius: 20px;
138
+ padding: 8px 16px;
139
+ font-size: 14px;
140
+ cursor: pointer;
141
+ transition: background-color 0.3s, transform 0.1s;
142
  }
143
  .option-button:hover {
144
  background-color: #e4e6e9;
 
151
  background-color: #9fb4cd;
152
  border-color: #c9d9ec;
153
  }
154
+ .play-button {
155
+ background-color: #4CAF50;
156
+ border: none;
157
+ color: white;
158
+ padding: 10px 20px;
159
+ text-align: center;
160
+ text-decoration: none;
161
+ display: inline-block;
162
+ font-size: 16px;
163
+ margin: 4px 2px;
164
+ cursor: pointer;
165
+ border-radius: 4px;
166
+ }
167
+ .audio-button {
168
+ background: none;
169
+ border: none;
170
+ cursor: pointer;
171
+ padding: 0;
172
+ margin-top: 10px;
173
+ }
174
+
175
+ .audio-button svg {
176
+ width: 24px;
177
+ height: 24px;
178
+ }
179
+
180
+ .audio-button:focus {
181
+ outline: none;
182
+ }
183
  @media (max-width: 600px) {
184
  #app {
185
  padding: 10px;
 
198
  <div class="chat-container">
199
  <div class="messages" ref="messageContainer">
200
  <div v-for="(message, index) in messages" :key="index"
201
+ :class="['message', message.type === 'user' ? 'user-message' : 'bot-message']">
202
+ <div v-html="message.content"></div>
203
+ <button v-if="message.type === 'bot' && message.audio" @click="toggleAudio(index)" class="audio-button">
204
+ <svg v-if="!message.isPlaying" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
205
+ <polygon points="5 3 19 12 5 21 5 3"></polygon>
206
+ </svg>
207
+ <svg v-else xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
208
+ <rect x="6" y="4" width="4" height="16"></rect>
209
+ <rect x="14" y="4" width="4" height="16"></rect>
210
+ </svg>
211
+ </button>
212
  </div>
213
  </div>
214
  <div class="input-area">
 
226
 
227
  <script>
228
  marked.setOptions({
229
+ highlight: function (code, lang) {
230
+ if (lang && hljs.getLanguage(lang)) {
231
+ return hljs.highlight(code, { language: lang }).value;
232
+ } else {
233
+ return hljs.highlightAuto(code).value;
234
+ }
235
+ },
236
  sanitize: false
237
  });
238
 
 
243
  userInput: '',
244
  selectedOptions: {},
245
  conversationId: '',
246
+ currentAudio: null,
247
  },
248
  methods: {
249
  async sendMessage() {
 
282
  this.$set(this.messages, streamingIndex, { type: 'bot', content: marked.parse(rawResponse) });
283
  }
284
 
 
285
  this.messages.splice(streamingIndex, 1);
286
 
287
  const jsonStart = rawResponse.lastIndexOf('\n\n');
 
296
  }
297
  this.$nextTick(() => this.scrollToBottom());
298
  },
299
+ async renderParsedResponse(parsedResponse) {
300
  let botResponse = '';
301
+ let responseForAudio = '';
302
 
303
  if (parsedResponse.response) {
304
  botResponse += parsedResponse.response + '\n\n';
305
+ responseForAudio = parsedResponse.response;
306
  }
307
 
308
  if (parsedResponse.clarification) {
 
318
  }
319
 
320
  if (botResponse) {
321
+ const markedContent = marked.parse(botResponse);
322
+ const messageIndex = this.messages.push({
323
+ type: 'bot',
324
+ content: markedContent,
325
+ audio: null,
326
+ isPlaying: false
327
+ }) - 1;
328
+ this.$nextTick(async () => {
329
+ document.querySelectorAll('pre code').forEach((block) => {
330
+ hljs.highlightBlock(block);
331
+ });
332
+ this.scrollToBottom();
333
+ this.updateButtonStates();
334
+
335
+ if (responseForAudio) {
336
+ const audioUrl = await this.convertToSpeech(responseForAudio);
337
+ if (audioUrl) {
338
+ this.$set(this.messages[messageIndex], 'audio', audioUrl);
339
+ this.$forceUpdate();
340
+ }
341
+ }
342
+ });
343
  }
 
 
 
 
344
  },
345
  toggleOption(option, questionIndex, optionIndex) {
346
  if (!this.selectedOptions[questionIndex]) {
 
385
  scrollToBottom() {
386
  const container = this.$refs.messageContainer;
387
  container.scrollTop = container.scrollHeight;
388
+ },
389
+ async convertToSpeech(text) {
390
+ const voice = 'en-US-JennyNeural';
391
+ const encodedText = encodeURIComponent(text);
392
+ try {
393
+ const response = await fetch(`https://pvanand-audio-chat.hf.space/tts?text=${encodedText}&voice=${voice}`, {
394
+ method: 'GET',
395
+ headers: {
396
+ 'accept': 'application/json'
397
+ }
398
+ });
399
+ if (!response.ok) {
400
+ throw new Error(`HTTP error! status: ${response.status}`);
401
+ }
402
+ const blob = await response.blob();
403
+ return URL.createObjectURL(blob);
404
+ } catch (error) {
405
+ console.error('Error:', error);
406
+ return null;
407
+ }
408
+ },
409
+ toggleAudio(index) {
410
+ const message = this.messages[index];
411
+ if (this.currentAudio && this.currentAudio !== message.audioElement) {
412
+ this.currentAudio.pause();
413
+ this.messages.forEach(m => {
414
+ if (m.audioElement === this.currentAudio) {
415
+ m.isPlaying = false;
416
+ }
417
+ });
418
+ }
419
+
420
+ if (!message.audioElement) {
421
+ message.audioElement = new Audio(message.audio);
422
+ message.audioElement.addEventListener('ended', () => {
423
+ message.isPlaying = false;
424
+ this.$forceUpdate();
425
+ });
426
+ }
427
+
428
+ if (message.isPlaying) {
429
+ message.audioElement.pause();
430
+ } else {
431
+ message.audioElement.play();
432
+ this.currentAudio = message.audioElement;
433
+ }
434
+ message.isPlaying = !message.isPlaying;
435
+ this.$forceUpdate();
436
  }
437
  },
438
  mounted() {
439
  this.resetConversation();
440
  }
441
  });
442
+ </script>
443
  </body>
444
  </html>