Vedika commited on
Commit
e2f2336
·
verified ·
1 Parent(s): 6325348

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +94 -84
index.html CHANGED
@@ -1,4 +1,3 @@
1
- <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
@@ -149,6 +148,12 @@
149
  .btn-new-chat:hover { background: rgba(255, 255, 255, 0.9); box-shadow: 0 4px 16px rgba(0,0,0,0.08); color: var(--brand-accent); border-color: var(--border-focus);}
150
 
151
  .sidebar-history { flex: 1; overflow-y: auto; padding: 10px 16px; display: flex; flex-direction: column; gap: 4px; }
 
 
 
 
 
 
152
  .history-item {
153
  padding: 10px 14px;
154
  border-radius: var(--radius-sm);
@@ -178,6 +183,7 @@
178
  .user-info-box { flex: 1; overflow: hidden; display: flex; flex-direction: column; justify-content: center; }
179
  .user-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-primary); line-height: 1.2; }
180
  .user-sub { font-size: 11.5px; font-weight: 500; color: var(--text-secondary); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2; cursor: pointer; }
 
181
  .btn-power { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: var(--transition-default); }
182
  .btn-power:hover { background: #fce8e8; color: var(--brand-danger); }
183
 
@@ -242,7 +248,7 @@
242
 
243
  .bot-content-wrapper {
244
  flex: 1 1 0%;
245
- min-width: 0; /* CRITICAL: Prevents flexbox from shrinking to character width */
246
  max-width: calc(100% - 50px);
247
  width: 100%;
248
  overflow: hidden;
@@ -255,7 +261,7 @@
255
  word-wrap: break-word;
256
  overflow-wrap: break-word;
257
  word-break: normal;
258
- white-space: pre-wrap; /* Restored to pre-wrap for Markdown formatting */
259
  width: 100%;
260
  max-width: 100%;
261
  }
@@ -586,6 +592,9 @@
586
  <div class="user-name" id="uName">Guest Session</div>
587
  <div class="user-sub" id="uSub" style="cursor:pointer; color:var(--brand-accent);" onclick="Auth.openModal()">Secure Login</div>
588
  </div>
 
 
 
589
  <button class="btn-power" id="btnLogout" onclick="Auth.handleLogout()" style="display:none;" title="Logout">
590
  <svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
591
  <path d="M18.36 6.64a9 9 0 1 1-12.73 0"></path>
@@ -796,7 +805,6 @@
796
  cache: {},
797
  currentAudio: null,
798
  selectedVoice: "M2",
799
- audioQueue: [],
800
  isPlaying: false,
801
  currentMsgId: null,
802
  currentBtnElement: null,
@@ -831,72 +839,39 @@
831
  .replace(/[*_#`~>]/g, '')
832
  .trim();
833
  },
834
- splitIntoSentences(text) {
835
- const sentences = text.match(/[^।.!?]+[।.!?]+/g) || [text];
836
- return sentences.map(s => s.trim()).filter(s => s.length > 0);
837
- },
838
- async generateAudioChunk(text, chunkIndex) {
 
 
 
839
  try {
840
  const response = await fetch('/api/tts', {
841
  method: 'POST',
842
  headers: {'Content-Type':'application/json'},
843
  body: JSON.stringify({
844
- text: text,
845
  voice: this.selectedVoice,
846
  language_name: "English"
847
  })
848
  });
849
- if(!response.ok) throw new Error(`Chunk ${chunkIndex} failed`);
 
850
  const blob = await response.blob();
851
- return URL.createObjectURL(blob);
852
- } catch(e) {
853
- return null;
854
- }
855
- },
856
- async autoPrepare(msgId, fullText, btnElement) {
857
- const cleanText = this.cleanTextForTTS(fullText);
858
- if(!cleanText) return;
859
-
860
- btnElement.style.display = 'flex';
861
- btnElement.innerHTML = `<svg class="spin-icon" style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="2" x2="12" y2="6"></line><line x1="12" y1="18" x2="12" y2="22"></line><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line><line x1="2" y1="12" x2="6" y2="12"></line><line x1="18" y1="12" x2="22" y2="12"></line><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line></svg> Preparing...`;
862
- btnElement.style.pointerEvents = 'none';
863
-
864
- const sentences = this.splitIntoSentences(cleanText);
865
- const chunkSize = 3;
866
- const chunks = [];
867
-
868
- for (let i = 0; i < sentences.length; i += chunkSize) {
869
- chunks.push(sentences.slice(i, i + chunkSize).join(' '));
870
- }
871
-
872
- if (chunks.length === 0) {
873
- btnElement.innerHTML = `<span style="color:var(--brand-danger)">No text</span>`;
874
- setTimeout(() => { btnElement.style.display = 'none'; }, 2000);
875
- return;
876
- }
877
-
878
- this.cache[msgId] = [];
879
- let successCount = 0;
880
-
881
- for (let i = 0; i < chunks.length; i++) {
882
- const audioUrl = await this.generateAudioChunk(chunks[i], i);
883
- if (audioUrl) {
884
- this.cache[msgId].push(audioUrl);
885
- successCount++;
886
- }
887
- }
888
-
889
- if (successCount > 0) {
890
  btnElement.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg> Listen`;
891
  btnElement.style.pointerEvents = 'auto';
892
- } else {
893
  btnElement.innerHTML = `<span style="color:var(--brand-danger)">Voice Error</span>`;
894
  setTimeout(() => { btnElement.style.display = 'none'; }, 3000);
895
  }
896
  },
897
- async play(msgId, btnElement) {
898
- const audioChunks = this.cache[msgId];
899
- if(!audioChunks || audioChunks.length === 0) {
900
  this.autoPrepare(msgId, document.getElementById(`bot-content-${msgId}`).innerText, btnElement);
901
  return;
902
  }
@@ -926,22 +901,24 @@
926
  btnElement.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg> Playing...`;
927
  btnElement.style.background = 'rgba(211, 227, 253, 0.9)';
928
 
929
- for (let i = 0; i < audioChunks.length; i++) {
930
- if (!this.isPlaying) break;
931
-
932
- await new Promise((resolve) => {
933
- this.currentAudio = new Audio(audioChunks[i]);
934
- this.currentAudio.playbackRate = 1.15;
935
- this.currentAudio.onended = resolve;
936
- this.currentAudio.onerror = resolve;
937
- this.currentAudio.play().catch(resolve);
938
- });
939
- }
940
 
941
- this.isPlaying = false;
942
- this.currentMsgId = null;
943
- btnElement.innerHTML = originalHTML;
944
- btnElement.style.background = '';
 
 
 
 
 
 
 
 
 
 
 
945
  }
946
  };
947
 
@@ -977,6 +954,7 @@
977
  document.getElementById('uName').innerText = dispName;
978
  document.getElementById('uSub').innerText = email;
979
  document.getElementById('btnLogout').style.display = 'flex';
 
980
 
981
  document.getElementById('welcomeTitle').innerHTML = `Hello, ${dispName}!<br>How can I help you today?`;
982
  },
@@ -1017,10 +995,18 @@
1017
  document.getElementById('uName').innerText = "Guest Mode";
1018
  document.getElementById('uSub').innerText = `Queries: ${State.guestCount}/10`;
1019
  document.getElementById('btnLogout').style.display = 'none';
 
1020
  document.getElementById('welcomeTitle').innerHTML = `Hello, Guest!<br>How can I help you today?`;
1021
  }
