Spaces:
Running
Running
Vedika commited on
Update index.html
Browse files- 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);
|
| 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;
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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;
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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);
|
| 254 |
-
backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px);
|
| 255 |
-
border: 1px solid rgba(255, 255, 255, 0.9);
|
| 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;
|
| 296 |
-
color: #041e49;
|
| 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 |
-
<
|
|
|
|
|
|
|
|
|
|
| 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 |
-
<
|
| 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: "
|
| 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)
|
|
|
|
|
|
|
|
|
|
| 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;
|
| 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 |
-
|
| 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
|
|
|
|
| 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
|
| 847 |
-
|
| 848 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 849 |
if(navigator.geolocation) {
|
| 850 |
-
|
|
|
|
| 851 |
navigator.geolocation.getCurrentPosition(async pos => {
|
| 852 |
State.location = { lat: pos.coords.latitude, lng: pos.coords.longitude };
|
| 853 |
-
|
| 854 |
setTimeout(() => status.style.display = 'none', 3000);
|
| 855 |
-
}, err => {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 856 |
}
|
| 857 |
}
|
| 858 |
}
|
| 859 |
};
|
| 860 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 861 |
const ThinkingManager = {
|
| 862 |
-
toggleMenu: () => {
|
| 863 |
-
|
| 864 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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;
|
|
|
|
| 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 |
-
|
| 936 |
-
|
| 937 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 1014 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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;
|
|
|
|
|
|
|
|
|
|
| 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:
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
}
|