Vedika commited on
Commit
c3300bc
·
verified ·
1 Parent(s): 6e2854b

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +167 -88
index.html CHANGED
@@ -1,4 +1,3 @@
1
-
2
  <!DOCTYPE html>
3
  <html lang="en">
4
  <head>
@@ -20,10 +19,9 @@
20
 
21
  <style>
22
  :root {
23
- /* 🎨 PREMIUM COLOR PALETTE - LIGHT & AIRY */
24
  --bg-main: #f8fafd;
25
  --bg-sidebar: #f0f4f9;
26
- --bg-user-msg: rgba(227, 236, 250, 0.85); /* Slightly blue user message */
27
 
28
  --text-primary: #1f1f1f;
29
  --text-secondary: #444746;
@@ -36,12 +34,13 @@
36
  --brand-accent: #0b57d0;
37
  --brand-success: #146c2e;
38
  --brand-danger: #b3261e;
 
39
 
40
  --font-ui: 'Inter', sans-serif;
41
  --font-code: 'JetBrains Mono', monospace;
42
  --radius-sm: 12px;
43
  --radius-md: 18px;
44
- --radius-lg: 32px; /* Super rounded input */
45
  }
46
 
47
  * { box-sizing: border-box; margin: 0; padding: 0; }
@@ -53,7 +52,6 @@
53
  display: flex; overflow: hidden; -webkit-font-smoothing: antialiased;
54
  }
55
 