1022
  },
1023
  handleLogout() { if(confirm("Logout?")) { localStorage.removeItem('codeved_user'); localStorage.removeItem('codeved_name'); location.reload(); } },
 
 
 
 
 
 
 
1024
  openModal() { document.getElementById('authModal').style.display = 'flex'; },
1025
  closeModal() { document.getElementById('authModal').style.display = 'none'; },
1026
  switchTab(tab) {
@@ -1072,15 +1058,28 @@
1072
  const data = await res.json();
1073
  if(data.status === 'success') {
1074
  if(data.user) UI.updateUserInfo(data.user.name, data.user.email);
1075
- HistoryManager.renderSidebar(data.chats || []);
1076
  }
1077
  } catch(e) {}
1078
  },
1079
  renderSidebar(chats) {
1080
- const container = document.getElementById('chatHistory'); container.innerHTML = '';
 
 
 
 
 
 
1081
  chats.forEach(chat => {
1082
  const isActive = State.currentThreadId === chat.threadId ? 'active' : '';
1083
- container.innerHTML += `<div class="history-item ${isActive}" onclick="HistoryManager.loadChat('${chat.threadId}')"><span class="history-text">${UI.escape(chat.title)}</span></div>`;
 
 
 
 
 
 
 
1084
  });
1085
  },
