Spaces:
Running
Running
Update index.html
Browse files- index.html +80 -103
index.html
CHANGED
|
@@ -37,8 +37,7 @@
|
|
| 37 |
--brand-accent: #3b82f6;
|
| 38 |
--brand-success: #10b981;
|
| 39 |
--brand-danger: #ef4444;
|
| 40 |
-
--brand-warning: #
|
| 41 |
-
--brand-weather: #0ea5e9; /* Sky blue for weather */
|
| 42 |
|
| 43 |
--font-ui: 'Inter', sans-serif;
|
| 44 |
--font-code: 'JetBrains Mono', monospace;
|
|
@@ -121,13 +120,11 @@
|
|
| 121 |
/* --- CHAT CONTAINER --- */
|
| 122 |
.chat-container { flex: 1; overflow-y: auto; padding: 60px 20px 140px; display: flex; flex-direction: column; align-items: center; scroll-behavior: smooth; }
|
| 123 |
|
| 124 |
-
/* Welcome Center Layout */
|
| 125 |
.welcome-center { margin: auto; text-align: center; display: flex; flex-direction: column; align-items: center; animation: fadeIn 0.4s ease; width: 100%; justify-content: center; flex: 1; }
|
| 126 |
.welcome-center img { width: 68px; height: 68px; border-radius: 18px; margin-bottom: 20px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); object-fit: cover; }
|
| 127 |
.welcome-center h1 { font-size: 24px; font-weight: 500; color: var(--text-primary); margin-bottom: 6px; letter-spacing: -0.5px; }
|
| 128 |
.welcome-center p { font-size: 14px; color: var(--text-secondary); }
|
| 129 |
|
| 130 |
-
/* Chat Messages Wrapper */
|
| 131 |
#chatMessages { width: 100%; display: flex; flex-direction: column; align-items: center; }
|
| 132 |
|
| 133 |
.message-wrapper { width: 100%; max-width: 780px; margin-bottom: 32px; display: flex; flex-direction: column; }
|
|
@@ -163,7 +160,7 @@
|
|
| 163 |
.qwen-think-box[open] summary svg.arrow { transform: rotate(90deg); }
|
| 164 |
.qwen-think-content { padding: 14px; border-top: 1px solid var(--border-light); font-size: 13.5px; color: var(--text-secondary); font-style: italic; background: #fff; line-height: 1.6; }
|
| 165 |
|
| 166 |
-
/* --- ✨ SEARCH & LOCATION ANIMATIONS --- */
|
| 167 |
.action-status {
|
| 168 |
display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px;
|
| 169 |
background: var(--bg-sidebar); border-radius: 12px; font-size: 13px; font-weight: 500;
|
|
@@ -174,7 +171,7 @@
|
|
| 174 |
.flicker-text { animation: pulseOpacity 1.2s infinite alternate; }
|
| 175 |
|
| 176 |
@keyframes pulseOpacity {
|
| 177 |
-
0% { opacity: 0.
|
| 178 |
100% { opacity: 1; }
|
| 179 |
}
|
| 180 |
|
|
@@ -226,8 +223,7 @@
|
|
| 226 |
.tool-btn:hover { background: var(--bg-sidebar); color: var(--text-primary); }
|
| 227 |
|
| 228 |
/* 🎨 DYNAMIC COLORS */
|
| 229 |
-
.tool-btn.active-
|
| 230 |
-
.tool-btn.active-weather { color: var(--brand-weather); background: #e0f2fe; }
|
| 231 |
|
| 232 |
.tool-btn.active-think-low { color: var(--brand-danger); background: #fee2e2; }
|
| 233 |
.tool-btn.active-think-medium { color: var(--brand-warning); background: #fef3c7; }
|
|
@@ -319,10 +315,7 @@
|
|
| 319 |
<svg style="width:14px; height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
| 320 |
New Workspace
|
| 321 |
</button>
|
| 322 |
-
|
| 323 |
-
<div class="sidebar-history" id="chatHistory">
|
| 324 |
-
<!-- Threads will load here dynamically -->
|
| 325 |
-
</div>
|
| 326 |
|
| 327 |
<div class="sidebar-footer">
|
| 328 |
<div class="user-avatar" id="uAv">G</div>
|
|
@@ -338,20 +331,19 @@
|
|
| 338 |
|
| 339 |
<main class="main-area">
|
| 340 |
<div class="chat-container" id="chatContainer">
|
| 341 |
-
<!-- 🌟
|
| 342 |
<div class="welcome-center" id="welcomeScreen">
|
| 343 |
<img src="https://i.ibb.co/MyYStcGP/TIRANGA-20260613-131924-0000.png" alt="CODE VED Logo">
|
| 344 |
<h1>CODE VED</h1>
|
| 345 |
<p id="welcomeGreeting">Engineered by Divy Patel</p>
|
| 346 |
</div>
|
| 347 |
|
| 348 |
-
<
|
| 349 |
-
|
| 350 |
-
</div>
|
| 351 |
</div>
|
| 352 |
|
| 353 |
<div class="input-dock" id="inputDock">
|
| 354 |
-
<!-- Status Toast -->
|
| 355 |
<div class="loc-toast" id="locStatus"></div>
|
| 356 |
|
| 357 |
<div class="input-container">
|
|
@@ -379,14 +371,14 @@
|
|
| 379 |
<input type="file" id="docUpload" class="hidden-input" accept=".pdf,.txt,.docx,.html,.js,.py,.css,.cpp,.c,.json,.md" onchange="FileSys.process(this, 'document')">
|
| 380 |
</div>
|
| 381 |
|
| 382 |
-
<!--
|
| 383 |
-
<button class="tool-btn" id="
|
| 384 |
-
<svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><
|
| 385 |
</button>
|
| 386 |
|
| 387 |
-
<!--
|
| 388 |
-
<button class="tool-btn" id="
|
| 389 |
-
<svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"
|
| 390 |
</button>
|
| 391 |
|
| 392 |
<!-- Thinking Effort Menu -->
|
|
@@ -413,7 +405,7 @@
|
|
| 413 |
</div>
|
| 414 |
</main>
|
| 415 |
|
| 416 |
-
<!-- WORKSPACE PANEL -->
|
| 417 |
<aside class="workspace-panel" id="workspacePanel">
|
| 418 |
<div class="ws-header">
|
| 419 |
<div class="ws-tabs">
|
|
@@ -491,9 +483,9 @@
|
|
| 491 |
currentWs: null,
|
| 492 |
abortController: null,
|
| 493 |
location: null,
|
|
|
|
| 494 |
thinkingMode: false,
|
| 495 |
thinkingEffort: "medium",
|
| 496 |
-
weatherContext: null, // 🌤️ Added for Frontend Weather Injection
|
| 497 |
lastUserMessage: null,
|
| 498 |
currentThreadId: null,
|
| 499 |
currentTitle: null
|
|
@@ -533,7 +525,7 @@
|
|
| 533 |
document.getElementById('btnLogout').style.display = 'flex';
|
| 534 |
document.getElementById('welcomeGreeting').innerText = `Ready, ${dispName}.`;
|
| 535 |
},
|
| 536 |
-
// 🌟
|
| 537 |
updateWelcomeScreen: () => {
|
| 538 |
const ws = document.getElementById('welcomeScreen');
|
| 539 |
if(State.history.length === 0) {
|
|
@@ -606,11 +598,8 @@
|
|
| 606 |
else if (action === 'login_send_otp') Auth.switchPhase('loginPhase1', 'loginPhase2');
|
| 607 |
else {
|
| 608 |
localStorage.setItem('codeved_user', payload.email);
|
| 609 |
-
if(data.user && data.user.name)
|
| 610 |
-
|
| 611 |
-
} else if (payload.name) {
|
| 612 |
-
localStorage.setItem('codeved_name', payload.name);
|
| 613 |
-
}
|
| 614 |
location.reload();
|
| 615 |
}
|
| 616 |
} else { UI.showAuthMsg(data.message); }
|
|
@@ -656,21 +645,19 @@
|
|
| 656 |
if(!State.user || State.isProcessing) return;
|
| 657 |
State.currentThreadId = threadId;
|
| 658 |
|
| 659 |
-
// Clear
|
| 660 |
-
document.getElementById('chatMessages').innerHTML = '';
|
| 661 |
-
UI.updateWelcomeScreen();
|
| 662 |
|
| 663 |
try {
|
| 664 |
const res = await fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "get_chat", email: State.user, threadId: threadId})});
|
| 665 |
const data = await res.json();
|
| 666 |
if(data.status === 'success') {
|
| 667 |
State.history = JSON.parse(data.historyJSON || "[]");
|
| 668 |
-
|
| 669 |
-
UI.updateWelcomeScreen(); // Hide it since history has items
|
| 670 |
|
| 671 |
State.history.forEach(msg => {
|
| 672 |
if(msg.role === 'user') {
|
| 673 |
let dispText = msg.content;
|
|
|
|
| 674 |
if(dispText.includes('---END DATA---')) dispText = dispText.split('User: ')[1] || '[Attached File]';
|
| 675 |
Chat.renderUser(dispText);
|
| 676 |
} else {
|
|
@@ -690,9 +677,8 @@
|
|
| 690 |
State.currentTitle = null;
|
| 691 |
State.history = [];
|
| 692 |
|
| 693 |
-
// Clean the chat area perfectly
|
| 694 |
document.getElementById('chatMessages').innerHTML = '';
|
| 695 |
-
UI.updateWelcomeScreen();
|
| 696 |
|
| 697 |
HistoryManager.syncAllChats();
|
| 698 |
if(window.innerWidth <= 900) UI.toggleSidebar();
|
|
@@ -703,6 +689,7 @@
|
|
| 703 |
State.currentThreadId = "thr_" + Date.now();
|
| 704 |
const firstUser = State.history.find(m => m.role === 'user');
|
| 705 |
let rawText = firstUser ? firstUser.content : "New Workspace";
|
|
|
|
| 706 |
if(rawText.includes('---END DATA---')) rawText = rawText.split('User: ')[1] || "Document Analysis";
|
| 707 |
State.currentTitle = rawText.substring(0, 25) + (rawText.length > 25 ? '...' : '');
|
| 708 |
}
|
|
@@ -731,14 +718,15 @@
|
|
| 731 |
}
|
| 732 |
};
|
| 733 |
|
| 734 |
-
// --- 4.
|
| 735 |
-
const
|
| 736 |
toggle: () => {
|
| 737 |
-
const btn = document.getElementById('
|
| 738 |
const status = document.getElementById('locStatus');
|
| 739 |
-
if(State.location) {
|
| 740 |
State.location = null;
|
| 741 |
-
|
|
|
|
| 742 |
status.style.display = 'none';
|
| 743 |
} else {
|
| 744 |
if(navigator.geolocation) {
|
|
@@ -746,65 +734,33 @@
|
|
| 746 |
status.style.display = 'flex';
|
| 747 |
status.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="3"></circle></svg> <span class="flicker-text">Finding your exact location...</span>`;
|
| 748 |
|
| 749 |
-
navigator.geolocation.getCurrentPosition(
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
btn.classList.add('active-gps');
|
| 754 |
-
status.innerHTML = `<span style="color:var(--brand-success);">Location found & attached.</span>`;
|
| 755 |
-
setTimeout(() => status.style.display = 'none', 3000);
|
| 756 |
-
},
|
| 757 |
-
err => {
|
| 758 |
-
btn.style.color = '';
|
| 759 |
-
status.innerHTML = `<span style="color:var(--brand-danger);">Location access denied.</span>`;
|
| 760 |
-
setTimeout(() => status.style.display = 'none', 3000);
|
| 761 |
-
}
|
| 762 |
-
);
|
| 763 |
-
} else { alert("GPS not supported."); }
|
| 764 |
-
}
|
| 765 |
-
}
|
| 766 |
-
};
|
| 767 |
-
|
| 768 |
-
// 🌤️ CLIENT-SIDE WEATHER MANAGER (No Backend Reconstruction Needed!)
|
| 769 |
-
const WeatherManager = {
|
| 770 |
-
toggle: async () => {
|
| 771 |
-
const btn = document.getElementById('btnWeather');
|
| 772 |
-
const status = document.getElementById('locStatus');
|
| 773 |
-
if(State.weatherContext) {
|
| 774 |
-
State.weatherContext = null;
|
| 775 |
-
btn.classList.remove('active-weather');
|
| 776 |
-
status.style.display = 'none';
|
| 777 |
-
} else {
|
| 778 |
-
if(navigator.geolocation) {
|
| 779 |
-
btn.style.color = 'var(--brand-accent)';
|
| 780 |
-
status.style.display = 'flex';
|
| 781 |
-
status.innerHTML = `<span class="flicker-text">Fetching real-time weather...</span>`;
|
| 782 |
-
|
| 783 |
-
navigator.geolocation.getCurrentPosition(async pos => {
|
| 784 |
try {
|
| 785 |
-
|
| 786 |
-
const
|
| 787 |
-
// Using free open-meteo API (no key needed)
|
| 788 |
-
const res = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}¤t_weather=true`);
|
| 789 |
const data = await res.json();
|
| 790 |
const w = data.current_weather;
|
|
|
|
| 791 |
|
| 792 |
-
State.weatherContext = `Local Weather Status: ${w.temperature}°C, Wind Speed: ${w.windspeed} km/h.`;
|
| 793 |
btn.style.color = '';
|
| 794 |
-
btn.classList.add('active-
|
| 795 |
-
status.innerHTML = `<span style="color:var(--brand-success);">
|
| 796 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 797 |
-
} catch(e) {
|
| 798 |
-
btn.style.color = '';
|
| 799 |
-
|
|
|
|
| 800 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 801 |
}
|
| 802 |
-
}, err => {
|
| 803 |
-
btn.style.color = '';
|
| 804 |
-
status.innerHTML = `<span style="color:var(--brand-danger);">
|
| 805 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 806 |
});
|
| 807 |
-
} else { alert("
|
| 808 |
}
|
| 809 |
}
|
| 810 |
};
|
|
@@ -830,7 +786,7 @@
|
|
| 830 |
}
|
| 831 |
};
|
| 832 |
|
| 833 |
-
// --- 5. FILE HANDLING ---
|
| 834 |
const FileSys = {
|
| 835 |
process: async (input, type) => {
|
| 836 |
document.getElementById('attachMenu').classList.remove('active');
|
|
@@ -847,7 +803,25 @@
|
|
| 847 |
r.onload = (e) => { State.attachment = { type: 'text', data: e.target.result, name: file.name }; }; r.readAsText(file);
|
| 848 |
}
|
| 849 |
input.value = '';
|
| 850 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 851 |
};
|
| 852 |
|
| 853 |
// --- 6. WORKSPACE ---
|
|
@@ -917,16 +891,16 @@
|
|
| 917 |
};
|
| 918 |
marked.use({ renderer: MDRenderer });
|
| 919 |
|
| 920 |
-
// --- 7. CHAT ENGINE ---
|
| 921 |
const Chat = {
|
| 922 |
renderUser: (txt, attachUI = '') => {
|
| 923 |
-
const c = document.getElementById('chatMessages'); //
|
| 924 |
const w = document.createElement('div'); w.className = 'message-wrapper';
|
| 925 |
w.innerHTML = `<div class="user-message">${attachUI}${txt ? UI.escape(txt) : ''}</div>`;
|
| 926 |
c.appendChild(w); UI.scrollToBottom();
|
| 927 |
},
|
| 928 |
renderBot: (msgId) => {
|
| 929 |
-
const c = document.getElementById('chatMessages');
|
| 930 |
const w = document.createElement('div'); w.className = 'message-wrapper';
|
| 931 |
|
| 932 |
const copyIcon = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
|
|
@@ -1038,7 +1012,6 @@
|
|
| 1038 |
State.lastUserMessage = text;
|
| 1039 |
input.value = ''; input.style.height = 'auto';
|
| 1040 |
|
| 1041 |
-
// Hide Welcome Screen on first chat
|
| 1042 |
UI.updateWelcomeScreen();
|
| 1043 |
|
| 1044 |
const searchTriggers = ["search", "latest", "news", "near", "restaurant", "shop", "distance", "time", "आसपास", "दूरी", "दुकान"];
|
|
@@ -1058,14 +1031,16 @@
|
|
| 1058 |
}
|
| 1059 |
}
|
| 1060 |
|
| 1061 |
-
// 🌤️ Inject Weather Context
|
|
|
|
| 1062 |
if(State.weatherContext) {
|
| 1063 |
-
|
| 1064 |
}
|
| 1065 |
|
| 1066 |
if(!isRetry) {
|
|
|
|
| 1067 |
Chat.renderUser(text, attachUI);
|
| 1068 |
-
State.history.push({ role: 'user', content:
|
| 1069 |
}
|
| 1070 |
|
| 1071 |
FileSys.discard();
|
|
@@ -1079,7 +1054,7 @@
|
|
| 1079 |
const res = await fetch(Config.API_ENDPOINT, {
|
| 1080 |
method: 'POST', headers: {'Content-Type': 'application/json'},
|
| 1081 |
body: JSON.stringify({
|
| 1082 |
-
message:
|
| 1083 |
attachments: mediaArray,
|
| 1084 |
is_search: isSearching,
|
| 1085 |
location: State.location,
|
|
@@ -1111,7 +1086,9 @@
|
|
| 1111 |
if(json.choices && json.choices[0].delta.content) fullText += json.choices[0].delta.content;
|
| 1112 |
Chat.parseAndRender(fullText, isSearching, true, botObj.contentDiv);
|
| 1113 |
UI.scrollToBottom();
|
| 1114 |
-
} catch(e){
|
|
|
|
|
|
|
| 1115 |
}
|
| 1116 |
}
|
| 1117 |
}
|
|
|
|
| 37 |
--brand-accent: #3b82f6;
|
| 38 |
--brand-success: #10b981;
|
| 39 |
--brand-danger: #ef4444;
|
| 40 |
+
--brand-warning: #eab308; /* Yellow */
|
|
|
|
| 41 |
|
| 42 |
--font-ui: 'Inter', sans-serif;
|
| 43 |
--font-code: 'JetBrains Mono', monospace;
|
|
|
|
| 120 |
/* --- CHAT CONTAINER --- */
|
| 121 |
.chat-container { flex: 1; overflow-y: auto; padding: 60px 20px 140px; display: flex; flex-direction: column; align-items: center; scroll-behavior: smooth; }
|
| 122 |
|
|
|
|
| 123 |
.welcome-center { margin: auto; text-align: center; display: flex; flex-direction: column; align-items: center; animation: fadeIn 0.4s ease; width: 100%; justify-content: center; flex: 1; }
|
| 124 |
.welcome-center img { width: 68px; height: 68px; border-radius: 18px; margin-bottom: 20px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); object-fit: cover; }
|
| 125 |
.welcome-center h1 { font-size: 24px; font-weight: 500; color: var(--text-primary); margin-bottom: 6px; letter-spacing: -0.5px; }
|
| 126 |
.welcome-center p { font-size: 14px; color: var(--text-secondary); }
|
| 127 |
|
|
|
|
| 128 |
#chatMessages { width: 100%; display: flex; flex-direction: column; align-items: center; }
|
| 129 |
|
| 130 |
.message-wrapper { width: 100%; max-width: 780px; margin-bottom: 32px; display: flex; flex-direction: column; }
|
|
|
|
| 160 |
.qwen-think-box[open] summary svg.arrow { transform: rotate(90deg); }
|
| 161 |
.qwen-think-content { padding: 14px; border-top: 1px solid var(--border-light); font-size: 13.5px; color: var(--text-secondary); font-style: italic; background: #fff; line-height: 1.6; }
|
| 162 |
|
| 163 |
+
/* --- ✨ SEARCH & LOCATION ANIMATIONS (NO SPINNING, ONLY FLICKER) --- */
|
| 164 |
.action-status {
|
| 165 |
display: inline-flex; align-items: center; gap: 8px; padding: 6px 12px;
|
| 166 |
background: var(--bg-sidebar); border-radius: 12px; font-size: 13px; font-weight: 500;
|
|
|
|
| 171 |
.flicker-text { animation: pulseOpacity 1.2s infinite alternate; }
|
| 172 |
|
| 173 |
@keyframes pulseOpacity {
|
| 174 |
+
0% { opacity: 0.3; }
|
| 175 |
100% { opacity: 1; }
|
| 176 |
}
|
| 177 |
|
|
|
|
| 223 |
.tool-btn:hover { background: var(--bg-sidebar); color: var(--text-primary); }
|
| 224 |
|
| 225 |
/* 🎨 DYNAMIC COLORS */
|
| 226 |
+
.tool-btn.active-env { color: var(--brand-accent); background: #eff6ff; }
|
|
|
|
| 227 |
|
| 228 |
.tool-btn.active-think-low { color: var(--brand-danger); background: #fee2e2; }
|
| 229 |
.tool-btn.active-think-medium { color: var(--brand-warning); background: #fef3c7; }
|
|
|
|
| 315 |
<svg style="width:14px; height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
|
| 316 |
New Workspace
|
| 317 |
</button>
|
| 318 |
+
<div class="sidebar-history" id="chatHistory"></div>
|
|
|
|
|
|
|
|
|
|
| 319 |
|
| 320 |
<div class="sidebar-footer">
|
| 321 |
<div class="user-avatar" id="uAv">G</div>
|
|
|
|
| 331 |
|
| 332 |
<main class="main-area">
|
| 333 |
<div class="chat-container" id="chatContainer">
|
| 334 |
+
<!-- 🌟 STATIC WELCOME SCREEN -->
|
| 335 |
<div class="welcome-center" id="welcomeScreen">
|
| 336 |
<img src="https://i.ibb.co/MyYStcGP/TIRANGA-20260613-131924-0000.png" alt="CODE VED Logo">
|
| 337 |
<h1>CODE VED</h1>
|
| 338 |
<p id="welcomeGreeting">Engineered by Divy Patel</p>
|
| 339 |
</div>
|
| 340 |
|
| 341 |
+
<!-- 💬 ALL MESSAGES GO HERE -->
|
| 342 |
+
<div id="chatMessages"></div>
|
|
|
|
| 343 |
</div>
|
| 344 |
|
| 345 |
<div class="input-dock" id="inputDock">
|
| 346 |
+
<!-- 🌍 Environment Status Toast -->
|
| 347 |
<div class="loc-toast" id="locStatus"></div>
|
| 348 |
|
| 349 |
<div class="input-container">
|
|
|
|
| 371 |
<input type="file" id="docUpload" class="hidden-input" accept=".pdf,.txt,.docx,.html,.js,.py,.css,.cpp,.c,.json,.md" onchange="FileSys.process(this, 'document')">
|
| 372 |
</div>
|
| 373 |
|
| 374 |
+
<!-- Voice Typing -->
|
| 375 |
+
<button class="tool-btn" id="btnStt" onclick="Speech.toggle()" title="Voice Typing">
|
| 376 |
+
<svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><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="23"></line><line x1="8" y1="23" x2="16" y2="23"></line></svg>
|
| 377 |
</button>
|
| 378 |
|
| 379 |
+
<!-- 🌍 UNIFIED ENVIRONMENT (Location + Weather) -->
|
| 380 |
+
<button class="tool-btn" id="btnEnv" onclick="EnvironmentManager.toggle()" title="Environment (Location & Weather)">
|
| 381 |
+
<svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg>
|
| 382 |
</button>
|
| 383 |
|
| 384 |
<!-- Thinking Effort Menu -->
|
|
|
|
| 405 |
</div>
|
| 406 |
</main>
|
| 407 |
|
| 408 |
+
<!-- WORKSPACE PANEL (RESTORED & FUNCTIONAL) -->
|
| 409 |
<aside class="workspace-panel" id="workspacePanel">
|
| 410 |
<div class="ws-header">
|
| 411 |
<div class="ws-tabs">
|
|
|
|
| 483 |
currentWs: null,
|
| 484 |
abortController: null,
|
| 485 |
location: null,
|
| 486 |
+
weatherContext: null, // Unified Environment Context
|
| 487 |
thinkingMode: false,
|
| 488 |
thinkingEffort: "medium",
|
|
|
|
| 489 |
lastUserMessage: null,
|
| 490 |
currentThreadId: null,
|
| 491 |
currentTitle: null
|
|
|
|
| 525 |
document.getElementById('btnLogout').style.display = 'flex';
|
| 526 |
document.getElementById('welcomeGreeting').innerText = `Ready, ${dispName}.`;
|
| 527 |
},
|
| 528 |
+
// 🌟 Permanent Welcome Screen Fix
|
| 529 |
updateWelcomeScreen: () => {
|
| 530 |
const ws = document.getElementById('welcomeScreen');
|
| 531 |
if(State.history.length === 0) {
|
|
|
|
| 598 |
else if (action === 'login_send_otp') Auth.switchPhase('loginPhase1', 'loginPhase2');
|
| 599 |
else {
|
| 600 |
localStorage.setItem('codeved_user', payload.email);
|
| 601 |
+
if(data.user && data.user.name) localStorage.setItem('codeved_name', data.user.name);
|
| 602 |
+
else if (payload.name) localStorage.setItem('codeved_name', payload.name);
|
|
|
|
|
|
|
|
|
|
| 603 |
location.reload();
|
| 604 |
}
|
| 605 |
} else { UI.showAuthMsg(data.message); }
|
|
|
|
| 645 |
if(!State.user || State.isProcessing) return;
|
| 646 |
State.currentThreadId = threadId;
|
| 647 |
|
| 648 |
+
document.getElementById('chatMessages').innerHTML = ''; // Clear chat area
|
|
|
|
|
|
|
| 649 |
|
| 650 |
try {
|
| 651 |
const res = await fetch(Config.GAS_URL, { method: 'POST', body: JSON.stringify({action: "get_chat", email: State.user, threadId: threadId})});
|
| 652 |
const data = await res.json();
|
| 653 |
if(data.status === 'success') {
|
| 654 |
State.history = JSON.parse(data.historyJSON || "[]");
|
| 655 |
+
UI.updateWelcomeScreen();
|
|
|
|
| 656 |
|
| 657 |
State.history.forEach(msg => {
|
| 658 |
if(msg.role === 'user') {
|
| 659 |
let dispText = msg.content;
|
| 660 |
+
if(dispText.includes('[SYSTEM REAL-TIME WEATHER:')) dispText = dispText.split('\n\n[SYSTEM REAL-TIME WEATHER:')[0];
|
| 661 |
if(dispText.includes('---END DATA---')) dispText = dispText.split('User: ')[1] || '[Attached File]';
|
| 662 |
Chat.renderUser(dispText);
|
| 663 |
} else {
|
|
|
|
| 677 |
State.currentTitle = null;
|
| 678 |
State.history = [];
|
| 679 |
|
|
|
|
| 680 |
document.getElementById('chatMessages').innerHTML = '';
|
| 681 |
+
UI.updateWelcomeScreen();
|
| 682 |
|
| 683 |
HistoryManager.syncAllChats();
|
| 684 |
if(window.innerWidth <= 900) UI.toggleSidebar();
|
|
|
|
| 689 |
State.currentThreadId = "thr_" + Date.now();
|
| 690 |
const firstUser = State.history.find(m => m.role === 'user');
|
| 691 |
let rawText = firstUser ? firstUser.content : "New Workspace";
|
| 692 |
+
if(rawText.includes('[SYSTEM REAL-TIME WEATHER:')) rawText = rawText.split('\n\n[SYSTEM REAL-TIME WEATHER:')[0];
|
| 693 |
if(rawText.includes('---END DATA---')) rawText = rawText.split('User: ')[1] || "Document Analysis";
|
| 694 |
State.currentTitle = rawText.substring(0, 25) + (rawText.length > 25 ? '...' : '');
|
| 695 |
}
|
|
|
|
| 718 |
}
|
| 719 |
};
|
| 720 |
|
| 721 |
+
// --- 4. 🌍 UNIFIED ENVIRONMENT MANAGER (GPS + WEATHER) ---
|
| 722 |
+
const EnvironmentManager = {
|
| 723 |
toggle: () => {
|
| 724 |
+
const btn = document.getElementById('btnEnv');
|
| 725 |
const status = document.getElementById('locStatus');
|
| 726 |
+
if(State.location || State.weatherContext) {
|
| 727 |
State.location = null;
|
| 728 |
+
State.weatherContext = null;
|
| 729 |
+
btn.classList.remove('active-env');
|
| 730 |
status.style.display = 'none';
|
| 731 |
} else {
|
| 732 |
if(navigator.geolocation) {
|
|
|
|
| 734 |
status.style.display = 'flex';
|
| 735 |
status.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="3"></circle></svg> <span class="flicker-text">Finding your exact location...</span>`;
|
| 736 |
|
| 737 |
+
navigator.geolocation.getCurrentPosition(async pos => {
|
| 738 |
+
State.location = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
| 739 |
+
status.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z"></path></svg> <span class="flicker-text">Fetching real-time weather...</span>`;
|
| 740 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 741 |
try {
|
| 742 |
+
// Direct API call for Live Weather!
|
| 743 |
+
const res = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${State.location.lat}&longitude=${State.location.lng}¤t_weather=true`);
|
|
|
|
|
|
|
| 744 |
const data = await res.json();
|
| 745 |
const w = data.current_weather;
|
| 746 |
+
State.weatherContext = `Temperature: ${w.temperature}°C, Wind Speed: ${w.windspeed} km/h`;
|
| 747 |
|
|
|
|
| 748 |
btn.style.color = '';
|
| 749 |
+
btn.classList.add('active-env');
|
| 750 |
+
status.innerHTML = `<span style="color:var(--brand-success);">Location & Weather active!</span>`;
|
| 751 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 752 |
+
} catch (e) {
|
| 753 |
+
btn.style.color = '';
|
| 754 |
+
btn.classList.add('active-env');
|
| 755 |
+
status.innerHTML = `<span style="color:var(--brand-warning);">Location found, weather failed.</span>`;
|
| 756 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 757 |
}
|
| 758 |
+
}, err => {
|
| 759 |
+
btn.style.color = '';
|
| 760 |
+
status.innerHTML = `<span style="color:var(--brand-danger);">Location access denied.</span>`;
|
| 761 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 762 |
});
|
| 763 |
+
} else { alert("GPS not supported."); }
|
| 764 |
}
|
| 765 |
}
|
| 766 |
};
|
|
|
|
| 786 |
}
|
| 787 |
};
|
| 788 |
|
| 789 |
+
// --- 5. FILE HANDLING & VOICE ---
|
| 790 |
const FileSys = {
|
| 791 |
process: async (input, type) => {
|
| 792 |
document.getElementById('attachMenu').classList.remove('active');
|
|
|
|
| 803 |
r.onload = (e) => { State.attachment = { type: 'text', data: e.target.result, name: file.name }; }; r.readAsText(file);
|
| 804 |
}
|
| 805 |
input.value = '';
|
| 806 |
+
},
|
| 807 |
+
discard: () => { State.attachment = null; document.getElementById('filePreviewBar').classList.remove('active'); }
|
| 808 |
+
};
|
| 809 |
+
|
| 810 |
+
const Speech = {
|
| 811 |
+
rec: null, isRec: false,
|
| 812 |
+
init: () => {
|
| 813 |
+
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
| 814 |
+
if (!SR) return alert("Speech Recognition not supported.");
|
| 815 |
+
Speech.rec = new SR(); Speech.rec.continuous = false; Speech.rec.interimResults = true; Speech.rec.lang = 'en-US';
|
| 816 |
+
Speech.rec.onstart = () => { Speech.isRec = true; document.getElementById('btnStt').classList.add('recording'); };
|
| 817 |
+
Speech.rec.onresult = (e) => {
|
| 818 |
+
let trans = ''; for (let i = e.resultIndex; i < e.results.length; ++i) { if (e.results[i].isFinal) trans += e.results[i][0].transcript; }
|
| 819 |
+
if (trans) { const input = document.getElementById('mainInput'); input.value += (input.value ? ' ' : '') + trans; UI.autoGrow(input); }
|
| 820 |
+
};
|
| 821 |
+
Speech.rec.onerror = () => Speech.stop(); Speech.rec.onend = () => Speech.stop();
|
| 822 |
+
},
|
| 823 |
+
toggle: () => { if (!Speech.rec) Speech.init(); if (Speech.isRec) Speech.stop(); else { try { Speech.rec.start(); } catch(e) {} } },
|
| 824 |
+
stop: () => { if(Speech.rec) Speech.rec.stop(); Speech.isRec = false; document.getElementById('btnStt').classList.remove('recording'); }
|
| 825 |
};
|
| 826 |
|
| 827 |
// --- 6. WORKSPACE ---
|
|
|
|
| 891 |
};
|
| 892 |
marked.use({ renderer: MDRenderer });
|
| 893 |
|
| 894 |
+
// --- 7. CHAT ENGINE (ROCK SOLID) ---
|
| 895 |
const Chat = {
|
| 896 |
renderUser: (txt, attachUI = '') => {
|
| 897 |
+
const c = document.getElementById('chatMessages'); // Hardbound rendering
|
| 898 |
const w = document.createElement('div'); w.className = 'message-wrapper';
|
| 899 |
w.innerHTML = `<div class="user-message">${attachUI}${txt ? UI.escape(txt) : ''}</div>`;
|
| 900 |
c.appendChild(w); UI.scrollToBottom();
|
| 901 |
},
|
| 902 |
renderBot: (msgId) => {
|
| 903 |
+
const c = document.getElementById('chatMessages');
|
| 904 |
const w = document.createElement('div'); w.className = 'message-wrapper';
|
| 905 |
|
| 906 |
const copyIcon = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
|
|
|
|
| 1012 |
State.lastUserMessage = text;
|
| 1013 |
input.value = ''; input.style.height = 'auto';
|
| 1014 |
|
|
|
|
| 1015 |
UI.updateWelcomeScreen();
|
| 1016 |
|
| 1017 |
const searchTriggers = ["search", "latest", "news", "near", "restaurant", "shop", "distance", "time", "आसपास", "दूरी", "दुकान"];
|
|
|
|
| 1031 |
}
|
| 1032 |
}
|
| 1033 |
|
| 1034 |
+
// 🌤️ Inject Weather Context Safely
|
| 1035 |
+
let systemInjectedPayload = payloadStr;
|
| 1036 |
if(State.weatherContext) {
|
| 1037 |
+
systemInjectedPayload += `\n\n[SYSTEM REAL-TIME WEATHER: ${State.weatherContext}]`;
|
| 1038 |
}
|
| 1039 |
|
| 1040 |
if(!isRetry) {
|
| 1041 |
+
// Only show the user's text on screen, NOT the system tags
|
| 1042 |
Chat.renderUser(text, attachUI);
|
| 1043 |
+
State.history.push({ role: 'user', content: systemInjectedPayload });
|
| 1044 |
}
|
| 1045 |
|
| 1046 |
FileSys.discard();
|
|
|
|
| 1054 |
const res = await fetch(Config.API_ENDPOINT, {
|
| 1055 |
method: 'POST', headers: {'Content-Type': 'application/json'},
|
| 1056 |
body: JSON.stringify({
|
| 1057 |
+
message: systemInjectedPayload,
|
| 1058 |
attachments: mediaArray,
|
| 1059 |
is_search: isSearching,
|
| 1060 |
location: State.location,
|
|
|
|
| 1086 |
if(json.choices && json.choices[0].delta.content) fullText += json.choices[0].delta.content;
|
| 1087 |
Chat.parseAndRender(fullText, isSearching, true, botObj.contentDiv);
|
| 1088 |
UI.scrollToBottom();
|
| 1089 |
+
} catch(e){
|
| 1090 |
+
// Safe parsing: Ignore incomplete chunks during stream
|
| 1091 |
+
}
|
| 1092 |
}
|
| 1093 |
}
|
| 1094 |
}
|