56
- /* Subtle mesh gradient for premium background */
57
  body::before {
58
  content: ''; position: absolute; inset: 0; z-index: -1;
59
  background-image: radial-gradient(circle at 15% 50%, rgba(219, 234, 254, 0.5) 0%, transparent 40%),
@@ -69,7 +67,14 @@
69
  ::-webkit-scrollbar-thumb { background: rgba(114, 119, 117, 0.3); border-radius: 10px; }
70
  ::-webkit-scrollbar-thumb:hover { background: rgba(114, 119, 117, 0.6); }
71
 
72
- /* --- SLEEK HAMBURGER MENU --- */
 
 
 
 
 
 
 
73
  .btn-menu {
74
  position: absolute; top: 16px; left: 16px; z-index: 50;
75
  width: 44px; height: 44px; border-radius: 50%;
@@ -78,7 +83,6 @@
78
  }
79
  .btn-menu:hover { background: rgba(31, 31, 31, 0.08); color: var(--text-primary); }
80
 
81
- /* --- SIDEBAR --- */
82
  .sidebar {
83
  width: 280px; background-color: var(--bg-sidebar); border-right: 1px solid var(--border-light);
84
  display: flex; flex-direction: column; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 100;
@@ -122,10 +126,8 @@
122
  .btn-power { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: 0.2s; }
123
  .btn-power:hover { background: #fce8e8; color: var(--brand-danger); }
124
 
125
- /* --- MAIN WORKSPACE AREA --- */
126
  .main-area { flex: 1; display: flex; flex-direction: column; position: relative; transition: 0.3s; height: 100%; overflow: hidden; }
127
 
128
- /* --- CHAT CONTAINER --- */
129
  .chat-container { flex: 1; overflow-y: auto; padding: 60px 20px 180px; display: flex; flex-direction: column; align-items: center; scroll-behavior: smooth; }
130
 
131
  .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; }
@@ -135,10 +137,8 @@
135
 
136
  #chatMessages { width: 100%; display: flex; flex-direction: column; align-items: center; }
137
 
138
- /* 🌟 WIDE & RESPONSIVE CHAT WRAPPER */
139
  .message-wrapper { width: 100%; max-width: 850px; margin-bottom: 32px; display: flex; flex-direction: column; }
140
 
141
- /* 🌟 USER BUBBLE */
142
  .user-message {
143
  align-self: flex-end; max-width: 85%;
144
  background: var(--bg-user-msg);
@@ -146,9 +146,9 @@
146
  padding: 14px 20px; border-radius: 24px 24px 6px 24px;
147
  font-size: 15.5px; line-height: 1.6; color: var(--text-primary);
148
  box-shadow: 0 2px 10px rgba(0,0,0,0.02);
 
149
  }
150
 
151
- /* 🌟 AI MESSAGE (NO BOUNDARY BOX, COMPLETELY FREE) */
152
  .bot-message {
153
  align-self: flex-start; max-width: 100%; display: flex; gap: 16px; width: 100%;
154
  background: transparent; padding: 0; border: none; box-shadow: none; margin-top: 4px;
@@ -157,14 +157,13 @@
157
  .bot-avatar { width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; overflow: hidden; border: 1px solid var(--border-light); background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.05); margin-top: 2px;}
158
  .bot-avatar img { width: 100%; height: 100%; object-fit: cover; }
159
 
160
- .bot-content { flex: 1; font-size: 15.5px; line-height: 1.7; color: var(--text-primary); min-width: 0; }
161
 
162
  .bot-content p { margin-bottom: 16px; }
163
  .bot-content p:last-child { margin-bottom: 0; }
164
  .bot-content strong { font-weight: 600; color: #000; }
165
  .bot-content code { font-family: var(--font-code); background: rgba(31,31,31,0.05); padding: 3px 6px; border-radius: 6px; font-size: 0.9em; color: #0284c7; }
166
 
167
- /* Premium macOS-like Code Block */
168
  .bot-content pre {
169
  background: #0d1117; padding: 40px 16px 16px; border-radius: var(--radius-md);
170
  overflow-x: auto; margin: 16px 0; box-shadow: 0 4px 16px rgba(0,0,0,0.1);
@@ -177,7 +176,6 @@
177
  }
178
  .bot-content pre code { background: none; padding: 0; color: #e2e8f0; font-size: 13.5px; text-shadow: none; }
179
 
180
- /* 🌟 SMALL ATTACHMENTS (PILL DESIGN) */
181
  .chat-attachment-container { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
182
  .chat-file-pill {
183
  display: inline-flex; align-items: center; gap: 8px;
@@ -187,12 +185,11 @@
187
  box-shadow: 0 2px 6px rgba(0,0,0,0.03);
188
  }
189
  .chat-img-preview {
190
- max-width: 120px; max-height: 120px; /* Small, cute image preview */
191
  border-radius: 12px; border: 1px solid var(--border-light);
192
  object-fit: cover; box-shadow: 0 4px 12px rgba(0,0,0,0.06);
193
  }
194
 
195
- /* --- VISIBLE THINKING DROPDOWN --- */
196
  .qwen-think-box {
197
  background: rgba(255,255,255,0.7); border: 1px solid var(--border-light); border-radius: 16px;
198
  margin-bottom: 16px; overflow: hidden; transition: 0.2s; backdrop-filter: blur(10px);
@@ -207,7 +204,6 @@
207
  .qwen-think-box[open] summary svg.arrow { transform: rotate(90deg); }
208
  .qwen-think-content { padding: 16px; border-top: 1px solid var(--border-light); font-size: 13.5px; color: var(--text-secondary); font-style: italic; line-height: 1.6; opacity: 0.8;}
209
 
210
- /* --- ✨ SEARCH & LOCATION ANIMATIONS --- */
211
  .action-status {
212
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px;
213
  background: rgba(255,255,255,0.7); border-radius: 16px; font-size: 13px; font-weight: 500;
@@ -216,11 +212,17 @@
216
  }
217
  .action-status.active { background: rgba(227, 236, 250, 0.8); border-color: rgba(168, 199, 250, 0.5); color: var(--brand-accent); }
218
  .flicker-text { animation: pulseOpacity 1.2s infinite alternate; }
219
- @keyframes pulseOpacity { 0% { opacity: 0.4; } 100% { opacity: 1; } }
220
- @keyframes spin { 100% { transform: rotate(360deg); } }
221
  .spin-icon { animation: spin 1s linear infinite; }
222
 
223
- /* --- ACTION CONTROLS --- */
 
 
 
 
 
 
 
 
224
  .msg-actions { display: flex; gap: 8px; margin-top: 14px; opacity: 0; transition: opacity 0.2s; align-items: center; flex-wrap: wrap; }
225
  .message-wrapper:hover .msg-actions { opacity: 1; }
226
  @media (hover: none) { .msg-actions { opacity: 1; } }
@@ -228,11 +230,9 @@
228
  .action-btn { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500; color: var(--text-secondary); padding: 8px 14px; border-radius: 12px; transition: 0.2s; background: rgba(255,255,255,0.6); border: 1px solid var(--border-light); backdrop-filter: blur(4px); }
229
  .action-btn:hover { background: #fff; color: var(--text-primary); box-shadow: 0 2px 6px rgba(0,0,0,0.04); }
230
 
231
- /* 🔊 Premium Listen Button */
232
  .action-btn.listen-btn { background: #e3ecfa; color: #0b57d0; border-color: transparent; }
233
  .action-btn.listen-btn:hover { background: #d3e3fd; }
234
 
235
- /* --- 🌟 GEMINI STYLE TRANSLUCENT INPUT DOCK --- */
236
  .input-dock {
237
  position: absolute; bottom: 0; left: 0; width: 100%;
238
  padding: 10px 20px 24px; padding-bottom: max(24px, env(safe-area-inset-bottom));
@@ -247,12 +247,11 @@
247
  box-shadow: 0 4px 12px rgba(0,0,0,0.05); backdrop-filter: blur(12px);
248
  }
249
 
250
- /* 🌟 THE HIGHLY TRANSLUCENT & WIDE TEXT BOX */
251
  .input-container {
252
  width: 100%; max-width: 900px;
253
- background: rgba(255, 255, 255, 0.65); /* Glassmorphism Base */
254
- backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px); /* Strong Blur */
255
- border: 1px solid rgba(255, 255, 255, 0.9); /* White edge for premium feel */
256
  box-shadow: 0 8px 30px rgba(0,0,0,0.04);
257
  border-radius: var(--radius-lg);
258
  padding: 10px 12px 10px 20px;
@@ -260,7 +259,6 @@
260
  }
261
  .input-container:focus-within { border-color: rgba(255,255,255,1); box-shadow: 0 10px 40px rgba(11, 87, 208, 0.08); background: rgba(255, 255, 255, 0.85); }
262
 
263
- /* Inner Attachments View */
264
  .file-preview-bar { display: none; padding: 4px 0 12px; align-items: center; gap: 10px; margin-bottom: 4px; }
265
  .file-preview-bar.active { display: flex; }
266
  .file-preview-pill {
@@ -275,32 +273,40 @@
275
 
276
  .input-row { display: flex; align-items: flex-end; gap: 10px; width: 100%; }
277
 
278
- .tools-left { display: flex; gap: 4px; align-items: center; margin-bottom: 2px;}
279
  .tool-btn { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: 0.2s; position: relative; flex-shrink: 0; }
280
  .tool-btn:hover { background: rgba(31,31,31,0.06); color: var(--text-primary); }
281
 
282
- /* 🎨 DYNAMIC COLORS */
283
  .tool-btn.active-env { color: var(--brand-accent); background: #e3ecfa; }
284
  .tool-btn.active-think-low { color: var(--brand-danger); background: #fce8e8; }
285
  .tool-btn.active-think-medium { color: var(--brand-warning); background: #fef3c7; }
286
  .tool-btn.active-think-high { color: var(--brand-accent); background: #e3ecfa; }
 
287
  .tool-btn.recording { color: var(--brand-danger); animation: pulseOpacity 1s infinite alternate; }
288
 
289
  .chat-input { flex: 1; padding: 12px 4px; font-size: 15.5px; resize: none; max-height: 180px; min-height: 24px; color: var(--text-primary); line-height: 1.5; background: transparent; }
290
  .chat-input::placeholder { color: var(--text-tertiary); }
291
 
292
- /* 🌟 GEMINI STYLE SEND BUTTON (Up Arrow on Light Blue Circle) */
293
  .btn-send {
294
  width: 44px; height: 44px; border-radius: 50%;
295
- background: #c2e7ff; /* Exact light blue from image */
296
- color: #041e49; /* Dark blue arrow */
297
  display: flex; align-items: center; justify-content: center;
298
  transition: 0.2s; flex-shrink: 0; margin-bottom: 2px;
299
  }
300
  .btn-send:hover:not(:disabled) { background: #b4dfff; transform: scale(1.05); }
301
  .btn-send:disabled { background: rgba(31,31,31,0.08); color: rgba(31,31,31,0.3); cursor: not-allowed; }
 
 
 
 
 
 
 
 
 
 
302
 
303
- /* Dropdowns */
304
  .hidden-input { display: none; }
305
  .dropdown-menu { position: absolute; bottom: calc(100% + 14px); left: 0; background: rgba(255,255,255,0.95); backdrop-filter: blur(16px); border: 1px solid var(--border-light); border-radius: 20px; padding: 8px; box-shadow: 0 12px 32px rgba(0,0,0,0.1); display: none; flex-direction: column; min-width: 200px; z-index: 100; transform-origin: bottom left; }
306
  .dropdown-menu.active { display: flex; animation: dropUpFade 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
@@ -312,7 +318,6 @@
312
  .dropdown-item.selected { background: #e3ecfa; color: var(--brand-accent); font-weight: 600; }
313
  .dropdown-item svg { color: inherit; }
314
 
315
- /* --- AUTH MODAL --- */
316
  .auth-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.5); backdrop-filter: blur(8px); display: none; align-items: center; justify-content: center; z-index: 2000; }
317
  .auth-modal { background: #fff; width: 90%; max-width: 380px; border-radius: var(--radius-lg); padding: 32px; box-shadow: 0 24px 48px rgba(0,0,0,0.15); position: relative; }
318
  .auth-close { position: absolute; top: 16px; right: 16px; color: var(--text-tertiary); width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: 0.2s; }
@@ -331,7 +336,6 @@
331
  .auth-phase.active { display: block; }
332
  .auth-message { font-size: 13px; text-align: center; margin-top: 14px; min-height: 18px; font-weight: 500; }
333
 
334
- /* --- MOBILE PHONE FIXES --- */
335
  .mobile-overlay { display: none; position: fixed; inset: 0; background: rgba(15,23,42,0.3); backdrop-filter: blur(3px); z-index: 95; opacity: 0; transition: 0.3s; pointer-events: none; }
336
  .mobile-overlay.active { display: block; opacity: 1; pointer-events: all; }
337
 
@@ -342,6 +346,7 @@
342
  .input-container { border-radius: 28px; padding: 8px 10px 8px 16px; }
343
  .tool-btn { width: 38px; height: 38px; }
344
  .btn-send { width: 40px; height: 40px; }
 
345
  .chat-container { padding: 60px 10px 160px; }
346
  .user-message { font-size: 14.5px; max-width: 90%; }
347
  .bot-content { font-size: 14.5px; }
@@ -383,23 +388,19 @@
383
 
384
  <main class="main-area">
385
  <div class="chat-container" id="chatContainer">
386
- <!-- 🌟 STATIC WELCOME SCREEN -->
387
  <div class="welcome-center" id="welcomeScreen">
388
  <img src="https://i.ibb.co/MyYStcGP/TIRANGA-20260613-131924-0000.png" alt="CODE VED Logo">
389
  <h1>CODE VED</h1>
390
  <p id="welcomeGreeting">Engineered by Divy Patel</p>
391
  </div>
392
 
393
- <!-- 💬 ALL MESSAGES GO HERE -->
394
  <div id="chatMessages"></div>
395
  </div>
396
 
397
  <div class="input-dock" id="inputDock">
398
- <!-- 🌍 Environment Status Toast -->
399
  <div class="loc-toast" id="locStatus"></div>
400
 
401
  <div class="input-container">
402
- <!-- 🌟 Inner Attachment Preview (Like Canvas pill) -->
403
  <div class="file-preview-bar" id="filePreviewBar">
404
  <div class="file-preview-pill">
405
  <img id="imgPreview" class="file-preview-icon" src="" style="display:none;">
@@ -411,7 +412,6 @@
411
 
412
  <div class="input-row">
413
  <div class="tools-left">
414
- <!-- 🌟 '+' Menu (Attachments & Tools) -->
415
  <div style="position: relative;">
416
  <button class="tool-btn" onclick="UI.toggleAttachMenu()" title="Attach File">
417
  <svg style="width:24px;height:24px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
@@ -432,13 +432,11 @@
432
  <span style="display:flex;align-items:center;gap:10px;"><svg style="width:16px;height:16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a5 5 0 0 0-5 5v2a5 5 0 0 0 10 0V7a5 5 0 0 0-5-5z"></path><path d="M8 14H6a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2h-2"></path></svg> Thinking Mode</span>
433
  </div>
434
 
435
- <!-- 🎙️ PREMIUM VOICE SELECTOR MOVED HERE FOR CLEANER UI -->
436
  <div class="dropdown-item" onclick="TTSManager.toggleMenu()" style="color:#0b57d0;">
437
  <span style="display:flex;align-items:center;gap:10px;"><svg style="width:16px;height:16px;" 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> Select Voice Mode</span>
438
  </div>
439
  </div>
440
 
441
- <!-- Sub Menus -->
442
  <div class="dropdown-menu" id="voiceMenu" style="min-width: 200px; left: 45px; bottom: 40px;">
443
  <div class="dropdown-header">Premium Voices</div>
444
  <div id="voiceOptionsList"></div>
@@ -457,16 +455,21 @@
457
  <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')">
458
  </div>
459
 
 
 
 
460
  </div>
461
 
462
  <textarea class="chat-input" id="mainInput" placeholder="Ask CODE VED..." rows="1"></textarea>
463
 
464
- <!-- 🎤 Mic Button -->
465
  <button class="tool-btn" id="btnStt" onclick="Speech.toggle()" title="Voice Typing">
466
  <svg style="width:22px;height:22px;" 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>
467
  </button>
468
 
469
- <!-- 🚀 Gemini Style Send Button -->
 
 
 
470
  <button class="btn-send" id="btnSend" onclick="Chat.handleSend()">
471
  <svg style="width:20px;height:20px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg>
472
  </button>
@@ -475,8 +478,7 @@
475
  </div>
476
  </main>
477
 
478
- <!-- SECURE AUTHENTICATION MODAL -->
479
- <div class="auth-overlay" id="authModal">
480
  <div class="auth-modal">
481
  <button class="auth-close" onclick="Auth.closeModal()"><svg style="width:20px;height:20px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button>
482
  <div class="auth-title">System Access</div>
@@ -513,7 +515,6 @@
513
  </div>
514
 
515
  <script>
516
- // --- 1. CONFIG & STATE ---
517
  const Config = {
518
  GAS_URL: "https://script.google.com/macros/s/AKfycbzNO3inVc33ImhfLyde-JjjK9ZlPckLBksqCnCzelfhcklX6mp8KW8vfPTW4oWJTCcN/exec",
519
  API_ENDPOINT: "/api/chat",
@@ -534,12 +535,12 @@
534
  weatherContext: null,
535
  thinkingMode: false,
536
  thinkingEffort: "medium",
 
537
  lastUserMessage: null
538
  };
539
 
540
  if (typeof pdfjsLib !== 'undefined') { pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; }
541
 
542
- // --- 🎙️ AUTO-FETCH TTS MANAGER (SMART CACHING & FAST PLAYBACK) ---
543
  const TTSManager = {
544
  cache: {},
545
  currentAudio: null,
@@ -576,6 +577,7 @@
576
 
577
  setVoice: (code) => {
578
  TTSManager.selectedVoice = code;
 
579
  TTSManager.initUI();
580
  document.getElementById('voiceMenu').classList.remove('active');
581
  document.getElementById('attachMenu').classList.remove('active');
@@ -589,7 +591,6 @@
589
  return clean;
590
  },
591
 
592
- // 🌟 NEW: Auto-prepares audio safely right after AI stops
593
  autoPrepare: async (msgId, fullText, btnElement) => {
594
  const cleanText = TTSManager.cleanTextForTTS(fullText);
595
  if(!cleanText) return;
@@ -609,7 +610,7 @@
609
  body: JSON.stringify({
610
  text: cleanText,
611
  voice: TTSManager.selectedVoice,
612
- language_name: "English"
613
  })
614
  });
615
 
@@ -626,14 +627,16 @@
626
 
627
  } catch (e) {
628
  btnElement.innerHTML = `<span style="color:var(--brand-danger)">Voice Error</span>`;
629
- setTimeout(() => btnElement.style.display = 'none', 3000);
630
  }
631
  },
632
 
633
- // 🌟 Instant playback from cache (FASTER SPEED 1.15x)
634
  play: (msgId, btnElement) => {
635
  const audioUrl = TTSManager.cache[msgId];
636
- if(!audioUrl) return alert("Audio still generating.");
 
 
 
637
 
638
  if(TTSManager.currentAudio) {
639
  TTSManager.currentAudio.pause();
@@ -641,7 +644,7 @@
641
  }
642
 
643
  TTSManager.currentAudio = new Audio(audioUrl);
644
- TTSManager.currentAudio.playbackRate = 1.15; // Makes it feel smarter and faster
645
 
646
  const originalHTML = btnElement.innerHTML;
647
  const playingIcon = `<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>`;
@@ -660,7 +663,6 @@
660
  }
661
  };
662
 
663
- // --- 2. UI UTILITIES ---
664
  const UI = {
665
  toggleSidebar: () => {
666
  const sb = document.getElementById('sidebar');
@@ -708,7 +710,6 @@
708
  inp.addEventListener('input', function() { UI.autoGrow(this); });
709
  inp.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); Chat.handleSend(); } });
710
 
711
- // --- 3. AUTH & HISTORY (KEPT INTACT) ---
712
  const Auth = {
713
  init: () => {
714
  if (State.user) {
@@ -802,17 +803,19 @@
802
  State.history = JSON.parse(data.historyJSON || "[]");
803
  UI.updateWelcomeScreen();
804
 
805
- State.history.forEach(msg => {
806
  if(msg.role === 'user') {
807
  let dispText = msg.content;
808
  if(dispText.includes('[SYSTEM REAL-TIME WEATHER:')) dispText = dispText.split('\n\n[SYSTEM REAL-TIME WEATHER:')[0];
809
  if(dispText.includes('---END DATA---')) dispText = dispText.split('User: ')[1] || '[Attached File]';
810
  Chat.renderUser(dispText);
811
  } else {
812
- const botObj = Chat.renderBot(Date.now() + Math.random());
 
813
  Chat.parseAndRender(msg.content, false, false, botObj.contentDiv);
 
814
  }
815
- });
816
  setTimeout(UI.scrollToBottom, 200);
817
  if(window.innerWidth <= 900) UI.toggleSidebar();
818
  }
@@ -840,31 +843,74 @@
840
  }
841
  };
842
 
843
- // --- 4. ENVIRONMENT & THINKING ---
844
  const EnvironmentManager = {
845
  toggle: () => {
846
- const btn = document.getElementById('btnEnv'); const status = document.getElementById('locStatus');
847
- if(State.location) { State.location = null; State.weatherContext = null; btn.classList.remove('active-env'); status.style.display = 'none'; }
848
- else {
 
 
 
 
 
849
  if(navigator.geolocation) {
850
- btn.style.color = 'var(--brand-warning)'; status.style.display = 'flex'; status.innerHTML = `<span class="flicker-text">Finding location...</span>`;
 
851
  navigator.geolocation.getCurrentPosition(async pos => {
852
  State.location = { lat: pos.coords.latitude, lng: pos.coords.longitude };
853
- btn.classList.add('active-env'); status.innerHTML = `<span style="color:var(--brand-success);">Location active!</span>`;
854
  setTimeout(() => status.style.display = 'none', 3000);
855
- }, err => { status.innerHTML = `<span style="color:var(--brand-danger);">Location denied.</span>`; setTimeout(() => status.style.display = 'none', 3000); });
 
 
 
 
 
 
856
  }
857
  }
858
  }
859
  };
860
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
861
  const ThinkingManager = {
862
- toggleMenu: () => { document.getElementById('thinkMenu').classList.toggle('active'); document.getElementById('attachMenu').classList.remove('active'); },
863
- setEffort: (level) => { State.thinkingMode = true; State.thinkingEffort = level; document.getElementById('thinkMenu').classList.remove('active'); },
864
- disable: () => { State.thinkingMode = false; document.getElementById('thinkMenu').classList.remove('active'); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865
  };
866
 
867
- // --- 5. 🌟 FIX 1: AUTO IMAGE COMPRESSION FOR PAYLOAD BUG ---
868
  const FileSys = {
869
  process: async (input, type) => {
870
  document.getElementById('attachMenu').classList.remove('active');
@@ -880,7 +926,6 @@
880
  document.getElementById('docPreview').style.display = 'none';
881
 
882
  r.onload = (e) => {
883
- // Compress Image logic to prevent Payload Too Large
884
  const img = new Image();
885
  img.onload = () => {
886
  const canvas = document.createElement('canvas');
@@ -917,7 +962,8 @@
917
  init: () => {
918
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
919
  if (!SR) return alert("Speech Recognition not supported.");
920
- Speech.rec = new SR(); Speech.rec.continuous = false; Speech.rec.interimResults = true; Speech.rec.lang = 'en-US';
 
921
  Speech.rec.onstart = () => { Speech.isRec = true; document.getElementById('btnStt').classList.add('recording'); };
922
  Speech.rec.onresult = (e) => {
923
  let trans = ''; for (let i = e.resultIndex; i < e.results.length; ++i) { if (e.results[i].isFinal) trans += e.results[i][0].transcript; }
@@ -929,16 +975,18 @@
929
  stop: () => { if(Speech.rec) Speech.rec.stop(); Speech.isRec = false; document.getElementById('btnStt').classList.remove('recording'); }
930
  };
931
 
932
- // --- MARKDOWN RENDERER ---
933
  const MDRenderer = new marked.Renderer();
934
  MDRenderer.code = function(code, lang) {
935
- const highlighted = hljs.highlightAuto(code).value;
936
- return `
937
- <pre><code class="hljs">${highlighted}</code></pre>`;
 
 
 
 
938
  };
939
  marked.use({ renderer: MDRenderer });
940
 
941
- // --- 7. CHAT ENGINE (ROCK SOLID & PREMIUM AI FEEL) ---
942
  const Chat = {
943
  renderUser: (txt, attachUI = '') => {
944
  const c = document.getElementById('chatMessages');
@@ -947,7 +995,6 @@
947
  c.appendChild(w); UI.scrollToBottom();
948
  },
949
 
950
- // 🌟 FIX 2: NO BOUNDARY BOX FOR AI
951
  renderBot: (msgId) => {
952
  const c = document.getElementById('chatMessages');
953
  const w = document.createElement('div'); w.className = 'message-wrapper';
@@ -955,7 +1002,6 @@
955
  const listenIcon = `<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>`;
956
  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>`;
957
 
958
- // Completely transparent background
959
  w.innerHTML = `
960
  <div class="bot-message">
961
  <div class="bot-avatar"><img src="${Config.LOGO}"></div>
@@ -1000,18 +1046,39 @@
1000
 
1001
  let mainText = normalizedText.replace(thinkRegex, '').trim();
1002
 
1003
- // Add smooth fade-in typing effect
1004
  if(mainText) finalHtml += `<div style="animation: fadeIn 0.3s ease;">${marked.parse(mainText)}</div>`;
1005
 
1006
  if(isProcessing) {
1007
- finalHtml += `<div class="typing-indicator"><div class="typing-dot"></div><div class="typing-dot"></div></div>`;
1008
  }
1009
  container.innerHTML = finalHtml;
1010
  },
1011
 
1012
  copyMsg: (id) => {
1013
- const text = document.getElementById(`bot-content-${id}`).innerText;
1014
- navigator.clipboard.writeText(text).then(() => alert('Copied to clipboard.'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1015
  },
1016
 
1017
  handleSend: async () => {
@@ -1020,7 +1087,10 @@
1020
  const text = input.value.trim();
1021
  if(!text && !State.attachment) return;
1022
 
1023
- State.isProcessing = true; document.getElementById('btnSend').disabled = true;
 
 
 
1024
  State.lastUserMessage = text;
1025
  input.value = ''; input.style.height = 'auto';
1026
  UI.updateWelcomeScreen();
@@ -1062,7 +1132,7 @@
1062
  const res = await fetch(Config.API_ENDPOINT, {
1063
  method: 'POST', headers: {'Content-Type': 'application/json'},
1064
  body: JSON.stringify({
1065
- message: systemInjectedPayload, attachments: mediaArray, is_search: false,
1066
  location: State.location, thinking_mode: State.thinkingMode, thinking_effort: State.thinkingEffort,
1067
  history: State.history
1068
  }),
@@ -1099,13 +1169,22 @@
1099
  HistoryManager.saveCurrent();
1100
 
1101
  } catch(e) {
1102
- botObj.contentDiv.innerHTML += `<br><span style="color:var(--brand-danger);">Connection Offline.</span>`;
 
 
 
 
 
 
 
 
1103
  }
1104
  finally {
1105
  State.isProcessing = false;
1106
  document.getElementById('btnSend').disabled = false;
 
 
1107
 
1108
- // 🌟 FIX 4: AUTO PREPARE TTS (FAST)
1109
  if(botObj.listenBtn && fullText.trim().length > 0) {
1110
  TTSManager.autoPrepare(msgId, fullText, botObj.listenBtn);
1111
  }
 
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
 
19
 
20
  <style>
21
  :root {
 
22
  --bg-main: #f8fafd;
23
  --bg-sidebar: #f0f4f9;
24
+ --bg-user-msg: rgba(227, 236, 250, 0.85);
25
 
26
  --text-primary: #1f1f1f;
27
  --text-secondary: #444746;
 
34
  --brand-accent: #0b57d0;
35
  --brand-success: #146c2e;
36
  --brand-danger: #b3261e;
37
+ --brand-warning: #e37400;
38
 
39
  --font-ui: 'Inter', sans-serif;
40
  --font-code: 'JetBrains Mono', monospace;
41
  --radius-sm: 12px;
42
  --radius-md: 18px;
43
+ --radius-lg: 32px;
44
  }
45
 
46
  * { box-sizing: border-box; margin: 0; padding: 0; }
 
52
  display: flex; overflow: hidden; -webkit-font-smoothing: antialiased;
53
  }
54
 
 
55
  body::before {
56
  content: ''; position: absolute; inset: 0; z-index: -1;
57
  background-image: radial-gradient(circle at 15% 50%, rgba(219, 234, 254, 0.5) 0%, transparent 40%),
 
67
  ::-webkit-scrollbar-thumb { background: rgba(114, 119, 117, 0.3); border-radius: 10px; }
68
  ::-webkit-scrollbar-thumb:hover { background: rgba(114, 119, 117, 0.6); }
69
 
70
+ @keyframes fadeIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
71
+ @keyframes pulseOpacity { 0% { opacity: 0.4; } 100% { opacity: 1; } }
72
+ @keyframes spin { 100% { transform: rotate(360deg); } }
73
+ @keyframes typingBounce {
74
+ 0%, 60%, 100% { transform: translateY(0); opacity: 0.4; }
75
+ 30% { transform: translateY(-4px); opacity: 1; }
76
+ }
77
+
78
  .btn-menu {
79
  position: absolute; top: 16px; left: 16px; z-index: 50;
80
  width: 44px; height: 44px; border-radius: 50%;
 
83
  }
84
  .btn-menu:hover { background: rgba(31, 31, 31, 0.08); color: var(--text-primary); }
85
 
 
86
  .sidebar {
87
  width: 280px; background-color: var(--bg-sidebar); border-right: 1px solid var(--border-light);
88
  display: flex; flex-direction: column; transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); z-index: 100;
 
126
  .btn-power { width: 34px; height: 34px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: 0.2s; }
127
  .btn-power:hover { background: #fce8e8; color: var(--brand-danger); }
128
 
 
129
  .main-area { flex: 1; display: flex; flex-direction: column; position: relative; transition: 0.3s; height: 100%; overflow: hidden; }
130
 
 
131
  .chat-container { flex: 1; overflow-y: auto; padding: 60px 20px 180px; display: flex; flex-direction: column; align-items: center; scroll-behavior: smooth; }
132
 
133
  .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; }
 
137
 
138
  #chatMessages { width: 100%; display: flex; flex-direction: column; align-items: center; }
139
 
 
140
  .message-wrapper { width: 100%; max-width: 850px; margin-bottom: 32px; display: flex; flex-direction: column; }
141
 
 
142
  .user-message {
143
  align-self: flex-end; max-width: 85%;
144
  background: var(--bg-user-msg);
 
146
  padding: 14px 20px; border-radius: 24px 24px 6px 24px;
147
  font-size: 15.5px; line-height: 1.6; color: var(--text-primary);
148
  box-shadow: 0 2px 10px rgba(0,0,0,0.02);
149
+ word-wrap: break-word;
150
  }
151
 
 
152
  .bot-message {
153
  align-self: flex-start; max-width: 100%; display: flex; gap: 16px; width: 100%;
154
  background: transparent; padding: 0; border: none; box-shadow: none; margin-top: 4px;
 
157
  .bot-avatar { width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0; overflow: hidden; border: 1px solid var(--border-light); background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.05); margin-top: 2px;}
158
  .bot-avatar img { width: 100%; height: 100%; object-fit: cover; }
159
 
160
+ .bot-content { flex: 1; font-size: 15.5px; line-height: 1.7; color: var(--text-primary); min-width: 0; word-wrap: break-word; }
161
 
162
  .bot-content p { margin-bottom: 16px; }
163
  .bot-content p:last-child { margin-bottom: 0; }
164
  .bot-content strong { font-weight: 600; color: #000; }
165
  .bot-content code { font-family: var(--font-code); background: rgba(31,31,31,0.05); padding: 3px 6px; border-radius: 6px; font-size: 0.9em; color: #0284c7; }
166
 
 
167
  .bot-content pre {
168
  background: #0d1117; padding: 40px 16px 16px; border-radius: var(--radius-md);
169
  overflow-x: auto; margin: 16px 0; box-shadow: 0 4px 16px rgba(0,0,0,0.1);
 
176
  }
177
  .bot-content pre code { background: none; padding: 0; color: #e2e8f0; font-size: 13.5px; text-shadow: none; }
178
 
 
179
  .chat-attachment-container { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
180
  .chat-file-pill {
181
  display: inline-flex; align-items: center; gap: 8px;
 
185
  box-shadow: 0 2px 6px rgba(0,0,0,0.03);
186
  }
187
  .chat-img-preview {
188
+ max-width: 120px; max-height: 120px;
189
  border-radius: 12px; border: 1px solid var(--border-light);
190
  object-fit: cover; box-shadow: 0 4px 12px rgba(0,0,0,0.06);
191
  }
192
 
 
193
  .qwen-think-box {
194
  background: rgba(255,255,255,0.7); border: 1px solid var(--border-light); border-radius: 16px;
195
  margin-bottom: 16px; overflow: hidden; transition: 0.2s; backdrop-filter: blur(10px);
 
204
  .qwen-think-box[open] summary svg.arrow { transform: rotate(90deg); }
205
  .qwen-think-content { padding: 16px; border-top: 1px solid var(--border-light); font-size: 13.5px; color: var(--text-secondary); font-style: italic; line-height: 1.6; opacity: 0.8;}
206
 
 
207
  .action-status {
208
  display: inline-flex; align-items: center; gap: 8px; padding: 6px 14px;
209
  background: rgba(255,255,255,0.7); border-radius: 16px; font-size: 13px; font-weight: 500;
 
212
  }
213
  .action-status.active { background: rgba(227, 236, 250, 0.8); border-color: rgba(168, 199, 250, 0.5); color: var(--brand-accent); }
214
  .flicker-text { animation: pulseOpacity 1.2s infinite alternate; }
 
 
215
  .spin-icon { animation: spin 1s linear infinite; }
216
 
217
+ .typing-indicator { display: inline-flex; gap: 4px; padding: 8px 0; align-items: center; }
218
+ .typing-dot {
219
+ width: 7px; height: 7px; border-radius: 50%;
220
+ background: var(--text-tertiary);
221
+ animation: typingBounce 1.2s infinite;
222
+ }
223
+ .typing-dot:nth-child(2) { animation-delay: 0.15s; }
224
+ .typing-dot:nth-child(3) { animation-delay: 0.3s; }
225
+
226
  .msg-actions { display: flex; gap: 8px; margin-top: 14px; opacity: 0; transition: opacity 0.2s; align-items: center; flex-wrap: wrap; }
227
  .message-wrapper:hover .msg-actions { opacity: 1; }
228
  @media (hover: none) { .msg-actions { opacity: 1; } }
 
230
  .action-btn { display: flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 500; color: var(--text-secondary); padding: 8px 14px; border-radius: 12px; transition: 0.2s; background: rgba(255,255,255,0.6); border: 1px solid var(--border-light); backdrop-filter: blur(4px); }
231
  .action-btn:hover { background: #fff; color: var(--text-primary); box-shadow: 0 2px 6px rgba(0,0,0,0.04); }
232
 
 
233
  .action-btn.listen-btn { background: #e3ecfa; color: #0b57d0; border-color: transparent; }
234
  .action-btn.listen-btn:hover { background: #d3e3fd; }
235
 
 
236
  .input-dock {
237
  position: absolute; bottom: 0; left: 0; width: 100%;
238
  padding: 10px 20px 24px; padding-bottom: max(24px, env(safe-area-inset-bottom));
 
247
  box-shadow: 0 4px 12px rgba(0,0,0,0.05); backdrop-filter: blur(12px);
248
  }
249
 
 
250
  .input-container {
251
  width: 100%; max-width: 900px;
252
+ background: rgba(255, 255, 255, 0.65);
253
+ backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px);
254
+ border: 1px solid rgba(255, 255, 255, 0.9);
255
  box-shadow: 0 8px 30px rgba(0,0,0,0.04);
256
  border-radius: var(--radius-lg);
257
  padding: 10px 12px 10px 20px;
 
259
  }
260
  .input-container:focus-within { border-color: rgba(255,255,255,1); box-shadow: 0 10px 40px rgba(11, 87, 208, 0.08); background: rgba(255, 255, 255, 0.85); }
261
 
 
262
  .file-preview-bar { display: none; padding: 4px 0 12px; align-items: center; gap: 10px; margin-bottom: 4px; }
263
  .file-preview-bar.active { display: flex; }
264
  .file-preview-pill {
 
273
 
274
  .input-row { display: flex; align-items: flex-end; gap: 10px; width: 100%; }
275
 
276
+ .tools-left { display: flex; gap: 4px; align-items: center; margin-bottom: 2px; position: relative;}
277
  .tool-btn { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); transition: 0.2s; position: relative; flex-shrink: 0; }
278
  .tool-btn:hover { background: rgba(31,31,31,0.06); color: var(--text-primary); }
279
 
 
280
  .tool-btn.active-env { color: var(--brand-accent); background: #e3ecfa; }
281
  .tool-btn.active-think-low { color: var(--brand-danger); background: #fce8e8; }
282
  .tool-btn.active-think-medium { color: var(--brand-warning); background: #fef3c7; }
283
  .tool-btn.active-think-high { color: var(--brand-accent); background: #e3ecfa; }
284
+ .tool-btn.active-search { color: var(--brand-success); background: #e6f4ea; }
285
  .tool-btn.recording { color: var(--brand-danger); animation: pulseOpacity 1s infinite alternate; }
286
 
287
  .chat-input { flex: 1; padding: 12px 4px; font-size: 15.5px; resize: none; max-height: 180px; min-height: 24px; color: var(--text-primary); line-height: 1.5; background: transparent; }
288
  .chat-input::placeholder { color: var(--text-tertiary); }
289
 
 
290
  .btn-send {
291
  width: 44px; height: 44px; border-radius: 50%;
292
+ background: #c2e7ff;
293
+ color: #041e49;
294
  display: flex; align-items: center; justify-content: center;
295
  transition: 0.2s; flex-shrink: 0; margin-bottom: 2px;
296
  }
297
  .btn-send:hover:not(:disabled) { background: #b4dfff; transform: scale(1.05); }
298
  .btn-send:disabled { background: rgba(31,31,31,0.08); color: rgba(31,31,31,0.3); cursor: not-allowed; }
299
+
300
+ .btn-stop {
301
+ width: 44px; height: 44px; border-radius: 50%;
302
+ background: #fce8e8;
303
+ color: var(--brand-danger);
304
+ display: none; align-items: center; justify-content: center;
305
+ transition: 0.2s; flex-shrink: 0; margin-bottom: 2px;
306
+ }
307
+ .btn-stop.active { display: flex; }
308
+ .btn-stop:hover { background: #fca5a5; color: #fff; }
309
 
 
310
  .hidden-input { display: none; }
311
  .dropdown-menu { position: absolute; bottom: calc(100% + 14px); left: 0; background: rgba(255,255,255,0.95); backdrop-filter: blur(16px); border: 1px solid var(--border-light); border-radius: 20px; padding: 8px; box-shadow: 0 12px 32px rgba(0,0,0,0.1); display: none; flex-direction: column; min-width: 200px; z-index: 100; transform-origin: bottom left; }
312
  .dropdown-menu.active { display: flex; animation: dropUpFade 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
 
318
  .dropdown-item.selected { background: #e3ecfa; color: var(--brand-accent); font-weight: 600; }
319
  .dropdown-item svg { color: inherit; }
320
 
 
321
  .auth-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.5); backdrop-filter: blur(8px); display: none; align-items: center; justify-content: center; z-index: 2000; }
322
  .auth-modal { background: #fff; width: 90%; max-width: 380px; border-radius: var(--radius-lg); padding: 32px; box-shadow: 0 24px 48px rgba(0,0,0,0.15); position: relative; }
323
  .auth-close { position: absolute; top: 16px; right: 16px; color: var(--text-tertiary); width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; border-radius: 50%; transition: 0.2s; }
 
336
  .auth-phase.active { display: block; }
337
  .auth-message { font-size: 13px; text-align: center; margin-top: 14px; min-height: 18px; font-weight: 500; }
338
 
 
339
  .mobile-overlay { display: none; position: fixed; inset: 0; background: rgba(15,23,42,0.3); backdrop-filter: blur(3px); z-index: 95; opacity: 0; transition: 0.3s; pointer-events: none; }
340
  .mobile-overlay.active { display: block; opacity: 1; pointer-events: all; }
341
 
 
346
  .input-container { border-radius: 28px; padding: 8px 10px 8px 16px; }
347
  .tool-btn { width: 38px; height: 38px; }
348
  .btn-send { width: 40px; height: 40px; }
349
+ .btn-stop { width: 40px; height: 40px; }
350
  .chat-container { padding: 60px 10px 160px; }
351
  .user-message { font-size: 14.5px; max-width: 90%; }
352
  .bot-content { font-size: 14.5px; }
 
388
 
389
  <main class="main-area">
390
  <div class="chat-container" id="chatContainer">
 
391
  <div class="welcome-center" id="welcomeScreen">
392
  <img src="https://i.ibb.co/MyYStcGP/TIRANGA-20260613-131924-0000.png" alt="CODE VED Logo">
393
  <h1>CODE VED</h1>
394
  <p id="welcomeGreeting">Engineered by Divy Patel</p>
395
  </div>
396
 
 
397
  <div id="chatMessages"></div>
398
  </div>
399
 
400
  <div class="input-dock" id="inputDock">
 
401
  <div class="loc-toast" id="locStatus"></div>
402
 
403
  <div class="input-container">
 
404
  <div class="file-preview-bar" id="filePreviewBar">
405
  <div class="file-preview-pill">
406
  <img id="imgPreview" class="file-preview-icon" src="" style="display:none;">
 
412
 
413
  <div class="input-row">
414
  <div class="tools-left">
 
415
  <div style="position: relative;">
416
  <button class="tool-btn" onclick="UI.toggleAttachMenu()" title="Attach File">
417
  <svg style="width:24px;height:24px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
 
432
  <span style="display:flex;align-items:center;gap:10px;"><svg style="width:16px;height:16px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a5 5 0 0 0-5 5v2a5 5 0 0 0 10 0V7a5 5 0 0 0-5-5z"></path><path d="M8 14H6a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2h-2"></path></svg> Thinking Mode</span>
433
  </div>
434
 
 
435
  <div class="dropdown-item" onclick="TTSManager.toggleMenu()" style="color:#0b57d0;">
436
  <span style="display:flex;align-items:center;gap:10px;"><svg style="width:16px;height:16px;" 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> Select Voice Mode</span>
437
  </div>
438
  </div>
439
 
 
440
  <div class="dropdown-menu" id="voiceMenu" style="min-width: 200px; left: 45px; bottom: 40px;">
441
  <div class="dropdown-header">Premium Voices</div>
442
  <div id="voiceOptionsList"></div>
 
455
  <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')">
456
  </div>
457
 
458
+ <button class="tool-btn" id="btnSearch" onclick="SearchManager.toggle()" title="Web Search">
459
+ <svg style="width:22px;height:22px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
460
+ </button>
461
  </div>
462
 
463
  <textarea class="chat-input" id="mainInput" placeholder="Ask CODE VED..." rows="1"></textarea>
464
 
 
465
  <button class="tool-btn" id="btnStt" onclick="Speech.toggle()" title="Voice Typing">
466
  <svg style="width:22px;height:22px;" 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>
467
  </button>
468
 
469
+ <button class="btn-stop" id="btnStop" onclick="Chat.stopGeneration()">
470
+ <svg style="width:18px;height:18px;" viewBox="0 0 24 24" fill="currentColor"><rect x="6" y="6" width="12" height="12" rx="2"></rect></svg>
471
+ </button>
472
+
473
  <button class="btn-send" id="btnSend" onclick="Chat.handleSend()">
474
  <svg style="width:20px;height:20px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="19" x2="12" y2="5"></line><polyline points="5 12 12 5 19 12"></polyline></svg>
475
  </button>
 
478
  </div>
479
  </main>
480
 
481
+ <div class="auth-overlay" id="authModal" onclick="if(event.target===this) Auth.closeModal()">
 
482
  <div class="auth-modal">
483
  <button class="auth-close" onclick="Auth.closeModal()"><svg style="width:20px;height:20px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg></button>
484
  <div class="auth-title">System Access</div>
 
515
  </div>
516
 
517
  <script>
 
518
  const Config = {
519
  GAS_URL: "https://script.google.com/macros/s/AKfycbzNO3inVc33ImhfLyde-JjjK9ZlPckLBksqCnCzelfhcklX6mp8KW8vfPTW4oWJTCcN/exec",
520
  API_ENDPOINT: "/api/chat",
 
535
  weatherContext: null,
536
  thinkingMode: false,
537
  thinkingEffort: "medium",
538
+ searchEnabled: false,
539
  lastUserMessage: null
540
  };
541
 
542
  if (typeof pdfjsLib !== 'undefined') { pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; }
543
 
 
544
  const TTSManager = {
545
  cache: {},
546
  currentAudio: null,
 
577
 
578
  setVoice: (code) => {
579
  TTSManager.selectedVoice = code;
580
+ TTSManager.cache = {};
581
  TTSManager.initUI();
582
  document.getElementById('voiceMenu').classList.remove('active');
583
  document.getElementById('attachMenu').classList.remove('active');
 
591
  return clean;
592
  },
593
 
 
594
  autoPrepare: async (msgId, fullText, btnElement) => {
595
  const cleanText = TTSManager.cleanTextForTTS(fullText);
596
  if(!cleanText) return;
 
610
  body: JSON.stringify({
611
  text: cleanText,
612
  voice: TTSManager.selectedVoice,
613
+ language_name: "Hindi"
614
  })
615
  });
616
 
 
627
 
628
  } catch (e) {
629
  btnElement.innerHTML = `<span style="color:var(--brand-danger)">Voice Error</span>`;
630
+ setTimeout(() => { if(btnElement) btnElement.style.display = 'none'; }, 3000);
631
  }
632
  },
633
 
 
634
  play: (msgId, btnElement) => {
635
  const audioUrl = TTSManager.cache[msgId];
636
+ if(!audioUrl) {
637
+ TTSManager.autoPrepare(msgId, document.getElementById(`bot-content-${msgId}`).innerText, btnElement);
638
+ return;
639
+ }
640
 
641
  if(TTSManager.currentAudio) {
642
  TTSManager.currentAudio.pause();
 
644
  }
645
 
646
  TTSManager.currentAudio = new Audio(audioUrl);
647
+ TTSManager.currentAudio.playbackRate = 1.15;
648
 
649
  const originalHTML = btnElement.innerHTML;
650
  const playingIcon = `<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>`;
 
663
  }
664
  };
665
 
 
666
  const UI = {
667
  toggleSidebar: () => {
668
  const sb = document.getElementById('sidebar');
 
710
  inp.addEventListener('input', function() { UI.autoGrow(this); });
711
  inp.addEventListener('keydown', function(e) { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); Chat.handleSend(); } });
712
 
 
713
  const Auth = {
714
  init: () => {
715
  if (State.user) {
 
803
  State.history = JSON.parse(data.historyJSON || "[]");
804
  UI.updateWelcomeScreen();
805
 
806
+ for (const msg of State.history) {
807
  if(msg.role === 'user') {
808
  let dispText = msg.content;
809
  if(dispText.includes('[SYSTEM REAL-TIME WEATHER:')) dispText = dispText.split('\n\n[SYSTEM REAL-TIME WEATHER:')[0];
810
  if(dispText.includes('---END DATA---')) dispText = dispText.split('User: ')[1] || '[Attached File]';
811
  Chat.renderUser(dispText);
812
  } else {
813
+ const msgId = 'hist_' + Date.now() + '_' + Math.random().toString(36).substr(2,5);
814
+ const botObj = Chat.renderBot(msgId);
815
  Chat.parseAndRender(msg.content, false, false, botObj.contentDiv);
816
+ TTSManager.autoPrepare(msgId, msg.content, botObj.listenBtn);
817
  }
818
+ }
819
  setTimeout(UI.scrollToBottom, 200);
820
  if(window.innerWidth <= 900) UI.toggleSidebar();
821
  }
 
843
  }
844
  };
845
 
 
846
  const EnvironmentManager = {
847
  toggle: () => {
848
+ const status = document.getElementById('locStatus');
849
+ document.getElementById('attachMenu').classList.remove('active');
850
+
851
+ if(State.location) {
852
+ State.location = null;
853
+ State.weatherContext = null;
854
+ status.style.display = 'none';
855
+ } else {
856
  if(navigator.geolocation) {
857
+ status.style.display = 'flex';
858
+ status.innerHTML = `<span class="flicker-text">Finding location...</span>`;
859
  navigator.geolocation.getCurrentPosition(async pos => {
860
  State.location = { lat: pos.coords.latitude, lng: pos.coords.longitude };
861
+ status.innerHTML = `<span style="color:var(--brand-success);">Location active!</span>`;
862
  setTimeout(() => status.style.display = 'none', 3000);
863
+ }, err => {
864
+ status.innerHTML = `<span style="color:var(--brand-danger);">Location denied.</span>`;
865
+ setTimeout(() => status.style.display = 'none', 3000);
866
+ });
867
+ } else {
868
+ status.innerHTML = `<span style="color:var(--brand-danger);">Geolocation not supported.</span>`;
869
+ setTimeout(() => status.style.display = 'none', 3000);
870
  }
871
  }
872
  }
873
  };
874
 
875
+ const SearchManager = {
876
+ toggle: () => {
877
+ State.searchEnabled = !State.searchEnabled;
878
+ const btn = document.getElementById('btnSearch');
879
+ if (State.searchEnabled) {
880
+ btn.classList.add('active-search');
881
+ btn.title = 'Web Search: ON';
882
+ } else {
883
+ btn.classList.remove('active-search');
884
+ btn.title = 'Web Search: OFF';
885
+ }
886
+ }
887
+ };
888
+
889
  const ThinkingManager = {
890
+ toggleMenu: () => {
891
+ document.getElementById('thinkMenu').classList.toggle('active');
892
+ document.getElementById('attachMenu').classList.remove('active');
893
+ },
894
+ setEffort: (level) => {
895
+ State.thinkingMode = true;
896
+ State.thinkingEffort = level;
897
+ document.getElementById('thinkMenu').classList.remove('active');
898
+ ThinkingManager.updateIndicator();
899
+ },
900
+ disable: () => {
901
+ State.thinkingMode = false;
902
+ document.getElementById('thinkMenu').classList.remove('active');
903
+ ThinkingManager.updateIndicator();
904
+ },
905
+ updateIndicator: () => {
906
+ const btn = document.getElementById('btnSearch');
907
+ btn.classList.remove('active-think-low', 'active-think-medium', 'active-think-high');
908
+ if (State.thinkingMode) {
909
+ btn.classList.add(`active-think-${State.thinkingEffort}`);
910
+ }
911
+ }
912
  };
913
 
 
914
  const FileSys = {
915
  process: async (input, type) => {
916
  document.getElementById('attachMenu').classList.remove('active');
 
926
  document.getElementById('docPreview').style.display = 'none';
927
 
928
  r.onload = (e) => {
 
929
  const img = new Image();
930
  img.onload = () => {
931
  const canvas = document.createElement('canvas');
 
962
  init: () => {
963
  const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
964
  if (!SR) return alert("Speech Recognition not supported.");
965
+ Speech.rec = new SR(); Speech.rec.continuous = false; Speech.rec.interimResults = true;
966
+ Speech.rec.lang = navigator.language || 'en-US';
967
  Speech.rec.onstart = () => { Speech.isRec = true; document.getElementById('btnStt').classList.add('recording'); };
968
  Speech.rec.onresult = (e) => {
969
  let trans = ''; for (let i = e.resultIndex; i < e.results.length; ++i) { if (e.results[i].isFinal) trans += e.results[i][0].transcript; }
 
975
  stop: () => { if(Speech.rec) Speech.rec.stop(); Speech.isRec = false; document.getElementById('btnStt').classList.remove('recording'); }
976
  };
977
 
 
978
  const MDRenderer = new marked.Renderer();
979
  MDRenderer.code = function(code, lang) {
980
+ let highlighted;
981
+ if (lang && hljs.getLanguage(lang)) {
982
+ highlighted = hljs.highlight(code, { language: lang }).value;
983
+ } else {
984
+ highlighted = hljs.highlightAuto(code).value;
985
+ }
986
+ return `<pre><code class="hljs">${highlighted}</code></pre>`;
987
  };
988
  marked.use({ renderer: MDRenderer });
989
 
 
990
  const Chat = {
991
  renderUser: (txt, attachUI = '') => {
992
  const c = document.getElementById('chatMessages');
 
995
  c.appendChild(w); UI.scrollToBottom();
996
  },
997
 
 
998
  renderBot: (msgId) => {
999
  const c = document.getElementById('chatMessages');
1000
  const w = document.createElement('div'); w.className = 'message-wrapper';
 
1002
  const listenIcon = `<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>`;
1003
  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>`;
1004
 
 
1005
  w.innerHTML = `
1006
  <div class="bot-message">
1007
  <div class="bot-avatar"><img src="${Config.LOGO}"></div>
 
1046
 
1047
  let mainText = normalizedText.replace(thinkRegex, '').trim();
1048
 
 
1049
  if(mainText) finalHtml += `<div style="animation: fadeIn 0.3s ease;">${marked.parse(mainText)}</div>`;
1050
 
1051
  if(isProcessing) {
1052
+ finalHtml += `<div class="typing-indicator"><div class="typing-dot"></div><div class="typing-dot"></div><div class="typing-dot"></div></div>`;
1053
  }
1054
  container.innerHTML = finalHtml;
1055
  },
1056
 
1057
  copyMsg: (id) => {
1058
+ const el = document.getElementById(`bot-content-${id}`);
1059
+ const thinkBox = el.querySelector('.qwen-think-box');
1060
+ const clone = el.cloneNode(true);
1061
+ const cloneThink = clone.querySelector('.qwen-think-box');
1062
+ if (cloneThink) cloneThink.remove();
1063
+ const text = clone.innerText;
1064
+
1065
+ navigator.clipboard.writeText(text).then(() => {
1066
+ const btn = el.parentElement.querySelector('.action-btn:not(.listen-btn)');
1067
+ const original = btn.innerHTML;
1068
+ btn.innerHTML = `<svg style="width:14px;height:14px;" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied!`;
1069
+ setTimeout(() => btn.innerHTML = original, 1500);
1070
+ });
1071
+ },
1072
+
1073
+ stopGeneration: () => {
1074
+ if (State.abortController) {
1075
+ State.abortController.abort();
1076
+ State.abortController = null;
1077
+ }
1078
+ State.isProcessing = false;
1079
+ document.getElementById('btnSend').disabled = false;
1080
+ document.getElementById('btnSend').style.display = 'flex';
1081
+ document.getElementById('btnStop').classList.remove('active');
1082
  },
1083
 
1084
  handleSend: async () => {
 
1087
  const text = input.value.trim();
1088
  if(!text && !State.attachment) return;
1089
 
1090
+ State.isProcessing = true;
1091
+ document.getElementById('btnSend').style.display = 'none';
1092
+ document.getElementById('btnStop').classList.add('active');
1093
+
1094
  State.lastUserMessage = text;
1095
  input.value = ''; input.style.height = 'auto';
1096
  UI.updateWelcomeScreen();
 
1132
  const res = await fetch(Config.API_ENDPOINT, {
1133
  method: 'POST', headers: {'Content-Type': 'application/json'},
1134
  body: JSON.stringify({
1135
+ message: systemInjectedPayload, attachments: mediaArray, is_search: State.searchEnabled,
1136
  location: State.location, thinking_mode: State.thinkingMode, thinking_effort: State.thinkingEffort,
1137
  history: State.history
1138
  }),
 
1169
  HistoryManager.saveCurrent();
1170
 
1171
  } catch(e) {
1172
+ if (e.name === 'AbortError') {
1173
+ botObj.contentDiv.innerHTML += `<br><span style="color:var(--text-tertiary); font-style:italic;">⏹ Generation stopped.</span>`;
1174
+ if (fullText.trim()) {
1175
+ State.history.push({ role: 'assistant', content: fullText });
1176
+ HistoryManager.saveCurrent();
1177
+ }
1178
+ } else {
1179
+ botObj.contentDiv.innerHTML += `<br><span style="color:var(--brand-danger);">Connection Offline.</span>`;
1180
+ }
1181
  }
1182
  finally {
1183
  State.isProcessing = false;
1184
  document.getElementById('btnSend').disabled = false;
1185
+ document.getElementById('btnSend').style.display = 'flex';
1186
+ document.getElementById('btnStop').classList.remove('active');
1187
 
 
1188
  if(botObj.listenBtn && fullText.trim().length > 0) {
1189
  TTSManager.autoPrepare(msgId, fullText, botObj.listenBtn);
1190
  }