1086
  async loadChat(threadId) {
@@ -1122,7 +1121,25 @@
1122
  const firstUser = State.history.find(m => m.role === 'user');
1123
  State.currentTitle = firstUser ? firstUser.content.substring(0, 25) : "New Workspace";
1124
  }
1125
- fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "save_chat", email: State.user, threadId: State.currentThreadId, title: State.currentTitle, historyJSON: JSON.stringify(State.history)})}).then(() => HistoryManager.syncAllChats()).catch(e=>{});
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1126
  }
1127
  };
1128
 
@@ -1385,14 +1402,6 @@
1385
 
1386
  if(json.choices && json.choices[0] && json.choices[0].delta && json.choices[0].delta.content) {
1387
  fullText += json.choices[0].delta.content;
1388
- if (!firstChunkReceived) {
1389
- firstChunkReceived = true;
1390
- if(botObj.listenBtn) {
1391
- setTimeout(() => {
1392
- TTSManager.autoPrepare(msgId, fullText, botObj.listenBtn);
1393
- }, 500);
1394
- }
1395
- }
1396
  }
1397
  }
1398
  catch(e) {}
@@ -1405,9 +1414,6 @@
1405
  State.history.push({ role: 'assistant', content: fullText });
1406
  HistoryManager.saveCurrent();
1407
 
1408
- if(botObj.listenBtn && fullText.trim().length > 0) {
1409
- TTSManager.autoPrepare(msgId, fullText, botObj.listenBtn);
1410
- }
1411
  } catch(e) {
1412
  if (e.name === 'AbortError') {
1413
  botObj.contentDiv.innerHTML += `<br><span style="color:var(--text-tertiary); font-style:italic;">⏹ Generation stopped.</span>`;
@@ -1424,6 +1430,10 @@
1424
  document.getElementById('btnSend').style.display = 'flex';
1425
  document.getElementById('btnStop').classList.remove('active');
1426
  UI.scrollToBottom();
 
 
 
 
1427
  }
1428
  }
1429
  };
 
 
1
  <html lang="en">
2
  <head>
3
  <meta charset="UTF-8">
 
148
  .btn-new-chat:hover { background: rgba(255, 255, 255, 0.9); box-shadow: 0 4px 16px rgba(0,0,0,0.08); color: var(--brand-accent); border-color: var(--border-focus);}
149
 
150
  .sidebar-history { flex: 1; overflow-y: auto; padding: 10px 16px; display: flex; flex-direction: column; gap: 4px; }
151
+
152
+ .history-header { display:flex; justify-content:space-between; align-items:center; padding: 0 4px 8px; margin-bottom:8px; border-bottom:1px solid var(--border-light); }
153
+ .history-title-text { font-size:11px; font-weight:600; color:var(--text-tertiary); text-transform:uppercase; letter-spacing:0.5px; }
154
+ .btn-clear-all { font-size:11px; font-weight:500; color:var(--text-tertiary); cursor:pointer; transition:var(--transition-default); }
155
+ .btn-clear-all:hover { color:var(--brand-danger); }
156
+
157
  .history-item {
158
  padding: 10px 14px;
159
  border-radius: var(--radius-sm);
 
183
  .user-info-box { flex: 1; overflow: hidden; display: flex; flex-direction: column; justify-content: center; }
184
  .user-name { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-primary); line-height: 1.2; }
185
  .user-sub { font-size: 11.5px; font-weight: 500; color: var(--text-secondary); margin-top: 2px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; line-height: 1.2; cursor: pointer; }
186
+
187
  .btn-power { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: var(--transition-default); }
