Spaces:
Running
Running
Vedika commited on
Update index.html
Browse files- 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;
|
| 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;
|
| 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 |
-
|
| 835 |
-
const
|
| 836 |
-
|
| 837 |
-
|
| 838 |
-
|
|
|
|
|
|
|
|
|
|
| 839 |
try {
|
| 840 |
const response = await fetch('/api/tts', {
|
| 841 |
method: 'POST',
|
| 842 |
headers: {'Content-Type':'application/json'},
|
| 843 |
body: JSON.stringify({
|
| 844 |
-
text:
|
| 845 |
voice: this.selectedVoice,
|
| 846 |
language_name: "English"
|
| 847 |
})
|
| 848 |
});
|
| 849 |
-
|
|
|
|
| 850 |
const blob = await response.blob();
|
| 851 |
-
|
| 852 |
-
|
| 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 |
-
}
|
| 893 |
btnElement.innerHTML = `<span style="color:var(--brand-danger)">Voice Error</span>`;
|
| 894 |
setTimeout(() => { btnElement.style.display = 'none'; }, 3000);
|
| 895 |
}
|
| 896 |
},
|
| 897 |
-
|
| 898 |
-
const
|
| 899 |
-
if(!
|
| 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 |
-
|
| 930 |
-
|
| 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.
|
| 942 |
-
|
| 943 |
-
|
| 944 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 1076 |
}
|
| 1077 |
} catch(e) {}
|
| 1078 |
},
|
| 1079 |
renderSidebar(chats) {
|
| 1080 |
-
const container = document.getElementById('chatHistory');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1081 |
chats.forEach(chat => {
|
| 1082 |
const isActive = State.currentThreadId === chat.threadId ? 'active' : '';
|
| 1083 |
-
container.innerHTML += `
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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(() =>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
};
|