188
  .btn-power:hover { background: #fce8e8; color: var(--brand-danger); }
189
 
 
248
 
249
  .bot-content-wrapper {
250
  flex: 1 1 0%;
251
+ min-width: 0;
252
  max-width: calc(100% - 50px);
253
  width: 100%;
254
  overflow: hidden;
 
261
  word-wrap: break-word;
262
  overflow-wrap: break-word;
263
  word-break: normal;
264
+ white-space: pre-wrap;
265
  width: 100%;
266
  max-width: 100%;
267
  }
 
592
  <div class="user-name" id="uName">Guest Session</div>
593
  <div class="user-sub" id="uSub" style="cursor:pointer; color:var(--brand-accent);" onclick="Auth.openModal()">Secure Login</div>
594
  </div>
595
+ <button class="btn-power" id="btnDeleteAcc" onclick="Auth.handleDeleteAccount()" style="display:none; color:var(--brand-danger);" title="Delete Account">
596
+ <svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 6h18M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2"></path></svg>
597
+ </button>
598
  <button class="btn-power" id="btnLogout" onclick="Auth.handleLogout()" style="display:none;" title="Logout">
599
  <svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
600
  <path d="M18.36 6.64a9 9 0 1 1-12.73 0"></path>
 
805
  cache: {},
806
  currentAudio: null,
807
  selectedVoice: "M2",
 
808
  isPlaying: false,
809
  currentMsgId: null,
810
  currentBtnElement: null,
 
839
  .replace(/[*_#`~>]/g, '')
840
  .trim();
841
  },
842
+ async autoPrepare(msgId, fullText, btnElement) {
843
+ const cleanText = this.cleanTextForTTS(fullText);
844
+ if(!cleanText) return;
845
+
846
+ btnElement.style.display = 'flex';
847
+ btnElement.innerHTML = `<svg class="spin-icon" style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="2" x2="12" y2="6"></line><line x1="12" y1="18" x2="12" y2="22"></line><line x1="4.93" y1="4.93" x2="7.76" y2="7.76"></line><line x1="16.24" y1="16.24" x2="19.07" y2="19.07"></line><line x1="2" y1="12" x2="6" y2="12"></line><line x1="18" y1="12" x2="22" y2="12"></line><line x1="4.93" y1="19.07" x2="7.76" y2="16.24"></line><line x1="16.24" y1="7.76" x2="19.07" y2="4.93"></line></svg> Preparing...`;
848
+ btnElement.style.pointerEvents = 'none';
849
+
850
  try {
851
  const response = await fetch('/api/tts', {
852
  method: 'POST',
853
  headers: {'Content-Type':'application/json'},
854
  body: JSON.stringify({
855
+ text: cleanText,
856
  voice: this.selectedVoice,
857
  language_name: "English"
858
  })
859
  });
860
+
861
+ if(!response.ok) throw new Error("TTS failed");
862
  const blob = await response.blob();
863
+ this.cache[msgId] = URL.createObjectURL(blob);
864
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865
  btnElement.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg> Listen`;
866
  btnElement.style.pointerEvents = 'auto';
867
+ } catch(e) {
868
  btnElement.innerHTML = `<span style="color:var(--brand-danger)">Voice Error</span>`;
869
  setTimeout(() => { btnElement.style.display = 'none'; }, 3000);
870
  }
871
  },
872
+ play(msgId, btnElement) {
873
+ const audioUrl = this.cache[msgId];
874
+ if(!audioUrl) {
875
  this.autoPrepare(msgId, document.getElementById(`bot-content-${msgId}`).innerText, btnElement);
876
  return;
877
  }
 
901
  btnElement.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg> Playing...`;
902
  btnElement.style.background = 'rgba(211, 227, 253, 0.9)';
903
 
904
+ this.currentAudio = new Audio(audioUrl);
905
+ this.currentAudio.playbackRate = 1.15;
 
 
 
 
 
 
 
 
 
906
 
907
+ this.currentAudio.onended = () => {
908
+ this.isPlaying = false;
909
+ this.currentMsgId = null;
910
+ btnElement.innerHTML = originalHTML;
911
+ btnElement.style.background = '';
912
+ };
913
+
914
+ this.currentAudio.onerror = () => {
915
+ this.isPlaying = false;
916
+ this.currentMsgId = null;
917
+ btnElement.innerHTML = originalHTML;
918
+ btnElement.style.background = '';
919
+ };
920
+
921
+ this.currentAudio.play();
922
  }
923
  };
924
 
 
954
  document.getElementById('uName').innerText = dispName;
955
  document.getElementById('uSub').innerText = email;
956
  document.getElementById('btnLogout').style.display = 'flex';
957
+ document.getElementById('btnDeleteAcc').style.display = 'flex';
958
 
959
  document.getElementById('welcomeTitle').innerHTML = `Hello, ${dispName}!<br>How can I help you today?`;
960
  },
 
995
  document.getElementById('uName').innerText = "Guest Mode";
996
  document.getElementById('uSub').innerText = `Queries: ${State.guestCount}/10`;
997
  document.getElementById('btnLogout').style.display = 'none';
998
+ document.getElementById('btnDeleteAcc').style.display = 'none';
999
  document.getElementById('welcomeTitle').innerHTML = `Hello, Guest!<br>How can I help you today?`;
1000
  }
1001
  },
1002
  handleLogout() { if(confirm("Logout?")) { localStorage.removeItem('codeved_user'); localStorage.removeItem('codeved_name'); location.reload(); } },
1003
+ handleDeleteAccount() {
1004
+ if(confirm("Are you sure you want to permanently delete your account and all data?")) {
1005
+ fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "delete_account", email: State.user})}).then(() => {
1006
+ localStorage.removeItem('codeved_user'); localStorage.removeItem('codeved_name'); location.reload();
1007
+ });
1008
+ }
1009
+ },
1010
  openModal() { document.getElementById('authModal').style.display = 'flex'; },
1011
  closeModal() { document.getElementById('authModal').style.display = 'none'; },
1012
  switchTab(tab) {
 
1058
  const data = await res.json();
1059
  if(data.status === 'success') {
1060
  if(data.user) UI.updateUserInfo(data.user.name, data.user.email);
1061
+ this.renderSidebar(data.chats || []);
1062
  }
1063
  } catch(e) {}
1064
  },
1065
  renderSidebar(chats) {
1066
+ const container = document.getElementById('chatHistory');
1067
+ container.innerHTML = `
1068
+ <div class="history-header">
1069
+ <span class="history-title-text">Recent Workspaces</span>
1070
+ <span class="btn-clear-all" onclick="HistoryManager.clearAll()">Clear All</span>
1071
+ </div>
1072
+ `;
1073
  chats.forEach(chat => {
1074
  const isActive = State.currentThreadId === chat.threadId ? 'active' : '';
1075
+ container.innerHTML += `
1076
+ <div class="history-item ${isActive}" onclick="HistoryManager.loadChat('${chat.threadId}')">
1077
+ <span class="history-text">${UI.escape(chat.title)}</span>
1078
+ <button class="btn-delete-chat" onclick="event.stopPropagation(); HistoryManager.deleteChat('${chat.threadId}')" title="Delete">
1079
+ <svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
1080
+ </button>
1081
+ </div>
1082
+ `;
1083
  });
1084
  },
1085
  async loadChat(threadId) {
 
1121
  const firstUser = State.history.find(m => m.role === 'user');
1122
  State.currentTitle = firstUser ? firstUser.content.substring(0, 25) : "New Workspace";
1123
  }
1124
+ fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "save_chat", email: State.user, threadId: State.currentThreadId, title: State.currentTitle, historyJSON: JSON.stringify(State.history)})}).then(() => this.syncAllChats()).catch(e=>{});
1125
+ },
1126
+ clearAll() {
1127
+ if(confirm("Clear all workspaces?")) {
1128
+ State.history = [];
1129
+ State.currentThreadId = null;
1130
+ State.currentTitle = null;
1131
+ document.getElementById('chatMessages').innerHTML = '';
1132
+ UI.updateWelcomeScreen();
1133
+ fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "clear_all_chats", email: State.user})}).then(() => this.syncAllChats());
1134
+ }
1135
+ },
1136
+ deleteChat(threadId) {
1137
+ if(confirm("Delete this workspace?")) {
1138
+ if(State.currentThreadId === threadId) {
1139
+ this.startNew();
1140
+ }
1141
+ fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "delete_chat", email: State.user, threadId: threadId})}).then(() => this.syncAllChats());
1142
+ }
1143
  }
1144
  };
1145
 
 
1402
 
1403
  if(json.choices && json.choices[0] && json.choices[0].delta && json.choices[0].delta.content) {
1404
  fullText += json.choices[0].delta.content;
 
 
 
 
 
 
 
 
1405
  }
1406
  }
1407
  catch(e) {}
 
1414
  State.history.push({ role: 'assistant', content: fullText });
1415
  HistoryManager.saveCurrent();
1416
 
 
 
 
1417
  } catch(e) {
1418
  if (e.name === 'AbortError') {
1419
  botObj.contentDiv.innerHTML += `<br><span style="color:var(--text-tertiary); font-style:italic;">⏹ Generation stopped.</span>`;
 
1430
  document.getElementById('btnSend').style.display = 'flex';
1431
  document.getElementById('btnStop').classList.remove('active');
1432
  UI.scrollToBottom();
1433
+
1434
+ if(botObj.listenBtn && fullText.trim().length > 0) {
1435
+ TTSManager.autoPrepare(msgId, fullText, botObj.listenBtn);
1436
+ }
1437
  }
1438
  }
1439
  };