Spaces:
Running
Running
Vedika commited on
Update index.html
Browse files- index.html +355 -186
index.html
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
|
|
| 1 |
<!DOCTYPE html>
|
| 2 |
<html lang="en" dir="ltr">
|
| 3 |
<head>
|
|
@@ -11,7 +12,7 @@
|
|
| 11 |
<title>Arjun 2.O - Professional Advanced AI</title>
|
| 12 |
|
| 13 |
<!-- =========================================================================================
|
| 14 |
-
|
| 15 |
========================================================================================= -->
|
| 16 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 17 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
@@ -29,14 +30,14 @@
|
|
| 29 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
| 30 |
|
| 31 |
<!-- =========================================================================================
|
| 32 |
-
|
| 33 |
========================================================================================= -->
|
| 34 |
<style>
|
| 35 |
/* ---------------------------------------------------------------------------------------
|
| 36 |
-
A. CSS VARIABLES, DESIGN TOKENS &
|
| 37 |
--------------------------------------------------------------------------------------- */
|
| 38 |
:root {
|
| 39 |
-
/* Brand Identity: Saffron, White, Green (Tiranga) */
|
| 40 |
--brand-saffron: #FF9933;
|
| 41 |
--brand-saffron-light: rgba(255, 153, 51, 0.12);
|
| 42 |
--brand-saffron-dark: #e68a2e;
|
|
@@ -64,11 +65,12 @@
|
|
| 64 |
--text-tertiary: #9ca3af;
|
| 65 |
--text-inverse: #ffffff;
|
| 66 |
--text-error: #ef4444;
|
|
|
|
| 67 |
|
| 68 |
/* Borders & Dividers */
|
| 69 |
--border-light: rgba(0, 0, 0, 0.08);
|
| 70 |
--border-medium: rgba(0, 0, 0, 0.15);
|
| 71 |
-
--border-focus: rgba(255, 153, 51, 0.
|
| 72 |
|
| 73 |
/* Shadows & Elevation */
|
| 74 |
--shadow-xs: 0 1px 2px rgba(0,0,0,0.04);
|
|
@@ -76,6 +78,7 @@
|
|
| 76 |
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.05), 0 2px 4px -1px rgba(0,0,0,0.03);
|
| 77 |
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.05), 0 4px 6px -2px rgba(0,0,0,0.025);
|
| 78 |
--shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.05), 0 10px 10px -5px rgba(0,0,0,0.02);
|
|
|
|
| 79 |
--shadow-inner: inset 0 2px 4px rgba(0,0,0,0.06);
|
| 80 |
|
| 81 |
/* Font Families */
|
|
@@ -96,7 +99,7 @@
|
|
| 96 |
--ease-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 97 |
--ease-spring: 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
| 98 |
|
| 99 |
-
/* Z-Indices */
|
| 100 |
--z-ambient: -1;
|
| 101 |
--z-base: 1;
|
| 102 |
--z-header: 10;
|
|
@@ -107,6 +110,7 @@
|
|
| 107 |
--z-modal: 5000;
|
| 108 |
--z-toast: 9000;
|
| 109 |
--z-live: 9999;
|
|
|
|
| 110 |
}
|
| 111 |
|
| 112 |
/* ---------------------------------------------------------------------------------------
|
|
@@ -116,7 +120,6 @@
|
|
| 116 |
box-sizing: border-box;
|
| 117 |
margin: 0;
|
| 118 |
padding: 0;
|
| 119 |
-
/* CRITICAL: REMOVE BLUE TAP HIGHLIGHT ON MOBILE/BROWSERS */
|
| 120 |
-webkit-tap-highlight-color: transparent !important;
|
| 121 |
-webkit-touch-callout: none !important;
|
| 122 |
}
|
|
@@ -135,8 +138,9 @@
|
|
| 135 |
-moz-osx-font-smoothing: grayscale;
|
| 136 |
}
|
| 137 |
|
| 138 |
-
/*
|
| 139 |
-
*:focus { outline:
|
|
|
|
| 140 |
|
| 141 |
button, input, textarea, select {
|
| 142 |
font-family: inherit;
|
|
@@ -146,19 +150,27 @@
|
|
| 146 |
}
|
| 147 |
|
| 148 |
button { cursor: pointer; user-select: none; }
|
|
|
|
| 149 |
a { color: inherit; text-decoration: none; }
|
| 150 |
img { max-width: 100%; height: auto; display: block; }
|
| 151 |
svg { flex-shrink: 0; }
|
| 152 |
|
| 153 |
-
/*
|
| 154 |
-
::-webkit-scrollbar { width:
|
| 155 |
::-webkit-scrollbar-track { background: transparent; }
|
| 156 |
-
::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.
|
| 157 |
-
::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.
|
| 158 |
|
| 159 |
/* ---------------------------------------------------------------------------------------
|
| 160 |
-
C.
|
| 161 |
--------------------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
.ambient-environment {
|
| 163 |
position: fixed; inset: 0; z-index: var(--z-ambient); overflow: hidden; pointer-events: none;
|
| 164 |
background: linear-gradient(135deg, var(--brand-saffron-light) 0%, var(--bg-primary) 50%, var(--brand-green-light) 100%);
|
|
@@ -185,21 +197,25 @@
|
|
| 185 |
}
|
| 186 |
|
| 187 |
/* ---------------------------------------------------------------------------------------
|
| 188 |
-
D. TOAST NOTIFICATION ENGINE
|
| 189 |
--------------------------------------------------------------------------------------- */
|
| 190 |
.toast-wrapper {
|
| 191 |
position: absolute; top: 24px; left: 50%; transform: translateX(-50%);
|
| 192 |
z-index: var(--z-toast); display: flex; flex-direction: column; gap: 12px;
|
| 193 |
-
pointer-events: none; width: 90%; max-width:
|
| 194 |
}
|
| 195 |
.toast-notification {
|
| 196 |
-
background:
|
| 197 |
border-radius: var(--radius-full); font-size: 14px; font-weight: 600; box-shadow: var(--shadow-xl);
|
| 198 |
animation: toastSlideIn var(--ease-spring) forwards, toastFadeOut 0.4s 3.5s forwards;
|
| 199 |
-
display: flex; align-items: center; gap:
|
| 200 |
-
border: 1px solid rgba(255,255,255,0.1);
|
| 201 |
}
|
| 202 |
-
.toast-notification
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
@keyframes toastSlideIn { from { opacity: 0; transform: translateY(-30px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } }
|
| 204 |
@keyframes toastFadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-20px); } }
|
| 205 |
|
|
@@ -214,22 +230,22 @@
|
|
| 214 |
}
|
| 215 |
.startup-overlay.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
|
| 216 |
|
| 217 |
-
.startup-logo-container { position: relative; margin-bottom: 32px; animation: popIn var(--ease-spring) 0.
|
| 218 |
.startup-logo-container img { width: 140px; height: 140px; border-radius: var(--radius-xl); object-fit: cover; box-shadow: var(--shadow-xl); border: 4px solid var(--bg-primary); position: relative; z-index: 2; }
|
| 219 |
.startup-logo-glow { position: absolute; inset: -20px; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); filter: blur(30px); opacity: 0.4; border-radius: var(--radius-full); z-index: 1; animation: pulseGlow 3s infinite alternate; }
|
| 220 |
|
| 221 |
@keyframes pulseGlow { from { opacity: 0.3; transform: scale(0.95); } to { opacity: 0.6; transform: scale(1.05); } }
|
| 222 |
-
@keyframes popIn {
|
| 223 |
|
| 224 |
-
.startup-title { font-family: var(--font-sans); font-size: 2.
|
| 225 |
-
.startup-desc { font-size: 1.
|
| 226 |
|
| 227 |
-
.btn-startup-launch { padding: 18px 48px; background: var(--text-primary); color: var(--text-inverse); border: none; border-radius: var(--radius-xl); font-size: 1.125rem; font-weight: 700; cursor: pointer; transition: var(--ease-spring); box-shadow: 0 10px 25px rgba(0,0,0,0.15); animation: slideUp var(--ease-smooth) 0.
|
| 228 |
.btn-startup-launch::before { content: ''; position: absolute; top: 0; left: -100%; width: 50%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); transform: skewX(-20deg); transition: 0.5s; }
|
| 229 |
.btn-startup-launch:hover { background: #000000; transform: translateY(-4px) scale(1.02); box-shadow: 0 15px 35px rgba(0,0,0,0.25); }
|
| 230 |
.btn-startup-launch:hover::before { left: 150%; }
|
| 231 |
|
| 232 |
-
@keyframes slideUp { from { opacity: 0; transform: translateY(
|
| 233 |
|
| 234 |
/* ---------------------------------------------------------------------------------------
|
| 235 |
F. CORE APPLICATION LAYOUT & SIDEBAR
|
|
@@ -241,11 +257,11 @@
|
|
| 241 |
.sidebar-header { padding: 28px 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
|
| 242 |
|
| 243 |
.brand-identity { display: flex; align-items: center; gap: 14px; text-decoration: none; }
|
| 244 |
-
.brand-identity img { width:
|
| 245 |
-
.brand-identity:hover img { transform: scale(1.08) rotate(-
|
| 246 |
.brand-typography { display: flex; flex-direction: column; }
|
| 247 |
.brand-name { font-family: var(--font-sans); font-weight: 900; font-size: 20px; color: var(--text-primary); letter-spacing: -0.5px; line-height: 1.1; }
|
| 248 |
-
.brand-edition { font-size: 11px; font-weight: 800; letter-spacing: 1.5px; text-transform: uppercase; background: linear-gradient(90deg, var(--brand-saffron), var(--brand-green)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-top: 4px; }
|
| 249 |
|
| 250 |
.btn-mobile-close { display: none; background: rgba(0,0,0,0.05); border: none; width: 36px; height: 36px; border-radius: 50%; color: var(--text-primary); cursor: pointer; align-items: center; justify-content: center; transition: var(--ease-fast); }
|
| 251 |
.btn-mobile-close:hover { background: rgba(239, 68, 68, 0.15); color: var(--text-error); transform: rotate(90deg); }
|
|
@@ -264,7 +280,7 @@
|
|
| 264 |
.user-details { flex: 1; overflow: hidden; }
|
| 265 |
.user-email-id { font-size: 14px; font-weight: 700; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 266 |
.user-clearance-level { font-size: 11px; color: var(--brand-green); font-weight: 600; margin-top: 2px; display: flex; align-items: center; gap: 4px; }
|
| 267 |
-
.user-clearance-level::before { content: ''; display: block; width: 6px; height: 6px; border-radius: 50%; background: var(--brand-green); box-shadow: var(--
|
| 268 |
.btn-system-logout { background: rgba(239, 68, 68, 0.08); color: var(--text-error); border: none; width: 38px; height: 38px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--ease-fast); }
|
| 269 |
.btn-system-logout:hover { background: var(--text-error); color: #ffffff; transform: scale(1.05); }
|
| 270 |
|
|
@@ -285,13 +301,14 @@
|
|
| 285 |
--------------------------------------------------------------------------------------- */
|
| 286 |
.main-chat-wrapper { flex: 1; display: flex; flex-direction: column; position: relative; min-width: 320px; transition: width var(--ease-smooth); background: transparent; }
|
| 287 |
|
| 288 |
-
.chat-top-bar { padding:
|
| 289 |
.top-bar-left { display: flex; align-items: center; gap: 16px; }
|
| 290 |
.btn-hamburger { display: none; background: var(--bg-primary); border: 1px solid var(--border-light); cursor: pointer; color: var(--text-primary); width: 42px; height: 42px; border-radius: var(--radius-sm); align-items: center; justify-content: center; box-shadow: var(--shadow-sm); transition: var(--ease-fast); }
|
| 291 |
.btn-hamburger:hover { background: var(--bg-secondary); }
|
| 292 |
|
| 293 |
-
.system-status-pill { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 700; color: var(--text-primary); padding: 8px 18px; background: var(--bg-secondary); border-radius: var(--radius-full); border: 1px solid var(--border-light); }
|
| 294 |
-
.pulse-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--brand-green); box-shadow: 0 0 0 3px rgba(19, 136, 8, 0.2); animation: statusPulse 2s infinite; }
|
|
|
|
| 295 |
@keyframes statusPulse { 0% { box-shadow: 0 0 0 0 rgba(19,136,8,0.4); } 70% { box-shadow: 0 0 0 6px rgba(19,136,8,0); } 100% { box-shadow: 0 0 0 0 rgba(19,136,8,0); } }
|
| 296 |
|
| 297 |
/* DYNAMIC LIVE MODE ACTIVATOR */
|
|
@@ -309,11 +326,12 @@
|
|
| 309 |
.hero-avatar-wrapper img { width: 100px; height: 100px; border-radius: var(--radius-xl); object-fit: cover; box-shadow: var(--shadow-xl); border: 3px solid var(--bg-primary); position: relative; z-index: 2; }
|
| 310 |
.hero-avatar-glow { position: absolute; inset: -12px; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); filter: blur(25px); opacity: 0.35; border-radius: 40px; z-index: 1; animation: rotateGlow 10s linear infinite;}
|
| 311 |
@keyframes rotateGlow { 100% { transform: rotate(360deg); } }
|
|
|
|
| 312 |
|
| 313 |
-
.hero-greeting { font-size: 42px; font-weight: 900; letter-spacing: -1.5px; margin-bottom: 18px; color: var(--text-primary); }
|
| 314 |
.hero-description { font-size: 17px; color: var(--text-secondary); line-height: 1.6; margin-bottom: 40px; font-weight: 500; }
|
| 315 |
|
| 316 |
-
/*
|
| 317 |
.guest-warning-badge { font-size: 13px; font-weight: 700; color: var(--text-error); background: #fee2e2; padding: 10px 20px; border-radius: var(--radius-full); margin-bottom: 32px; display: none; align-items: center; gap: 8px; border: 1px solid #fca5a5; box-shadow: var(--shadow-sm); }
|
| 318 |
.guest-warning-badge svg { width: 18px; height: 18px; }
|
| 319 |
|
|
@@ -329,8 +347,8 @@
|
|
| 329 |
/* ---------------------------------------------------------------------------------------
|
| 330 |
H. CHAT MESSAGE BUBBLES & PREVIEWS
|
| 331 |
--------------------------------------------------------------------------------------- */
|
| 332 |
-
.chat-message-row { display: flex; gap: 20px; max-width: 88%; animation: messagePopIn 0.
|
| 333 |
-
@keyframes messagePopIn { from { opacity: 0; transform: translateY(
|
| 334 |
.chat-message-row.user-msg { flex-direction: row-reverse; align-self: flex-end; }
|
| 335 |
.chat-message-row.bot-msg { align-self: flex-start; }
|
| 336 |
|
|
@@ -346,7 +364,8 @@
|
|
| 346 |
.file-badge svg { width: 12px; height: 12px; }
|
| 347 |
|
| 348 |
/* In-Bubble File Previews */
|
| 349 |
-
.chat-in-bubble-img { max-width: 320px; max-height: 320px; border-radius: var(--radius-md); object-fit: cover; border: 2px solid rgba(255,255,255,0.2); box-shadow: var(--shadow-md); margin-bottom: 12px; }
|
|
|
|
| 350 |
.chat-file-card { display: flex; align-items: center; gap: 14px; background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); padding: 12px 18px; border-radius: var(--radius-md); margin-bottom: 12px; max-width: 350px; backdrop-filter: blur(10px); }
|
| 351 |
.chat-file-card .icon { font-size: 24px; background: rgba(0,0,0,0.1); width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); }
|
| 352 |
.chat-file-card .name { font-size: 14px; font-weight: 600; color: var(--brand-white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
@@ -366,15 +385,29 @@
|
|
| 366 |
.btn-bot-action.action-tts { color: var(--brand-green-dark); border-color: rgba(19, 136, 8, 0.25); background: var(--brand-green-light); }
|
| 367 |
.btn-bot-action.action-tts:hover { background: rgba(19, 136, 8, 0.2); }
|
| 368 |
|
| 369 |
-
/* Markdown
|
| 370 |
.chat-bubble p { margin-bottom: 16px; }
|
| 371 |
.chat-bubble p:last-child { margin-bottom: 0; }
|
| 372 |
.chat-bubble strong { font-weight: 800; color: inherit; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
.chat-bubble code { font-family: var(--font-mono); background: rgba(0,0,0,0.06); padding: 4px 8px; border-radius: 6px; font-size: 0.85em; color: #d946ef; font-weight: 600; border: 1px solid rgba(0,0,0,0.05); }
|
| 374 |
.user-msg .chat-bubble code { background: rgba(255,255,255,0.2); color: var(--brand-white); border-color: rgba(255,255,255,0.1); }
|
|
|
|
| 375 |
.chat-bubble img { max-width: 100%; border-radius: var(--radius-md); margin: 16px 0; box-shadow: var(--shadow-lg); border: 1px solid var(--border-light); display: block; }
|
| 376 |
-
|
| 377 |
-
|
|
|
|
|
|
|
| 378 |
|
| 379 |
/* Streaming Loading UI Component */
|
| 380 |
.streaming-code-container { background: #1e1e2e; border-radius: var(--radius-md); border: 1px solid #313244; overflow: hidden; margin: 20px 0; box-shadow: var(--shadow-lg); }
|
|
@@ -398,12 +431,12 @@
|
|
| 398 |
I. BOTTOM INPUT DOCK & TOOLS
|
| 399 |
--------------------------------------------------------------------------------------- */
|
| 400 |
.input-dock { flex-shrink: 0; padding: 0 40px 32px; background: transparent; position: relative; z-index: var(--z-dock); }
|
| 401 |
-
.input-master-container { display: flex; flex-direction: column; background: var(--bg-glass); backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px); border: 1px solid var(--border-light); border-radius: var(--radius-xl); box-shadow: var(--shadow-xl); transition: var(--ease-smooth); }
|
| 402 |
.input-master-container:focus-within { border-color: var(--brand-saffron); box-shadow: 0 20px 40px -5px rgba(255,153,51,0.15), 0 0 0 4px var(--brand-saffron-light); }
|
| 403 |
|
| 404 |
-
/* Upload Preview Header
|
| 405 |
-
.upload-preview-bar { display: none; padding: 20px 28px 0; align-items: center; gap: 16px; }
|
| 406 |
-
.upload-preview-bar.visible { display: flex; animation: fadeIn 0.3s ease; }
|
| 407 |
.upload-thumb { width: 52px; height: 52px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border-light); display: flex; justify-content: center; align-items: center; background: var(--bg-tertiary); font-size: 24px; box-shadow: var(--shadow-sm); }
|
| 408 |
.upload-meta { flex: 1; }
|
| 409 |
.upload-filename { font-size: 14px; font-weight: 700; color: var(--text-primary); max-width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
@@ -418,9 +451,13 @@
|
|
| 418 |
.main-textarea { flex: 1; border: none; outline: none; background: transparent; font-family: var(--font-sans); font-size: 16px; color: var(--text-primary); line-height: 1.5; resize: none; max-height: 200px; min-height: 24px; padding: 12px 0; margin: 0 8px; font-weight: 500; }
|
| 419 |
.main-textarea::placeholder { color: var(--text-tertiary); font-weight: 400; }
|
| 420 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
/* Attachment Menu Dropdown */
|
| 422 |
.attachment-module { position: relative; }
|
| 423 |
-
.btn-tool-icon { width: 44px; height: 44px; border-radius: var(--radius-md); background: transparent; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); transition: var(--ease-fast); }
|
| 424 |
.btn-tool-icon:hover { background: var(--bg-tertiary); color: var(--text-primary); }
|
| 425 |
.btn-tool-icon svg { width: 24px; height: 24px; stroke-width: 2; }
|
| 426 |
|
|
@@ -438,15 +475,19 @@
|
|
| 438 |
.language-toggle-chip.hindi-active { background: var(--brand-green-light); color: var(--brand-green-dark); border-color: var(--brand-green); }
|
| 439 |
.language-toggle-chip svg { width: 16px; height: 16px; }
|
| 440 |
|
| 441 |
-
.btn-mic-tool
|
| 442 |
-
|
|
|
|
|
|
|
| 443 |
|
| 444 |
/* Master Send Button */
|
| 445 |
-
.btn-master-send { width: 48px; height: 48px; border-radius: 16px; border: none; background: linear-gradient(135deg, var(--brand-saffron) 0%, var(--brand-green) 100%); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--ease-spring); box-shadow: 0 6px 20px rgba(19, 136, 8, 0.25); }
|
| 446 |
-
.btn-master-send
|
|
|
|
| 447 |
.btn-master-send:hover { transform: translateY(-3px); box-shadow: 0 10px 25px rgba(255, 153, 51, 0.4); }
|
|
|
|
| 448 |
.btn-master-send:hover svg { transform: translate(2px, -2px); }
|
| 449 |
-
.btn-master-send:disabled { opacity: 0.
|
| 450 |
|
| 451 |
.bottom-branding-text { text-align: center; font-size: 12px; color: var(--text-tertiary); margin-top: 16px; font-weight: 600; letter-spacing: 0.5px; }
|
| 452 |
|
|
@@ -467,12 +508,16 @@
|
|
| 467 |
.btn-workspace-tool.close:hover { color: #e06c75; background: rgba(224, 108, 117, 0.1); }
|
| 468 |
|
| 469 |
.workspace-editor-surface { flex: 1; overflow-y: auto; background: #282c34; padding: 24px; position: relative; }
|
| 470 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 471 |
|
| 472 |
/* ---------------------------------------------------------------------------------------
|
| 473 |
K. LIVE MODE FULLSCREEN OVERLAY
|
| 474 |
--------------------------------------------------------------------------------------- */
|
| 475 |
-
.live-mode-viewport { position: fixed; inset: 0; background: rgba(255, 255, 255, 0.98); z-index: var(--z-live); display: flex; flex-direction: column; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity var(--ease-smooth); }
|
| 476 |
.live-mode-viewport.active { opacity: 1; pointer-events: auto; }
|
| 477 |
.btn-close-live { position: absolute; top: 40px; right: 40px; background: rgba(0,0,0,0.04); border: none; width: 56px; height: 56px; border-radius: 50%; font-size: 24px; cursor: pointer; transition: var(--ease-spring); display: flex; align-items: center; justify-content: center; color: var(--text-secondary); }
|
| 478 |
.btn-close-live:hover { background: rgba(239, 68, 68, 0.15); color: var(--text-error); transform: rotate(90deg); }
|
|
@@ -480,22 +525,22 @@
|
|
| 480 |
.pulsing-orb-wrapper { position: relative; width: 250px; height: 250px; display: flex; align-items: center; justify-content: center; margin-bottom: 50px; }
|
| 481 |
.energy-core { width: 120px; height: 120px; border-radius: 50%; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); box-shadow: 0 0 50px rgba(19, 136, 8, 0.5); position: relative; z-index: 2; transition: transform 0.15s ease-out; }
|
| 482 |
.energy-core.speaking { animation: coreSpeak 0.5s infinite alternate; }
|
| 483 |
-
@keyframes coreSpeak { 0% { transform: scale(1); box-shadow: 0 0 30px rgba(19,136,8,0.4); } 100% { transform: scale(1.
|
| 484 |
|
| 485 |
.energy-ring { position: absolute; inset: 0; border: 3px solid var(--brand-saffron); border-radius: 50%; opacity: 0; animation: energyRipple 2.5s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
|
| 486 |
.energy-ring:nth-child(2) { animation-delay: 0.8s; border-color: var(--brand-green); }
|
| 487 |
.energy-ring:nth-child(3) { animation-delay: 1.6s; border-color: var(--text-primary); }
|
| 488 |
@keyframes energyRipple { 0% { transform: scale(0.4); opacity: 1; border-width: 6px; } 100% { transform: scale(1.6); opacity: 0; border-width: 1px; } }
|
| 489 |
|
| 490 |
-
.live-feedback-status { font-size: 28px; font-weight: 900; color: var(--text-primary); margin-bottom: 16px; font-family: var(--font-sans); letter-spacing: -0.5px; }
|
| 491 |
-
.live-feedback-text { font-size: 18px; color: var(--text-secondary); max-width: 700px; text-align: center; line-height: 1.6; min-height: 60px; font-weight: 500; padding: 0 20px; }
|
| 492 |
|
| 493 |
.live-control-deck { display: flex; gap: 24px; margin-top: 50px; }
|
| 494 |
.btn-live-deck { width: 80px; height: 80px; border-radius: 50%; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--shadow-lg); transition: var(--ease-spring); }
|
| 495 |
.btn-live-deck.mic-control { background: var(--bg-primary); border: 3px solid var(--border-light); color: var(--text-primary); }
|
| 496 |
.btn-live-deck.mic-control:hover { transform: translateY(-6px); border-color: var(--brand-saffron); color: var(--brand-saffron); box-shadow: var(--shadow-xl); }
|
| 497 |
.btn-live-deck.mic-control.is-recording { background: var(--text-error); color: white; border-color: var(--text-error); animation: pulseMicLive 1.2s infinite; }
|
| 498 |
-
@keyframes pulseMicLive { 0% { box-shadow: 0 0 0 0 rgba(239,68,68,0.6); } 70% { box-shadow: 0 0 0
|
| 499 |
|
| 500 |
/* Scroll Trigger Button */
|
| 501 |
.btn-scroll-trigger { position: absolute; bottom: 150px; left: 50%; width: 48px; height: 48px; border-radius: 50%; background: var(--bg-glass); backdrop-filter: blur(15px); border: 1px solid var(--border-light); box-shadow: var(--shadow-lg); display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); opacity: 0; pointer-events: none; transform: translateX(-50%) translateY(20px); transition: var(--ease-smooth); z-index: 20; }
|
|
@@ -508,7 +553,7 @@
|
|
| 508 |
.secure-modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); backdrop-filter: blur(10px); z-index: var(--z-modal); display: none; justify-content: center; align-items: center; animation: fadeIn 0.3s ease; }
|
| 509 |
.secure-modal-surface { background: var(--bg-primary); padding: 44px; border-radius: var(--radius-xl); width: 90%; max-width: 460px; box-shadow: var(--shadow-xl); position: relative; border: 1px solid rgba(255,255,255,0.4); }
|
| 510 |
.btn-modal-dismiss { position: absolute; top: 24px; right: 24px; background: var(--bg-secondary); border: none; width: 36px; height: 36px; border-radius: 50%; font-size: 18px; color: var(--text-secondary); cursor: pointer; transition: 0.2s; display: flex; align-items: center; justify-content: center; }
|
| 511 |
-
.btn-modal-dismiss:hover { background: #fee2e2; color: var(--text-error); }
|
| 512 |
.modal-brand-header { text-align: center; margin-bottom: 36px; font-family: var(--font-sans); font-size: 28px; font-weight: 900; color: var(--text-primary); letter-spacing: -0.5px; }
|
| 513 |
|
| 514 |
.auth-tab-switch { display: flex; background: var(--bg-secondary); padding: 6px; border-radius: var(--radius-md); margin-bottom: 28px; }
|
|
@@ -562,11 +607,20 @@
|
|
| 562 |
|
| 563 |
.input-tools-row { flex-wrap: wrap; }
|
| 564 |
.main-textarea { min-width: 100%; order: -1; margin-bottom: 10px; }
|
|
|
|
| 565 |
}
|
| 566 |
</style>
|
| 567 |
</head>
|
| 568 |
<body>
|
| 569 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 570 |
<!-- =========================================================================================
|
| 571 |
DOM STRUCTURE: NOTIFICATIONS
|
| 572 |
========================================================================================= -->
|
|
@@ -633,7 +687,7 @@
|
|
| 633 |
<img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo">
|
| 634 |
<div class="brand-typography">
|
| 635 |
<span class="brand-name">ARJUN 2.O</span>
|
| 636 |
-
<span class="brand-edition">
|
| 637 |
</div>
|
| 638 |
</a>
|
| 639 |
<button class="btn-mobile-close" id="btnSidebarClose">
|
|
@@ -671,7 +725,7 @@
|
|
| 671 |
<!-- Wipe Memory Button -->
|
| 672 |
<button class="btn-erase-memory" id="btnClearMemory">
|
| 673 |
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
|
| 674 |
-
Wipe
|
| 675 |
</button>
|
| 676 |
</div>
|
| 677 |
</aside>
|
|
@@ -685,9 +739,9 @@
|
|
| 685 |
<button class="btn-hamburger" id="btnSidebarOpen">
|
| 686 |
<svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2.5" fill="none"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
| 687 |
</button>
|
| 688 |
-
<div class="system-status-pill">
|
| 689 |
<div class="pulse-dot"></div>
|
| 690 |
-
System Online
|
| 691 |
</div>
|
| 692 |
</div>
|
| 693 |
|
|
@@ -742,7 +796,7 @@
|
|
| 742 |
|
| 743 |
<!-- File Preview Component -->
|
| 744 |
<div class="upload-preview-bar" id="filePreviewBar">
|
| 745 |
-
<img class="upload-thumb" id="previewImgThumb" src="" style="display:none;">
|
| 746 |
<div class="upload-thumb" id="previewDocIcon" style="display:none;">📄</div>
|
| 747 |
<div class="upload-meta">
|
| 748 |
<div class="upload-filename" id="previewFileName">document.pdf</div>
|
|
@@ -751,7 +805,7 @@
|
|
| 751 |
File ready to be transmitted
|
| 752 |
</div>
|
| 753 |
</div>
|
| 754 |
-
<button class="btn-discard-upload" id="btnDiscardFile">
|
| 755 |
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
| 756 |
</button>
|
| 757 |
</div>
|
|
@@ -803,7 +857,11 @@
|
|
| 803 |
</button>
|
| 804 |
|
| 805 |
<!-- Expanding Text Area -->
|
| 806 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
| 807 |
|
| 808 |
<!-- Master Send Button -->
|
| 809 |
<button class="btn-master-send" id="masterSendBtn">
|
|
@@ -833,7 +891,10 @@
|
|
| 833 |
</div>
|
| 834 |
</div>
|
| 835 |
<div class="workspace-editor-surface">
|
| 836 |
-
<
|
|
|
|
|
|
|
|
|
|
| 837 |
</div>
|
| 838 |
</aside>
|
| 839 |
</div>
|
|
@@ -883,11 +944,8 @@
|
|
| 883 |
</div>
|
| 884 |
|
| 885 |
<!-- =========================================================================================
|
| 886 |
-
|
| 887 |
========================================================================================= -->
|
| 888 |
-
<!-- CRITICAL FIX: Removed type="module" from the script block to ensure all elements
|
| 889 |
-
and functions remain within the window/global scope and are easily bound.
|
| 890 |
-
Gradio Client will be dynamically imported. -->
|
| 891 |
<script>
|
| 892 |
// =======================================================================================
|
| 893 |
// 1. CONFIGURATION & STATE MANAGEMENT
|
|
@@ -897,6 +955,7 @@
|
|
| 897 |
LOGO_URL: "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png",
|
| 898 |
PDF_WORKER: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js',
|
| 899 |
API_ENDPOINT: '/api/chat', // Uses relative path to connect to Flask App.py
|
|
|
|
| 900 |
};
|
| 901 |
|
| 902 |
// Initialize PDF worker
|
|
@@ -914,17 +973,19 @@
|
|
| 914 |
isStreamingActive: false,
|
| 915 |
isLiveModeEngaged: false,
|
| 916 |
gradioClient: null,
|
| 917 |
-
isMicRecording: false
|
|
|
|
| 918 |
};
|
| 919 |
|
| 920 |
-
// Initialize Gradio Client Dynamically
|
| 921 |
window.initGradioEngine = async function() {
|
| 922 |
try {
|
| 923 |
const module = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js");
|
| 924 |
AppState.gradioClient = await module.Client.connect("Vedika66/Edge-TTS");
|
| 925 |
console.log("Neural TTS Engine connected successfully to Gradio.");
|
| 926 |
} catch(err) {
|
| 927 |
-
console.
|
|
|
|
| 928 |
}
|
| 929 |
};
|
| 930 |
window.initGradioEngine();
|
|
@@ -933,38 +994,37 @@
|
|
| 933 |
// 2. UTILITY ENGINE (Robust Copy, Notifications, and Text Cleaners)
|
| 934 |
// =======================================================================================
|
| 935 |
const Utils = {
|
| 936 |
-
showToast: (message) => {
|
| 937 |
const container = document.getElementById('toast-container');
|
| 938 |
const toast = document.createElement('div');
|
| 939 |
-
toast.className =
|
| 940 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 941 |
container.appendChild(toast);
|
| 942 |
-
setTimeout(() => toast.remove(),
|
| 943 |
},
|
| 944 |
|
| 945 |
escapeHTML: (str) => {
|
|
|
|
| 946 |
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
| 947 |
},
|
| 948 |
|
| 949 |
-
// ✨
|
| 950 |
cleanTextForVoice: (rawText) => {
|
| 951 |
if(!rawText) return "";
|
| 952 |
let cleaned = rawText;
|
| 953 |
|
| 954 |
-
//
|
| 955 |
-
cleaned = cleaned.replace(/```[\s\S]*?```/g, '');
|
| 956 |
-
|
| 957 |
-
cleaned = cleaned.replace(/
|
| 958 |
-
|
| 959 |
-
cleaned = cleaned.replace(/
|
| 960 |
-
|
| 961 |
-
cleaned = cleaned.replace(/
|
| 962 |
-
// 5. Remove Think Tags (<think>...</think>)
|
| 963 |
-
cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, '');
|
| 964 |
-
// 6. Remove URLs/HTTP links explicitly
|
| 965 |
-
cleaned = cleaned.replace(/https?:\/\/[^\s]+/g, '');
|
| 966 |
-
// 7. Remove stray Markdown formatting symbols
|
| 967 |
-
cleaned = cleaned.replace(/[*_~#]/g, '');
|
| 968 |
|
| 969 |
return cleaned.trim();
|
| 970 |
},
|
|
@@ -974,6 +1034,7 @@
|
|
| 974 |
const textArea = document.createElement("textarea");
|
| 975 |
textArea.value = text;
|
| 976 |
textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.position = "fixed";
|
|
|
|
| 977 |
document.body.appendChild(textArea);
|
| 978 |
textArea.focus(); textArea.select();
|
| 979 |
|
|
@@ -981,13 +1042,13 @@
|
|
| 981 |
const successful = document.execCommand('copy');
|
| 982 |
if (successful) {
|
| 983 |
const originalHTML = btnElement.innerHTML;
|
| 984 |
-
btnElement.innerHTML = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied
|
| 985 |
setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500);
|
| 986 |
} else {
|
| 987 |
-
Utils.showToast("Clipboard access denied by browser.");
|
| 988 |
}
|
| 989 |
} catch (err) {
|
| 990 |
-
Utils.showToast("System error copying text.");
|
| 991 |
}
|
| 992 |
document.body.removeChild(textArea);
|
| 993 |
},
|
|
@@ -999,7 +1060,7 @@
|
|
| 999 |
}
|
| 1000 |
navigator.clipboard.writeText(textToCopy).then(() => {
|
| 1001 |
const originalHTML = btnElement.innerHTML;
|
| 1002 |
-
btnElement.innerHTML = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied
|
| 1003 |
setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500);
|
| 1004 |
}).catch((err) => {
|
| 1005 |
console.warn('Modern API failed, utilizing fallback...', err);
|
|
@@ -1009,7 +1070,7 @@
|
|
| 1009 |
};
|
| 1010 |
|
| 1011 |
// =======================================================================================
|
| 1012 |
-
// 3. AUTHENTICATION & SYNC MANAGER
|
| 1013 |
// =======================================================================================
|
| 1014 |
const AuthManager = {
|
| 1015 |
initUI: () => {
|
|
@@ -1048,8 +1109,9 @@
|
|
| 1048 |
document.getElementById(nextId).classList.add('active');
|
| 1049 |
},
|
| 1050 |
|
| 1051 |
-
// ✨ FIXED GAS SYNC LOGIC
|
| 1052 |
process: async (actionType) => {
|
|
|
|
|
|
|
| 1053 |
const payload = { action: actionType };
|
| 1054 |
let buttonId = '';
|
| 1055 |
|
|
@@ -1057,18 +1119,18 @@
|
|
| 1057 |
payload.name = document.getElementById('authRegName').value.trim();
|
| 1058 |
payload.email = document.getElementById('authRegEmail').value.trim();
|
| 1059 |
payload.phone = "0000000000"; payload.organization = "Arjun Base";
|
| 1060 |
-
if (!payload.email || !payload.name) return Utils.showToast("Identification details required.");
|
| 1061 |
buttonId = 'btn-fire-reg-otp';
|
| 1062 |
}
|
| 1063 |
else if (actionType === 'login_send_otp') {
|
| 1064 |
payload.email = document.getElementById('authLogEmail').value.trim();
|
| 1065 |
-
if (!payload.email) return Utils.showToast("Email required for validation.");
|
| 1066 |
buttonId = 'btn-fire-log-otp';
|
| 1067 |
}
|
| 1068 |
else if (actionType === 'register_verify' || actionType === 'login_verify') {
|
| 1069 |
payload.email = document.getElementById(actionType === 'register_verify' ? 'authRegEmail' : 'authLogEmail').value.trim();
|
| 1070 |
payload.otp = document.getElementById(actionType === 'register_verify' ? 'authRegOTP' : 'authLogOTP').value.trim();
|
| 1071 |
-
if (!payload.otp) return Utils.showToast("Security token required.");
|
| 1072 |
buttonId = actionType === 'register_verify' ? 'btn-fire-reg-verify' : 'btn-fire-log-verify';
|
| 1073 |
}
|
| 1074 |
|
|
@@ -1078,7 +1140,6 @@
|
|
| 1078 |
triggerBtn.innerText = 'Establishing Connection...';
|
| 1079 |
|
| 1080 |
try {
|
| 1081 |
-
// mode: 'cors' handles redirect issues from GAS effectively
|
| 1082 |
const response = await fetch(Config.GAS_URL, {
|
| 1083 |
method: 'POST',
|
| 1084 |
mode: 'cors',
|
|
@@ -1089,7 +1150,7 @@
|
|
| 1089 |
const result = await response.json();
|
| 1090 |
|
| 1091 |
if (result.status === 'success') {
|
| 1092 |
-
Utils.showToast(result.message);
|
| 1093 |
if (actionType === 'register_send_otp') AuthManager.switchPhase('auth-reg-phase-1', 'auth-reg-phase-2');
|
| 1094 |
else if (actionType === 'login_send_otp') AuthManager.switchPhase('auth-log-phase-1', 'auth-log-phase-2');
|
| 1095 |
else if (actionType === 'register_verify' || actionType === 'login_verify') {
|
|
@@ -1100,11 +1161,11 @@
|
|
| 1100 |
ChatEngine.syncHistoryFromCloud();
|
| 1101 |
}
|
| 1102 |
} else {
|
| 1103 |
-
Utils.showToast(result.message);
|
| 1104 |
}
|
| 1105 |
} catch (error) {
|
| 1106 |
console.error("Auth Network Error: ", error);
|
| 1107 |
-
Utils.showToast("Encrypted gateway error. Check network.");
|
| 1108 |
}
|
| 1109 |
|
| 1110 |
triggerBtn.disabled = false;
|
|
@@ -1153,22 +1214,48 @@
|
|
| 1153 |
|
| 1154 |
toggleAttachMenu: () => {
|
| 1155 |
document.getElementById('attachDropdownMenu').classList.toggle('visible');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1156 |
}
|
| 1157 |
};
|
| 1158 |
|
| 1159 |
// =======================================================================================
|
| 1160 |
-
// 5. SPEECH & TTS ENGINE (
|
| 1161 |
// =======================================================================================
|
| 1162 |
const VoiceEngine = {
|
| 1163 |
recognition: null,
|
| 1164 |
currentLanguage: 'en-IN',
|
| 1165 |
-
|
|
|
|
|
|
|
| 1166 |
init: () => {
|
| 1167 |
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
| 1168 |
-
if (!SR)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1169 |
|
| 1170 |
VoiceEngine.recognition = new SR();
|
| 1171 |
-
//
|
| 1172 |
VoiceEngine.recognition.continuous = true;
|
| 1173 |
VoiceEngine.recognition.interimResults = true;
|
| 1174 |
VoiceEngine.recognition.lang = VoiceEngine.currentLanguage;
|
|
@@ -1176,7 +1263,12 @@
|
|
| 1176 |
VoiceEngine.recognition.onstart = () => {
|
| 1177 |
AppState.isMicRecording = true;
|
| 1178 |
document.getElementById('masterVoiceBtn').classList.add('active-listening');
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1179 |
if (AppState.isLiveModeEngaged) {
|
|
|
|
| 1180 |
document.getElementById('liveMicBtn').classList.add('is-recording');
|
| 1181 |
document.getElementById('liveStatusText').innerText = "Listening...";
|
| 1182 |
document.getElementById('liveOrb').classList.remove('speaking');
|
|
@@ -1185,34 +1277,56 @@
|
|
| 1185 |
};
|
| 1186 |
|
| 1187 |
VoiceEngine.recognition.onresult = (event) => {
|
| 1188 |
-
|
| 1189 |
-
|
|
|
|
|
|
|
|
|
|
| 1190 |
|
| 1191 |
-
|
|
|
|
|
|
|
| 1192 |
if (event.results[i].isFinal) {
|
| 1193 |
-
|
| 1194 |
} else {
|
| 1195 |
-
|
| 1196 |
}
|
| 1197 |
}
|
| 1198 |
|
| 1199 |
-
|
| 1200 |
-
|
| 1201 |
-
|
| 1202 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1203 |
|
| 1204 |
-
|
| 1205 |
-
|
| 1206 |
-
|
| 1207 |
-
|
| 1208 |
-
|
| 1209 |
-
|
| 1210 |
-
|
| 1211 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1212 |
} else {
|
| 1213 |
-
|
| 1214 |
-
inputField.value = (inputField.value + ' ' + formattedText + ' ').trim();
|
| 1215 |
-
UIManager.autoGrowTextArea(inputField);
|
| 1216 |
}
|
| 1217 |
}
|
| 1218 |
};
|
|
@@ -1220,20 +1334,31 @@
|
|
| 1220 |
VoiceEngine.recognition.onend = () => {
|
| 1221 |
AppState.isMicRecording = false;
|
| 1222 |
document.getElementById('masterVoiceBtn').classList.remove('active-listening');
|
|
|
|
| 1223 |
|
| 1224 |
-
if (AppState.isLiveModeEngaged
|
| 1225 |
document.getElementById('liveMicBtn').classList.remove('is-recording');
|
| 1226 |
-
document.getElementById('liveStatusText').innerText = "Tap mic to interact";
|
| 1227 |
document.getElementById('liveOrb').style.transform = "scale(1)";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1228 |
}
|
| 1229 |
};
|
| 1230 |
|
| 1231 |
VoiceEngine.recognition.onerror = (event) => {
|
| 1232 |
AppState.isMicRecording = false;
|
| 1233 |
-
if(event.error !== 'no-speech') {
|
| 1234 |
-
Utils.showToast("Voice module error: " + event.error);
|
| 1235 |
}
|
| 1236 |
document.getElementById('masterVoiceBtn').classList.remove('active-listening');
|
|
|
|
|
|
|
| 1237 |
if (AppState.isLiveModeEngaged) {
|
| 1238 |
document.getElementById('liveMicBtn').classList.remove('is-recording');
|
| 1239 |
document.getElementById('liveStatusText').innerText = "Audio Failure. Retry.";
|
|
@@ -1249,25 +1374,26 @@
|
|
| 1249 |
VoiceEngine.currentLanguage = 'hi-IN';
|
| 1250 |
badge.innerText = 'हि';
|
| 1251 |
btn.classList.add('hindi-active');
|
| 1252 |
-
Utils.showToast("Listening Protocol: Hindi (देवनागरी)");
|
| 1253 |
} else {
|
| 1254 |
VoiceEngine.currentLanguage = 'en-IN';
|
| 1255 |
badge.innerText = 'EN';
|
| 1256 |
btn.classList.remove('hindi-active');
|
| 1257 |
-
Utils.showToast("Listening Protocol: English");
|
| 1258 |
}
|
| 1259 |
|
| 1260 |
if (VoiceEngine.recognition) {
|
| 1261 |
VoiceEngine.recognition.lang = VoiceEngine.currentLanguage;
|
| 1262 |
if(AppState.isMicRecording) {
|
| 1263 |
VoiceEngine.recognition.stop();
|
| 1264 |
-
|
|
|
|
| 1265 |
}
|
| 1266 |
}
|
| 1267 |
},
|
| 1268 |
|
| 1269 |
toggleDictation: () => {
|
| 1270 |
-
if (!VoiceEngine.recognition) return Utils.showToast("Hardware dictation unsupported.");
|
| 1271 |
try {
|
| 1272 |
if(AppState.isMicRecording) {
|
| 1273 |
VoiceEngine.recognition.stop();
|
|
@@ -1277,13 +1403,12 @@
|
|
| 1277 |
} catch (e) { console.error("Mic Start Error", e); }
|
| 1278 |
},
|
| 1279 |
|
| 1280 |
-
// ✨ GRADIO TTS INTEGRATION WITH SMART CLEANER
|
| 1281 |
executeTTS: async (rawText) => {
|
| 1282 |
// Apply smart text cleaner so AI doesn't read code or image links
|
| 1283 |
const cleanSpeechText = Utils.cleanTextForVoice(rawText);
|
| 1284 |
|
| 1285 |
if(!cleanSpeechText.trim()) {
|
| 1286 |
-
Utils.showToast("No readable text found.");
|
| 1287 |
return;
|
| 1288 |
}
|
| 1289 |
|
|
@@ -1294,7 +1419,6 @@
|
|
| 1294 |
|
| 1295 |
try {
|
| 1296 |
if(!AppState.gradioClient) {
|
| 1297 |
-
// Fallback dynamically import if not connected
|
| 1298 |
const module = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js");
|
| 1299 |
AppState.gradioClient = await module.Client.connect("Vedika66/Edge-TTS");
|
| 1300 |
}
|
|
@@ -1304,8 +1428,8 @@
|
|
| 1304 |
voice_type: "प्रभात (भारी और प्राकृतिक आवाज़ - हिंग्लिश के लिए)"
|
| 1305 |
});
|
| 1306 |
|
| 1307 |
-
if (result.data && result.data
|
| 1308 |
-
const audioUrl = typeof result.data
|
| 1309 |
const audio = new Audio(audioUrl);
|
| 1310 |
|
| 1311 |
audio.onended = () => {
|
|
@@ -1320,7 +1444,7 @@
|
|
| 1320 |
throw new Error("No audio payload returned.");
|
| 1321 |
}
|
| 1322 |
} catch (error) {
|
| 1323 |
-
Utils.showToast("Voice Neural Matrix is currently overloaded.");
|
| 1324 |
console.error("Gradio TTS Error:", error);
|
| 1325 |
if (AppState.isLiveModeEngaged) {
|
| 1326 |
document.getElementById('liveOrb').classList.remove('speaking');
|
|
@@ -1339,7 +1463,7 @@
|
|
| 1339 |
if (VoiceEngine.recognition && !AppState.isMicRecording) {
|
| 1340 |
setTimeout(() => {
|
| 1341 |
try { VoiceEngine.recognition.start(); } catch(e){}
|
| 1342 |
-
},
|
| 1343 |
}
|
| 1344 |
},
|
| 1345 |
terminate: () => {
|
|
@@ -1352,7 +1476,7 @@
|
|
| 1352 |
};
|
| 1353 |
|
| 1354 |
// =======================================================================================
|
| 1355 |
-
// 6. FILE & DOCUMENT PROCESSOR (
|
| 1356 |
// =======================================================================================
|
| 1357 |
const FileProcessor = {
|
| 1358 |
triggerFileSelector: (type) => {
|
|
@@ -1361,17 +1485,25 @@
|
|
| 1361 |
},
|
| 1362 |
|
| 1363 |
processFile: async (inputElement, typeContext) => {
|
| 1364 |
-
const file = inputElement.files
|
| 1365 |
if (!file) return;
|
| 1366 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1367 |
const fName = file.name.toLowerCase();
|
| 1368 |
const mime = file.type;
|
| 1369 |
|
| 1370 |
-
if (typeContext === 'image' && !mime.startsWith('image/')) { Utils.showToast("Visual media required."); inputElement.value=''; return; }
|
| 1371 |
-
if (typeContext === 'video' && !mime.startsWith('video/')) { Utils.showToast("Video media required."); inputElement.value=''; return; }
|
| 1372 |
-
if (typeContext === 'audio' && !mime.startsWith('audio/')) { Utils.showToast("Audio media required."); inputElement.value=''; return; }
|
| 1373 |
|
| 1374 |
-
Utils.showToast(`
|
| 1375 |
|
| 1376 |
const thumbImg = document.getElementById('previewImgThumb');
|
| 1377 |
const docIcon = document.getElementById('previewDocIcon');
|
|
@@ -1385,7 +1517,7 @@
|
|
| 1385 |
|
| 1386 |
if (typeContext === 'image') {
|
| 1387 |
fileReader.onload = (e) => {
|
| 1388 |
-
AppState.pendingAttachment = { type: 'image', data: e.target.result.split(',')
|
| 1389 |
thumbImg.src = e.target.result;
|
| 1390 |
thumbImg.style.display = 'flex';
|
| 1391 |
};
|
|
@@ -1393,7 +1525,7 @@
|
|
| 1393 |
}
|
| 1394 |
else if (typeContext === 'video' || typeContext === 'audio') {
|
| 1395 |
fileReader.onload = (e) => {
|
| 1396 |
-
AppState.pendingAttachment = { type: typeContext, data: e.target.result.split(',')
|
| 1397 |
docIcon.innerText = typeContext === 'video' ? '🎥' : '🎵';
|
| 1398 |
docIcon.style.display = 'flex';
|
| 1399 |
};
|
|
@@ -1415,9 +1547,9 @@
|
|
| 1415 |
extractedText += content.items.map(s => s.str).join(' ') + "\n";
|
| 1416 |
}
|
| 1417 |
AppState.pendingAttachment = { type: 'text', data: extractedText.trim() || "No readable text found.", name: file.name };
|
| 1418 |
-
Utils.showToast("
|
| 1419 |
} catch (e) {
|
| 1420 |
-
Utils.showToast("PDF parsing
|
| 1421 |
FileProcessor.discardFile();
|
| 1422 |
}
|
| 1423 |
};
|
|
@@ -1429,9 +1561,9 @@
|
|
| 1429 |
const buffer = this.result;
|
| 1430 |
const resultData = await mammoth.extractRawText({ arrayBuffer: buffer });
|
| 1431 |
AppState.pendingAttachment = { type: 'text', data: resultData.value.trim() || "No readable text found.", name: file.name };
|
| 1432 |
-
Utils.showToast("
|
| 1433 |
} catch(e) {
|
| 1434 |
-
Utils.showToast("Document parsing
|
| 1435 |
FileProcessor.discardFile();
|
| 1436 |
}
|
| 1437 |
};
|
|
@@ -1440,7 +1572,7 @@
|
|
| 1440 |
else {
|
| 1441 |
fileReader.onload = (e) => {
|
| 1442 |
AppState.pendingAttachment = { type: 'text', data: e.target.result, name: file.name };
|
| 1443 |
-
Utils.showToast("
|
| 1444 |
};
|
| 1445 |
fileReader.readAsText(file);
|
| 1446 |
}
|
|
@@ -1451,23 +1583,31 @@
|
|
| 1451 |
discardFile: () => {
|
| 1452 |
AppState.pendingAttachment = null;
|
| 1453 |
document.getElementById('filePreviewBar').classList.remove('visible');
|
|
|
|
| 1454 |
}
|
| 1455 |
};
|
| 1456 |
|
| 1457 |
// =======================================================================================
|
| 1458 |
-
// 7. WORKSPACE ENGINE & MARKDOWN RENDERING
|
| 1459 |
// =======================================================================================
|
| 1460 |
const WorkspaceEngine = {
|
| 1461 |
launch: (codeBlockId) => {
|
| 1462 |
AppState.currentWorkspaceId = codeBlockId;
|
| 1463 |
const codeData = AppState.generatedCodeBlocks[codeBlockId];
|
| 1464 |
|
| 1465 |
-
document.getElementById('wsTitleText').innerText = codeData.lang ? `Generated ${codeData.lang.toUpperCase()}` : "Compiled
|
| 1466 |
const blockDisplay = document.getElementById('workspaceCodeBlock');
|
| 1467 |
blockDisplay.className = `hljs language-${codeData.lang || 'plaintext'}`;
|
| 1468 |
blockDisplay.textContent = codeData.code;
|
| 1469 |
|
| 1470 |
hljs.highlightElement(blockDisplay);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1471 |
document.getElementById('workspacePanel').classList.add('expanded');
|
| 1472 |
|
| 1473 |
if (window.innerWidth <= 1024) {
|
|
@@ -1485,17 +1625,18 @@
|
|
| 1485 |
}
|
| 1486 |
};
|
| 1487 |
|
|
|
|
| 1488 |
const MD_Renderer = new marked.Renderer();
|
| 1489 |
MD_Renderer.code = function(token) {
|
| 1490 |
const rawCode = typeof token === 'string' ? token : (token.text || '');
|
| 1491 |
-
const langClass = typeof token === 'string' ? arguments
|
| 1492 |
|
| 1493 |
if (AppState.isStreamingActive) {
|
| 1494 |
return `
|
| 1495 |
<div class="streaming-code-container">
|
| 1496 |
<div class="streaming-code-header">
|
| 1497 |
<div class="loading-ring"></div>
|
| 1498 |
-
Generating Architecture...
|
| 1499 |
</div>
|
| 1500 |
<pre><code class="language-${langClass}">${Utils.escapeHTML(rawCode)}</code></pre>
|
| 1501 |
</div>`;
|
|
@@ -1512,15 +1653,36 @@
|
|
| 1512 |
</div>
|
| 1513 |
<div class="ws-card-details">
|
| 1514 |
<h4>Code Module Compiled</h4>
|
| 1515 |
-
<p>${langClass || 'Syntax
|
| 1516 |
</div>
|
| 1517 |
</div>
|
| 1518 |
<button class="btn-launch-workspace btn-launch-ws" data-id="${internalId}">Open Workspace</button>
|
| 1519 |
</div>`;
|
| 1520 |
};
|
| 1521 |
-
marked.use({ renderer: MD_Renderer });
|
| 1522 |
|
| 1523 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1524 |
document.getElementById('msgs').addEventListener('click', function(e) {
|
| 1525 |
if (e.target && e.target.classList.contains('btn-launch-ws')) {
|
| 1526 |
const blockId = e.target.getAttribute('data-id');
|
|
@@ -1545,8 +1707,10 @@
|
|
| 1545 |
},
|
| 1546 |
|
| 1547 |
clearMemory: async () => {
|
|
|
|
|
|
|
| 1548 |
if (AppState.currentUser) {
|
| 1549 |
-
Utils.showToast("Purging neural link memory...");
|
| 1550 |
try {
|
| 1551 |
await fetch(Config.GAS_URL, {
|
| 1552 |
method: 'POST',
|
|
@@ -1560,6 +1724,8 @@
|
|
| 1560 |
},
|
| 1561 |
|
| 1562 |
syncHistoryFromCloud: async () => {
|
|
|
|
|
|
|
| 1563 |
const messageZone = document.getElementById('msgs');
|
| 1564 |
document.getElementById('welcomeState').style.display = 'none';
|
| 1565 |
messageZone.innerHTML = '<div style="text-align:center; padding:80px; font-weight:800; color:var(--brand-saffron); font-size:18px;">Decrypting Historic Archives...</div>';
|
|
@@ -1595,7 +1761,7 @@
|
|
| 1595 |
},
|
| 1596 |
|
| 1597 |
saveHistoryToCloud: () => {
|
| 1598 |
-
if (AppState.currentUser) {
|
| 1599 |
fetch(Config.GAS_URL, {
|
| 1600 |
method: 'POST',
|
| 1601 |
mode: 'cors',
|
|
@@ -1608,13 +1774,13 @@
|
|
| 1608 |
buildPreviewHTML: (attachmentObj) => {
|
| 1609 |
if (!attachmentObj) return '';
|
| 1610 |
if (attachmentObj.type === 'image') {
|
| 1611 |
-
return `<img src="data:image/jpeg;base64,${attachmentObj.data}" class="chat-in-bubble-img" alt="Attached
|
| 1612 |
} else if (attachmentObj.type === 'video') {
|
| 1613 |
-
return `<div class="chat-file-card"><div class="icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg></div><span class="name">${attachmentObj.name}</span></div>`;
|
| 1614 |
} else if (attachmentObj.type === 'audio') {
|
| 1615 |
-
return `<div class="chat-file-card"><div class="icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg></div><span class="name">${attachmentObj.name}</span></div>`;
|
| 1616 |
} else {
|
| 1617 |
-
return `<div class="chat-file-card"><div class="icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg></div><span class="name">${attachmentObj.name}</span></div>`;
|
| 1618 |
}
|
| 1619 |
},
|
| 1620 |
|
|
@@ -1624,7 +1790,7 @@
|
|
| 1624 |
let visibleText = textContent;
|
| 1625 |
if(textContent.includes('---DATA BEGIN---')) {
|
| 1626 |
const parts = textContent.split('User Prompt:');
|
| 1627 |
-
if(parts.length > 1) visibleText = parts
|
| 1628 |
}
|
| 1629 |
|
| 1630 |
const bubbleBox = document.createElement('div');
|
|
@@ -1669,7 +1835,7 @@
|
|
| 1669 |
let actionTray = bubbleDOM.querySelector('.bot-actions-row');
|
| 1670 |
if (!isCurrentlyStreaming && !actionTray && strippedMarkup.trim().length > 0) {
|
| 1671 |
const safeRawCopy = strippedMarkup.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '');
|
| 1672 |
-
//
|
| 1673 |
const safeRawTTS = strippedMarkup.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, ' ');
|
| 1674 |
|
| 1675 |
const actionsHTML = `
|
|
@@ -1691,6 +1857,7 @@
|
|
| 1691 |
},
|
| 1692 |
|
| 1693 |
initiateDispatch: async () => {
|
|
|
|
| 1694 |
if (AppState.isProcessing) return;
|
| 1695 |
|
| 1696 |
const inputField = document.getElementById('mainChatInput');
|
|
@@ -1702,14 +1869,14 @@
|
|
| 1702 |
if (!AppState.currentUser) {
|
| 1703 |
if (AppState.guestCount >= 3) {
|
| 1704 |
AuthManager.openModal();
|
| 1705 |
-
return Utils.showToast("Query limit reached. Secure Login required.");
|
| 1706 |
}
|
| 1707 |
AppState.guestCount++;
|
| 1708 |
localStorage.setItem('arjun_guest', AppState.guestCount.toString());
|
| 1709 |
document.getElementById('guestCountDisplay').innerText = AppState.guestCount;
|
| 1710 |
}
|
| 1711 |
|
| 1712 |
-
// Prepare UI
|
| 1713 |
inputField.value = '';
|
| 1714 |
inputField.style.height = 'auto';
|
| 1715 |
document.getElementById('masterSendBtn').disabled = true;
|
|
@@ -1752,21 +1919,21 @@
|
|
| 1752 |
const activeBotBubble = ChatEngine.renderBotMessage('', false);
|
| 1753 |
UIManager.forceScrollDown();
|
| 1754 |
|
| 1755 |
-
//
|
| 1756 |
const cryptographicSeed = Math.floor(Math.random() * 999999999);
|
| 1757 |
|
| 1758 |
-
//
|
| 1759 |
-
let cognitivePrompt = `You are Arjun 2.O, a
|
| 1760 |
-
LANGUAGE
|
| 1761 |
|
| 1762 |
IMAGE SYNTHESIS OVERRIDE:
|
| 1763 |
When commanded to render, generate, or create an image/photograph, you MUST reply with a precise markdown image node targeting Pollinations AI.
|
| 1764 |
Formula: 
|
| 1765 |
Available Models: flux, flux-realism, flux-anime, flux-3d.
|
| 1766 |
-
CRITICAL: The seed parameter is mandatory to bypass cache mechanics. Always render this markdown node.`;
|
| 1767 |
|
| 1768 |
if (AppState.isLiveModeEngaged) {
|
| 1769 |
-
cognitivePrompt += `\n\n[LIVE ACOUSTIC OVERRIDE]: You are currently engaged in a LIVE, REAL-TIME VOCAL INTERFACE.
|
| 1770 |
MANDATORY direct answers. Maximum brevity. Eliminate complex markdown, massive code blocks, or extended lists. Replicate human telephonic brevity perfectly.`;
|
| 1771 |
}
|
| 1772 |
|
|
@@ -1777,7 +1944,7 @@
|
|
| 1777 |
attachments: payloadAttachments,
|
| 1778 |
system_prompt: cognitivePrompt,
|
| 1779 |
history: secureHistory,
|
| 1780 |
-
max_tokens: AppState.isLiveModeEngaged ?
|
| 1781 |
temperature: 0.75
|
| 1782 |
};
|
| 1783 |
|
|
@@ -1811,8 +1978,8 @@
|
|
| 1811 |
|
| 1812 |
try {
|
| 1813 |
const jsonSegment = JSON.parse(parseableStr);
|
| 1814 |
-
if (jsonSegment.choices && jsonSegment.choices
|
| 1815 |
-
responseBuffer += jsonSegment.choices
|
| 1816 |
ChatEngine.updateBotBubble(activeBotBubble, responseBuffer, true);
|
| 1817 |
UIManager.autoScroll();
|
| 1818 |
}
|
|
@@ -1849,14 +2016,12 @@
|
|
| 1849 |
// 9. EVENT DELEGATION & BOOTSTRAP INVOCATION
|
| 1850 |
// =======================================================================================
|
| 1851 |
|
| 1852 |
-
// Define global function for welcome screen overlay
|
| 1853 |
window.startArjunApp = function() {
|
| 1854 |
document.getElementById('welcomeOverlay').classList.add('hidden');
|
| 1855 |
localStorage.setItem('arjun_welcome_seen', 'true');
|
| 1856 |
setTimeout(() => { document.getElementById('welcomeOverlay').style.display = 'none'; }, 400);
|
| 1857 |
};
|
| 1858 |
|
| 1859 |
-
// Bind all interactive elements safely using Event Listeners to avoid scope issues
|
| 1860 |
document.addEventListener('DOMContentLoaded', () => {
|
| 1861 |
// Sidebar Controls
|
| 1862 |
document.getElementById('btnSidebarOpen').addEventListener('click', UIManager.toggleSidebar);
|
|
@@ -1918,6 +2083,10 @@
|
|
| 1918 |
document.getElementById('msgs').addEventListener('scroll', UIManager.checkScrollState);
|
| 1919 |
document.getElementById('scrollTriggerBtn').addEventListener('click', UIManager.forceScrollDown);
|
| 1920 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1921 |
// Prevent unwanted default mobile behaviours on double-tap
|
| 1922 |
document.addEventListener('dblclick', function(event) {
|
| 1923 |
event.preventDefault();
|
|
@@ -1931,6 +2100,7 @@
|
|
| 1931 |
}
|
| 1932 |
|
| 1933 |
// Fire Boot Processes
|
|
|
|
| 1934 |
VoiceEngine.init();
|
| 1935 |
AuthManager.initUI();
|
| 1936 |
if(AppState.currentUser) { ChatEngine.syncHistoryFromCloud(); }
|
|
@@ -1939,4 +2109,3 @@
|
|
| 1939 |
</script>
|
| 1940 |
</body>
|
| 1941 |
</html>
|
| 1942 |
-
|
|
|
|
| 1 |
+
|
| 2 |
<!DOCTYPE html>
|
| 3 |
<html lang="en" dir="ltr">
|
| 4 |
<head>
|
|
|
|
| 12 |
<title>Arjun 2.O - Professional Advanced AI</title>
|
| 13 |
|
| 14 |
<!-- =========================================================================================
|
| 15 |
+
EXTERNAL RESOURCES, FONTS & LIBRARIES (बाहरी स्रोत)
|
| 16 |
========================================================================================= -->
|
| 17 |
<link rel="preconnect" href="https://fonts.googleapis.com">
|
| 18 |
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
|
|
| 30 |
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
|
| 31 |
|
| 32 |
<!-- =========================================================================================
|
| 33 |
+
MASSIVE ENTERPRISE CSS ARCHITECTURE (विस्तृत और उन्नत स्टाइलिंग)
|
| 34 |
========================================================================================= -->
|
| 35 |
<style>
|
| 36 |
/* ---------------------------------------------------------------------------------------
|
| 37 |
+
A. CSS VARIABLES, DESIGN TOKENS & BRANDING
|
| 38 |
--------------------------------------------------------------------------------------- */
|
| 39 |
:root {
|
| 40 |
+
/* Brand Identity: Saffron, White, Green (Tiranga Theme) */
|
| 41 |
--brand-saffron: #FF9933;
|
| 42 |
--brand-saffron-light: rgba(255, 153, 51, 0.12);
|
| 43 |
--brand-saffron-dark: #e68a2e;
|
|
|
|
| 65 |
--text-tertiary: #9ca3af;
|
| 66 |
--text-inverse: #ffffff;
|
| 67 |
--text-error: #ef4444;
|
| 68 |
+
--text-success: #10b981;
|
| 69 |
|
| 70 |
/* Borders & Dividers */
|
| 71 |
--border-light: rgba(0, 0, 0, 0.08);
|
| 72 |
--border-medium: rgba(0, 0, 0, 0.15);
|
| 73 |
+
--border-focus: rgba(255, 153, 51, 0.5);
|
| 74 |
|
| 75 |
/* Shadows & Elevation */
|
| 76 |
--shadow-xs: 0 1px 2px rgba(0,0,0,0.04);
|
|
|
|
| 78 |
--shadow-md: 0 4px 6px -1px rgba(0,0,0,0.05), 0 2px 4px -1px rgba(0,0,0,0.03);
|
| 79 |
--shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.05), 0 4px 6px -2px rgba(0,0,0,0.025);
|
| 80 |
--shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.05), 0 10px 10px -5px rgba(0,0,0,0.02);
|
| 81 |
+
--shadow-float: 0 25px 50px -12px rgba(0,0,0,0.15);
|
| 82 |
--shadow-inner: inset 0 2px 4px rgba(0,0,0,0.06);
|
| 83 |
|
| 84 |
/* Font Families */
|
|
|
|
| 99 |
--ease-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
| 100 |
--ease-spring: 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
| 101 |
|
| 102 |
+
/* Z-Indices - Properly Layered Enterprise System */
|
| 103 |
--z-ambient: -1;
|
| 104 |
--z-base: 1;
|
| 105 |
--z-header: 10;
|
|
|
|
| 110 |
--z-modal: 5000;
|
| 111 |
--z-toast: 9000;
|
| 112 |
--z-live: 9999;
|
| 113 |
+
--z-offline: 10000;
|
| 114 |
}
|
| 115 |
|
| 116 |
/* ---------------------------------------------------------------------------------------
|
|
|
|
| 120 |
box-sizing: border-box;
|
| 121 |
margin: 0;
|
| 122 |
padding: 0;
|
|
|
|
| 123 |
-webkit-tap-highlight-color: transparent !important;
|
| 124 |
-webkit-touch-callout: none !important;
|
| 125 |
}
|
|
|
|
| 138 |
-moz-osx-font-smoothing: grayscale;
|
| 139 |
}
|
| 140 |
|
| 141 |
+
/* Accessibilty focus states */
|
| 142 |
+
*:focus-visible { outline: 2px solid var(--brand-saffron); outline-offset: 2px; }
|
| 143 |
+
*:focus { outline: none; }
|
| 144 |
|
| 145 |
button, input, textarea, select {
|
| 146 |
font-family: inherit;
|
|
|
|
| 150 |
}
|
| 151 |
|
| 152 |
button { cursor: pointer; user-select: none; }
|
| 153 |
+
button:disabled { cursor: not-allowed; opacity: 0.6; }
|
| 154 |
a { color: inherit; text-decoration: none; }
|
| 155 |
img { max-width: 100%; height: auto; display: block; }
|
| 156 |
svg { flex-shrink: 0; }
|
| 157 |
|
| 158 |
+
/* Custom Enterprise Scrollbars */
|
| 159 |
+
::-webkit-scrollbar { width: 8px; height: 8px; }
|
| 160 |
::-webkit-scrollbar-track { background: transparent; }
|
| 161 |
+
::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 10px; border: 2px solid transparent; background-clip: padding-box; transition: background var(--ease-smooth); }
|
| 162 |
+
::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.3); border: 2px solid transparent; background-clip: padding-box; }
|
| 163 |
|
| 164 |
/* ---------------------------------------------------------------------------------------
|
| 165 |
+
C. NETWORK OFFLINE & AMBIENT BACKGROUND SYSTEM
|
| 166 |
--------------------------------------------------------------------------------------- */
|
| 167 |
+
.network-offline-banner {
|
| 168 |
+
position: fixed; top: 0; left: 0; right: 0; background: var(--text-error); color: white;
|
| 169 |
+
text-align: center; padding: 8px; font-size: 14px; font-weight: 700; z-index: var(--z-offline);
|
| 170 |
+
transform: translateY(-100%); transition: transform var(--ease-spring); display: flex; align-items: center; justify-content: center; gap: 8px; box-shadow: var(--shadow-md);
|
| 171 |
+
}
|
| 172 |
+
.network-offline-banner.active { transform: translateY(0); }
|
| 173 |
+
|
| 174 |
.ambient-environment {
|
| 175 |
position: fixed; inset: 0; z-index: var(--z-ambient); overflow: hidden; pointer-events: none;
|
| 176 |
background: linear-gradient(135deg, var(--brand-saffron-light) 0%, var(--bg-primary) 50%, var(--brand-green-light) 100%);
|
|
|
|
| 197 |
}
|
| 198 |
|
| 199 |
/* ---------------------------------------------------------------------------------------
|
| 200 |
+
D. ENTERPRISE TOAST NOTIFICATION ENGINE
|
| 201 |
--------------------------------------------------------------------------------------- */
|
| 202 |
.toast-wrapper {
|
| 203 |
position: absolute; top: 24px; left: 50%; transform: translateX(-50%);
|
| 204 |
z-index: var(--z-toast); display: flex; flex-direction: column; gap: 12px;
|
| 205 |
+
pointer-events: none; width: 90%; max-width: 450px; align-items: center;
|
| 206 |
}
|
| 207 |
.toast-notification {
|
| 208 |
+
background: rgba(17, 24, 39, 0.95); color: var(--text-inverse); padding: 14px 24px;
|
| 209 |
border-radius: var(--radius-full); font-size: 14px; font-weight: 600; box-shadow: var(--shadow-xl);
|
| 210 |
animation: toastSlideIn var(--ease-spring) forwards, toastFadeOut 0.4s 3.5s forwards;
|
| 211 |
+
display: flex; align-items: center; gap: 12px; width: fit-content; max-width: 100%; word-break: break-word;
|
| 212 |
+
backdrop-filter: blur(10px); border: 1px solid rgba(255,255,255,0.1);
|
| 213 |
}
|
| 214 |
+
.toast-notification.error { background: rgba(239, 68, 68, 0.95); border-color: rgba(255,255,255,0.2); }
|
| 215 |
+
.toast-notification.success { background: rgba(16, 185, 129, 0.95); border-color: rgba(255,255,255,0.2); }
|
| 216 |
+
.toast-notification svg { width: 20px; height: 20px; flex-shrink: 0; color: var(--brand-saffron); }
|
| 217 |
+
.toast-notification.error svg, .toast-notification.success svg { color: white; }
|
| 218 |
+
|
| 219 |
@keyframes toastSlideIn { from { opacity: 0; transform: translateY(-30px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } }
|
| 220 |
@keyframes toastFadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-20px); } }
|
| 221 |
|
|
|
|
| 230 |
}
|
| 231 |
.startup-overlay.hidden { opacity: 0; visibility: hidden; pointer-events: none; }
|
| 232 |
|
| 233 |
+
.startup-logo-container { position: relative; margin-bottom: 32px; animation: popIn var(--ease-spring) 0.5s both; }
|
| 234 |
.startup-logo-container img { width: 140px; height: 140px; border-radius: var(--radius-xl); object-fit: cover; box-shadow: var(--shadow-xl); border: 4px solid var(--bg-primary); position: relative; z-index: 2; }
|
| 235 |
.startup-logo-glow { position: absolute; inset: -20px; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); filter: blur(30px); opacity: 0.4; border-radius: var(--radius-full); z-index: 1; animation: pulseGlow 3s infinite alternate; }
|
| 236 |
|
| 237 |
@keyframes pulseGlow { from { opacity: 0.3; transform: scale(0.95); } to { opacity: 0.6; transform: scale(1.05); } }
|
| 238 |
+
@keyframes popIn { 0% { opacity: 0; transform: scale(0.8) translateY(20px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }
|
| 239 |
|
| 240 |
+
.startup-title { font-family: var(--font-sans); font-size: 2.8rem; font-weight: 900; color: var(--text-primary); letter-spacing: -1px; margin-bottom: 16px; animation: slideUp var(--ease-smooth) 0.6s both; }
|
| 241 |
+
.startup-desc { font-size: 1.15rem; color: var(--text-secondary); line-height: 1.6; max-width: 600px; margin-bottom: 40px; animation: slideUp var(--ease-smooth) 0.7s both; font-weight: 500; }
|
| 242 |
|
| 243 |
+
.btn-startup-launch { padding: 18px 48px; background: var(--text-primary); color: var(--text-inverse); border: none; border-radius: var(--radius-xl); font-size: 1.125rem; font-weight: 700; cursor: pointer; transition: var(--ease-spring); box-shadow: 0 10px 25px rgba(0,0,0,0.15); animation: slideUp var(--ease-smooth) 0.8s both; display: flex; align-items: center; gap: 12px; position: relative; overflow: hidden; }
|
| 244 |
.btn-startup-launch::before { content: ''; position: absolute; top: 0; left: -100%; width: 50%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); transform: skewX(-20deg); transition: 0.5s; }
|
| 245 |
.btn-startup-launch:hover { background: #000000; transform: translateY(-4px) scale(1.02); box-shadow: 0 15px 35px rgba(0,0,0,0.25); }
|
| 246 |
.btn-startup-launch:hover::before { left: 150%; }
|
| 247 |
|
| 248 |
+
@keyframes slideUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
|
| 249 |
|
| 250 |
/* ---------------------------------------------------------------------------------------
|
| 251 |
F. CORE APPLICATION LAYOUT & SIDEBAR
|
|
|
|
| 257 |
.sidebar-header { padding: 28px 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; }
|
| 258 |
|
| 259 |
.brand-identity { display: flex; align-items: center; gap: 14px; text-decoration: none; }
|
| 260 |
+
.brand-identity img { width: 48px; height: 48px; border-radius: var(--radius-md); object-fit: cover; box-shadow: var(--shadow-sm); border: 2px solid var(--bg-primary); transition: transform var(--ease-spring); }
|
| 261 |
+
.brand-identity:hover img { transform: scale(1.08) rotate(-5deg); box-shadow: var(--shadow-md); }
|
| 262 |
.brand-typography { display: flex; flex-direction: column; }
|
| 263 |
.brand-name { font-family: var(--font-sans); font-weight: 900; font-size: 20px; color: var(--text-primary); letter-spacing: -0.5px; line-height: 1.1; }
|
| 264 |
+
.brand-edition { font-size: 11px; font-weight: 800; letter-spacing: 1.5px; text-transform: uppercase; background: linear-gradient(90deg, var(--brand-saffron-dark), var(--brand-green-dark)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; margin-top: 4px; }
|
| 265 |
|
| 266 |
.btn-mobile-close { display: none; background: rgba(0,0,0,0.05); border: none; width: 36px; height: 36px; border-radius: 50%; color: var(--text-primary); cursor: pointer; align-items: center; justify-content: center; transition: var(--ease-fast); }
|
| 267 |
.btn-mobile-close:hover { background: rgba(239, 68, 68, 0.15); color: var(--text-error); transform: rotate(90deg); }
|
|
|
|
| 280 |
.user-details { flex: 1; overflow: hidden; }
|
| 281 |
.user-email-id { font-size: 14px; font-weight: 700; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
| 282 |
.user-clearance-level { font-size: 11px; color: var(--brand-green); font-weight: 600; margin-top: 2px; display: flex; align-items: center; gap: 4px; }
|
| 283 |
+
.user-clearance-level::before { content: ''; display: block; width: 6px; height: 6px; border-radius: 50%; background: var(--brand-green); box-shadow: 0 0 5px var(--brand-green-glow); }
|
| 284 |
.btn-system-logout { background: rgba(239, 68, 68, 0.08); color: var(--text-error); border: none; width: 38px; height: 38px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--ease-fast); }
|
| 285 |
.btn-system-logout:hover { background: var(--text-error); color: #ffffff; transform: scale(1.05); }
|
| 286 |
|
|
|
|
| 301 |
--------------------------------------------------------------------------------------- */
|
| 302 |
.main-chat-wrapper { flex: 1; display: flex; flex-direction: column; position: relative; min-width: 320px; transition: width var(--ease-smooth); background: transparent; }
|
| 303 |
|
| 304 |
+
.chat-top-bar { padding: 16px 32px; border-bottom: 1px solid var(--border-light); background: rgba(255, 255, 255, 0.85); backdrop-filter: blur(15px); display: flex; align-items: center; justify-content: space-between; z-index: var(--z-header); }
|
| 305 |
.top-bar-left { display: flex; align-items: center; gap: 16px; }
|
| 306 |
.btn-hamburger { display: none; background: var(--bg-primary); border: 1px solid var(--border-light); cursor: pointer; color: var(--text-primary); width: 42px; height: 42px; border-radius: var(--radius-sm); align-items: center; justify-content: center; box-shadow: var(--shadow-sm); transition: var(--ease-fast); }
|
| 307 |
.btn-hamburger:hover { background: var(--bg-secondary); }
|
| 308 |
|
| 309 |
+
.system-status-pill { display: flex; align-items: center; gap: 10px; font-size: 14px; font-weight: 700; color: var(--text-primary); padding: 8px 18px; background: var(--bg-secondary); border-radius: var(--radius-full); border: 1px solid var(--border-light); transition: var(--ease-smooth); }
|
| 310 |
+
.pulse-dot { width: 10px; height: 10px; border-radius: 50%; background: var(--brand-green); box-shadow: 0 0 0 3px rgba(19, 136, 8, 0.2); animation: statusPulse 2s infinite; transition: background var(--ease-smooth); }
|
| 311 |
+
.system-status-pill.offline .pulse-dot { background: var(--text-error); box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.2); animation: none; }
|
| 312 |
@keyframes statusPulse { 0% { box-shadow: 0 0 0 0 rgba(19,136,8,0.4); } 70% { box-shadow: 0 0 0 6px rgba(19,136,8,0); } 100% { box-shadow: 0 0 0 0 rgba(19,136,8,0); } }
|
| 313 |
|
| 314 |
/* DYNAMIC LIVE MODE ACTIVATOR */
|
|
|
|
| 326 |
.hero-avatar-wrapper img { width: 100px; height: 100px; border-radius: var(--radius-xl); object-fit: cover; box-shadow: var(--shadow-xl); border: 3px solid var(--bg-primary); position: relative; z-index: 2; }
|
| 327 |
.hero-avatar-glow { position: absolute; inset: -12px; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); filter: blur(25px); opacity: 0.35; border-radius: 40px; z-index: 1; animation: rotateGlow 10s linear infinite;}
|
| 328 |
@keyframes rotateGlow { 100% { transform: rotate(360deg); } }
|
| 329 |
+
@keyframes slideUpFade { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
|
| 330 |
|
| 331 |
+
.hero-greeting { font-size: 42px; font-weight: 900; letter-spacing: -1.5px; margin-bottom: 18px; color: var(--text-primary); line-height: 1.2; }
|
| 332 |
.hero-description { font-size: 17px; color: var(--text-secondary); line-height: 1.6; margin-bottom: 40px; font-weight: 500; }
|
| 333 |
|
| 334 |
+
/* GUEST BADGE LIMIT DISPLAY */
|
| 335 |
.guest-warning-badge { font-size: 13px; font-weight: 700; color: var(--text-error); background: #fee2e2; padding: 10px 20px; border-radius: var(--radius-full); margin-bottom: 32px; display: none; align-items: center; gap: 8px; border: 1px solid #fca5a5; box-shadow: var(--shadow-sm); }
|
| 336 |
.guest-warning-badge svg { width: 18px; height: 18px; }
|
| 337 |
|
|
|
|
| 347 |
/* ---------------------------------------------------------------------------------------
|
| 348 |
H. CHAT MESSAGE BUBBLES & PREVIEWS
|
| 349 |
--------------------------------------------------------------------------------------- */
|
| 350 |
+
.chat-message-row { display: flex; gap: 20px; max-width: 88%; animation: messagePopIn 0.4s var(--ease-spring) both; }
|
| 351 |
+
@keyframes messagePopIn { from { opacity: 0; transform: translateY(20px) scale(0.97); } to { opacity: 1; transform: none; } }
|
| 352 |
.chat-message-row.user-msg { flex-direction: row-reverse; align-self: flex-end; }
|
| 353 |
.chat-message-row.bot-msg { align-self: flex-start; }
|
| 354 |
|
|
|
|
| 364 |
.file-badge svg { width: 12px; height: 12px; }
|
| 365 |
|
| 366 |
/* In-Bubble File Previews */
|
| 367 |
+
.chat-in-bubble-img { max-width: 320px; max-height: 320px; border-radius: var(--radius-md); object-fit: cover; border: 2px solid rgba(255,255,255,0.2); box-shadow: var(--shadow-md); margin-bottom: 12px; transition: transform var(--ease-smooth); cursor: zoom-in; }
|
| 368 |
+
.chat-in-bubble-img:hover { transform: scale(1.02); }
|
| 369 |
.chat-file-card { display: flex; align-items: center; gap: 14px; background: rgba(255,255,255,0.15); border: 1px solid rgba(255,255,255,0.3); padding: 12px 18px; border-radius: var(--radius-md); margin-bottom: 12px; max-width: 350px; backdrop-filter: blur(10px); }
|
| 370 |
.chat-file-card .icon { font-size: 24px; background: rgba(0,0,0,0.1); width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-sm); }
|
| 371 |
.chat-file-card .name { font-size: 14px; font-weight: 600; color: var(--brand-white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
|
|
| 385 |
.btn-bot-action.action-tts { color: var(--brand-green-dark); border-color: rgba(19, 136, 8, 0.25); background: var(--brand-green-light); }
|
| 386 |
.btn-bot-action.action-tts:hover { background: rgba(19, 136, 8, 0.2); }
|
| 387 |
|
| 388 |
+
/* Markdown Formatting inside Bubbles */
|
| 389 |
.chat-bubble p { margin-bottom: 16px; }
|
| 390 |
.chat-bubble p:last-child { margin-bottom: 0; }
|
| 391 |
.chat-bubble strong { font-weight: 800; color: inherit; }
|
| 392 |
+
.chat-bubble em { font-style: italic; }
|
| 393 |
+
.chat-bubble ul, .chat-bubble ol { margin-bottom: 16px; padding-left: 24px; }
|
| 394 |
+
.chat-bubble li { margin-bottom: 6px; }
|
| 395 |
+
.chat-bubble blockquote { border-left: 4px solid var(--brand-saffron); padding-left: 16px; margin: 16px 0; color: var(--text-secondary); background: var(--bg-secondary); padding: 12px 16px; border-radius: 0 var(--radius-sm) var(--radius-sm) 0; }
|
| 396 |
+
|
| 397 |
+
/* Advanced Tables */
|
| 398 |
+
.chat-bubble table { width: 100%; border-collapse: collapse; margin: 20px 0; border-radius: var(--radius-sm); overflow: hidden; box-shadow: var(--shadow-sm); }
|
| 399 |
+
.chat-bubble th, .chat-bubble td { border: 1px solid var(--border-light); padding: 12px 16px; text-align: left; }
|
| 400 |
+
.chat-bubble th { background: var(--bg-secondary); font-weight: 700; color: var(--text-primary); }
|
| 401 |
+
.chat-bubble tr:nth-child(even) { background: var(--bg-tertiary); }
|
| 402 |
+
|
| 403 |
.chat-bubble code { font-family: var(--font-mono); background: rgba(0,0,0,0.06); padding: 4px 8px; border-radius: 6px; font-size: 0.85em; color: #d946ef; font-weight: 600; border: 1px solid rgba(0,0,0,0.05); }
|
| 404 |
.user-msg .chat-bubble code { background: rgba(255,255,255,0.2); color: var(--brand-white); border-color: rgba(255,255,255,0.1); }
|
| 405 |
+
|
| 406 |
.chat-bubble img { max-width: 100%; border-radius: var(--radius-md); margin: 16px 0; box-shadow: var(--shadow-lg); border: 1px solid var(--border-light); display: block; }
|
| 407 |
+
|
| 408 |
+
/* Custom Pre-Code Blocks */
|
| 409 |
+
.chat-bubble pre { background: #1e1e2e; color: #a6adc8; padding: 0; border-radius: var(--radius-md); overflow: hidden; margin: 20px 0; font-family: var(--font-mono); font-size: 14px; line-height: 1.6; border: 1px solid #313244; box-shadow: var(--shadow-md); }
|
| 410 |
+
.chat-bubble pre code { background: transparent; color: inherit; padding: 20px; font-weight: 400; border: none; display: block; overflow-x: auto; }
|
| 411 |
|
| 412 |
/* Streaming Loading UI Component */
|
| 413 |
.streaming-code-container { background: #1e1e2e; border-radius: var(--radius-md); border: 1px solid #313244; overflow: hidden; margin: 20px 0; box-shadow: var(--shadow-lg); }
|
|
|
|
| 431 |
I. BOTTOM INPUT DOCK & TOOLS
|
| 432 |
--------------------------------------------------------------------------------------- */
|
| 433 |
.input-dock { flex-shrink: 0; padding: 0 40px 32px; background: transparent; position: relative; z-index: var(--z-dock); }
|
| 434 |
+
.input-master-container { display: flex; flex-direction: column; background: var(--bg-glass); backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px); border: 1px solid var(--border-light); border-radius: var(--radius-xl); box-shadow: var(--shadow-xl); transition: var(--ease-smooth); position: relative; }
|
| 435 |
.input-master-container:focus-within { border-color: var(--brand-saffron); box-shadow: 0 20px 40px -5px rgba(255,153,51,0.15), 0 0 0 4px var(--brand-saffron-light); }
|
| 436 |
|
| 437 |
+
/* Upload Preview Header */
|
| 438 |
+
.upload-preview-bar { display: none; padding: 20px 28px 0; align-items: center; gap: 16px; border-bottom: 1px solid transparent; }
|
| 439 |
+
.upload-preview-bar.visible { display: flex; animation: fadeIn 0.3s ease; border-bottom-color: var(--border-light); padding-bottom: 15px; }
|
| 440 |
.upload-thumb { width: 52px; height: 52px; object-fit: cover; border-radius: var(--radius-sm); border: 1px solid var(--border-light); display: flex; justify-content: center; align-items: center; background: var(--bg-tertiary); font-size: 24px; box-shadow: var(--shadow-sm); }
|
| 441 |
.upload-meta { flex: 1; }
|
| 442 |
.upload-filename { font-size: 14px; font-weight: 700; color: var(--text-primary); max-width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
|
|
| 451 |
.main-textarea { flex: 1; border: none; outline: none; background: transparent; font-family: var(--font-sans); font-size: 16px; color: var(--text-primary); line-height: 1.5; resize: none; max-height: 200px; min-height: 24px; padding: 12px 0; margin: 0 8px; font-weight: 500; }
|
| 452 |
.main-textarea::placeholder { color: var(--text-tertiary); font-weight: 400; }
|
| 453 |
|
| 454 |
+
/* Real-time Interim Text Feedback */
|
| 455 |
+
.interim-text-overlay { position: absolute; pointer-events: none; color: var(--text-tertiary); font-style: italic; z-index: 2; padding: 12px 0; margin: 0 8px; left: 140px; top: 14px; right: 80px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; display: none; }
|
| 456 |
+
.interim-text-overlay.active { display: block; }
|
| 457 |
+
|
| 458 |
/* Attachment Menu Dropdown */
|
| 459 |
.attachment-module { position: relative; }
|
| 460 |
+
.btn-tool-icon { width: 44px; height: 44px; border-radius: var(--radius-md); background: transparent; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); transition: var(--ease-fast); position: relative; }
|
| 461 |
.btn-tool-icon:hover { background: var(--bg-tertiary); color: var(--text-primary); }
|
| 462 |
.btn-tool-icon svg { width: 24px; height: 24px; stroke-width: 2; }
|
| 463 |
|
|
|
|
| 475 |
.language-toggle-chip.hindi-active { background: var(--brand-green-light); color: var(--brand-green-dark); border-color: var(--brand-green); }
|
| 476 |
.language-toggle-chip svg { width: 16px; height: 16px; }
|
| 477 |
|
| 478 |
+
.btn-mic-tool { position: relative; }
|
| 479 |
+
.btn-mic-tool.active-listening { color: #ffffff; background: var(--text-error); border-radius: var(--radius-full); }
|
| 480 |
+
.btn-mic-tool.active-listening::before { content: ''; position: absolute; inset: -4px; border-radius: 50%; border: 2px solid var(--text-error); animation: rippleMic 1.5s infinite ease-out; }
|
| 481 |
+
@keyframes rippleMic { 0% { transform: scale(0.8); opacity: 1; } 100% { transform: scale(1.5); opacity: 0; } }
|
| 482 |
|
| 483 |
/* Master Send Button */
|
| 484 |
+
.btn-master-send { width: 48px; height: 48px; border-radius: 16px; border: none; background: linear-gradient(135deg, var(--brand-saffron) 0%, var(--brand-green) 100%); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--ease-spring); box-shadow: 0 6px 20px rgba(19, 136, 8, 0.25); position: relative; overflow: hidden; }
|
| 485 |
+
.btn-master-send::before { content: ''; position: absolute; top: 0; left: -100%; width: 100%; height: 100%; background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); transition: 0.5s; }
|
| 486 |
+
.btn-master-send svg { width: 20px; height: 20px; fill: white; transition: transform 0.2s; position: relative; z-index: 2; }
|
| 487 |
.btn-master-send:hover { transform: translateY(-3px); box-shadow: 0 10px 25px rgba(255, 153, 51, 0.4); }
|
| 488 |
+
.btn-master-send:hover::before { left: 100%; }
|
| 489 |
.btn-master-send:hover svg { transform: translate(2px, -2px); }
|
| 490 |
+
.btn-master-send:disabled { opacity: 0.5; cursor: not-allowed; filter: grayscale(100%); transform: none; box-shadow: none; }
|
| 491 |
|
| 492 |
.bottom-branding-text { text-align: center; font-size: 12px; color: var(--text-tertiary); margin-top: 16px; font-weight: 600; letter-spacing: 0.5px; }
|
| 493 |
|
|
|
|
| 508 |
.btn-workspace-tool.close:hover { color: #e06c75; background: rgba(224, 108, 117, 0.1); }
|
| 509 |
|
| 510 |
.workspace-editor-surface { flex: 1; overflow-y: auto; background: #282c34; padding: 24px; position: relative; }
|
| 511 |
+
|
| 512 |
+
/* Line Numbers Implementation for Workspace */
|
| 513 |
+
.workspace-editor-content { display: flex; font-family: var(--font-mono); font-size: 14px; line-height: 1.6; }
|
| 514 |
+
.workspace-line-numbers { padding-right: 15px; border-right: 1px solid #3e4451; color: #5c6370; text-align: right; user-select: none; }
|
| 515 |
+
.workspace-editor-surface pre { margin: 0; padding: 0 0 0 15px; background: transparent; overflow-x: auto; color: #abb2bf; border: none; box-shadow: none; flex: 1; }
|
| 516 |
|
| 517 |
/* ---------------------------------------------------------------------------------------
|
| 518 |
K. LIVE MODE FULLSCREEN OVERLAY
|
| 519 |
--------------------------------------------------------------------------------------- */
|
| 520 |
+
.live-mode-viewport { position: fixed; inset: 0; background: rgba(255, 255, 255, 0.98); z-index: var(--z-live); display: flex; flex-direction: column; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity var(--ease-smooth); backdrop-filter: blur(20px); }
|
| 521 |
.live-mode-viewport.active { opacity: 1; pointer-events: auto; }
|
| 522 |
.btn-close-live { position: absolute; top: 40px; right: 40px; background: rgba(0,0,0,0.04); border: none; width: 56px; height: 56px; border-radius: 50%; font-size: 24px; cursor: pointer; transition: var(--ease-spring); display: flex; align-items: center; justify-content: center; color: var(--text-secondary); }
|
| 523 |
.btn-close-live:hover { background: rgba(239, 68, 68, 0.15); color: var(--text-error); transform: rotate(90deg); }
|
|
|
|
| 525 |
.pulsing-orb-wrapper { position: relative; width: 250px; height: 250px; display: flex; align-items: center; justify-content: center; margin-bottom: 50px; }
|
| 526 |
.energy-core { width: 120px; height: 120px; border-radius: 50%; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); box-shadow: 0 0 50px rgba(19, 136, 8, 0.5); position: relative; z-index: 2; transition: transform 0.15s ease-out; }
|
| 527 |
.energy-core.speaking { animation: coreSpeak 0.5s infinite alternate; }
|
| 528 |
+
@keyframes coreSpeak { 0% { transform: scale(1); box-shadow: 0 0 30px rgba(19,136,8,0.4); } 100% { transform: scale(1.2); box-shadow: 0 0 80px rgba(255,153,51,0.7); } }
|
| 529 |
|
| 530 |
.energy-ring { position: absolute; inset: 0; border: 3px solid var(--brand-saffron); border-radius: 50%; opacity: 0; animation: energyRipple 2.5s cubic-bezier(0.4, 0, 0.2, 1) infinite; }
|
| 531 |
.energy-ring:nth-child(2) { animation-delay: 0.8s; border-color: var(--brand-green); }
|
| 532 |
.energy-ring:nth-child(3) { animation-delay: 1.6s; border-color: var(--text-primary); }
|
| 533 |
@keyframes energyRipple { 0% { transform: scale(0.4); opacity: 1; border-width: 6px; } 100% { transform: scale(1.6); opacity: 0; border-width: 1px; } }
|
| 534 |
|
| 535 |
+
.live-feedback-status { font-size: 28px; font-weight: 900; color: var(--text-primary); margin-bottom: 16px; font-family: var(--font-sans); letter-spacing: -0.5px; transition: color var(--ease-fast); }
|
| 536 |
+
.live-feedback-text { font-size: 18px; color: var(--text-secondary); max-width: 700px; text-align: center; line-height: 1.6; min-height: 60px; font-weight: 500; padding: 0 20px; transition: opacity var(--ease-smooth); }
|
| 537 |
|
| 538 |
.live-control-deck { display: flex; gap: 24px; margin-top: 50px; }
|
| 539 |
.btn-live-deck { width: 80px; height: 80px; border-radius: 50%; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--shadow-lg); transition: var(--ease-spring); }
|
| 540 |
.btn-live-deck.mic-control { background: var(--bg-primary); border: 3px solid var(--border-light); color: var(--text-primary); }
|
| 541 |
.btn-live-deck.mic-control:hover { transform: translateY(-6px); border-color: var(--brand-saffron); color: var(--brand-saffron); box-shadow: var(--shadow-xl); }
|
| 542 |
.btn-live-deck.mic-control.is-recording { background: var(--text-error); color: white; border-color: var(--text-error); animation: pulseMicLive 1.2s infinite; }
|
| 543 |
+
@keyframes pulseMicLive { 0% { box-shadow: 0 0 0 0 rgba(239,68,68,0.6); } 70% { box-shadow: 0 0 0 25px rgba(239,68,68,0); } 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); } }
|
| 544 |
|
| 545 |
/* Scroll Trigger Button */
|
| 546 |
.btn-scroll-trigger { position: absolute; bottom: 150px; left: 50%; width: 48px; height: 48px; border-radius: 50%; background: var(--bg-glass); backdrop-filter: blur(15px); border: 1px solid var(--border-light); box-shadow: var(--shadow-lg); display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); opacity: 0; pointer-events: none; transform: translateX(-50%) translateY(20px); transition: var(--ease-smooth); z-index: 20; }
|
|
|
|
| 553 |
.secure-modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); backdrop-filter: blur(10px); z-index: var(--z-modal); display: none; justify-content: center; align-items: center; animation: fadeIn 0.3s ease; }
|
| 554 |
.secure-modal-surface { background: var(--bg-primary); padding: 44px; border-radius: var(--radius-xl); width: 90%; max-width: 460px; box-shadow: var(--shadow-xl); position: relative; border: 1px solid rgba(255,255,255,0.4); }
|
| 555 |
.btn-modal-dismiss { position: absolute; top: 24px; right: 24px; background: var(--bg-secondary); border: none; width: 36px; height: 36px; border-radius: 50%; font-size: 18px; color: var(--text-secondary); cursor: pointer; transition: 0.2s; display: flex; align-items: center; justify-content: center; }
|
| 556 |
+
.btn-modal-dismiss:hover { background: #fee2e2; color: var(--text-error); transform: rotate(90deg); }
|
| 557 |
.modal-brand-header { text-align: center; margin-bottom: 36px; font-family: var(--font-sans); font-size: 28px; font-weight: 900; color: var(--text-primary); letter-spacing: -0.5px; }
|
| 558 |
|
| 559 |
.auth-tab-switch { display: flex; background: var(--bg-secondary); padding: 6px; border-radius: var(--radius-md); margin-bottom: 28px; }
|
|
|
|
| 607 |
|
| 608 |
.input-tools-row { flex-wrap: wrap; }
|
| 609 |
.main-textarea { min-width: 100%; order: -1; margin-bottom: 10px; }
|
| 610 |
+
.interim-text-overlay { left: 15px; top: 12px; }
|
| 611 |
}
|
| 612 |
</style>
|
| 613 |
</head>
|
| 614 |
<body>
|
| 615 |
|
| 616 |
+
<!-- =========================================================================================
|
| 617 |
+
DOM STRUCTURE: OFFLINE BANNER
|
| 618 |
+
========================================================================================= -->
|
| 619 |
+
<div id="networkStatusBanner" class="network-offline-banner">
|
| 620 |
+
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path><line x1="12" y1="9" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line></svg>
|
| 621 |
+
System Offline. Please check your internet connection.
|
| 622 |
+
</div>
|
| 623 |
+
|
| 624 |
<!-- =========================================================================================
|
| 625 |
DOM STRUCTURE: NOTIFICATIONS
|
| 626 |
========================================================================================= -->
|
|
|
|
| 687 |
<img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo">
|
| 688 |
<div class="brand-typography">
|
| 689 |
<span class="brand-name">ARJUN 2.O</span>
|
| 690 |
+
<span class="brand-edition">Enterprise</span>
|
| 691 |
</div>
|
| 692 |
</a>
|
| 693 |
<button class="btn-mobile-close" id="btnSidebarClose">
|
|
|
|
| 725 |
<!-- Wipe Memory Button -->
|
| 726 |
<button class="btn-erase-memory" id="btnClearMemory">
|
| 727 |
<svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
|
| 728 |
+
Wipe Neural Context
|
| 729 |
</button>
|
| 730 |
</div>
|
| 731 |
</aside>
|
|
|
|
| 739 |
<button class="btn-hamburger" id="btnSidebarOpen">
|
| 740 |
<svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2.5" fill="none"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>
|
| 741 |
</button>
|
| 742 |
+
<div class="system-status-pill" id="mainStatusPill">
|
| 743 |
<div class="pulse-dot"></div>
|
| 744 |
+
<span id="mainStatusText">System Online</span>
|
| 745 |
</div>
|
| 746 |
</div>
|
| 747 |
|
|
|
|
| 796 |
|
| 797 |
<!-- File Preview Component -->
|
| 798 |
<div class="upload-preview-bar" id="filePreviewBar">
|
| 799 |
+
<img class="upload-thumb" id="previewImgThumb" src="" alt="Thumbnail" style="display:none;">
|
| 800 |
<div class="upload-thumb" id="previewDocIcon" style="display:none;">📄</div>
|
| 801 |
<div class="upload-meta">
|
| 802 |
<div class="upload-filename" id="previewFileName">document.pdf</div>
|
|
|
|
| 805 |
File ready to be transmitted
|
| 806 |
</div>
|
| 807 |
</div>
|
| 808 |
+
<button class="btn-discard-upload" id="btnDiscardFile" title="Remove Attachment">
|
| 809 |
<svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
| 810 |
</button>
|
| 811 |
</div>
|
|
|
|
| 857 |
</button>
|
| 858 |
|
| 859 |
<!-- Expanding Text Area -->
|
| 860 |
+
<div style="flex:1; position: relative;">
|
| 861 |
+
<!-- STT Interim Feedback Overlay -->
|
| 862 |
+
<div id="interimTextOverlay" class="interim-text-overlay"></div>
|
| 863 |
+
<textarea class="main-textarea" id="mainChatInput" placeholder="Message Arjun Intelligence..." rows="1" style="width: 100%;"></textarea>
|
| 864 |
+
</div>
|
| 865 |
|
| 866 |
<!-- Master Send Button -->
|
| 867 |
<button class="btn-master-send" id="masterSendBtn">
|
|
|
|
| 891 |
</div>
|
| 892 |
</div>
|
| 893 |
<div class="workspace-editor-surface">
|
| 894 |
+
<div class="workspace-editor-content">
|
| 895 |
+
<div class="workspace-line-numbers" id="wsLineNumbers">1</div>
|
| 896 |
+
<pre><code id="workspaceCodeBlock" class="hljs"></code></pre>
|
| 897 |
+
</div>
|
| 898 |
</div>
|
| 899 |
</aside>
|
| 900 |
</div>
|
|
|
|
| 944 |
</div>
|
| 945 |
|
| 946 |
<!-- =========================================================================================
|
| 947 |
+
MASSIVE ENTERPRISE JAVASCRIPT ARCHITECTURE
|
| 948 |
========================================================================================= -->
|
|
|
|
|
|
|
|
|
|
| 949 |
<script>
|
| 950 |
// =======================================================================================
|
| 951 |
// 1. CONFIGURATION & STATE MANAGEMENT
|
|
|
|
| 955 |
LOGO_URL: "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png",
|
| 956 |
PDF_WORKER: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js',
|
| 957 |
API_ENDPOINT: '/api/chat', // Uses relative path to connect to Flask App.py
|
| 958 |
+
MAX_FILE_SIZE_MB: 10, // Prevent UI freeze on giant files
|
| 959 |
};
|
| 960 |
|
| 961 |
// Initialize PDF worker
|
|
|
|
| 973 |
isStreamingActive: false,
|
| 974 |
isLiveModeEngaged: false,
|
| 975 |
gradioClient: null,
|
| 976 |
+
isMicRecording: false,
|
| 977 |
+
isOnline: navigator.onLine
|
| 978 |
};
|
| 979 |
|
| 980 |
+
// Initialize Gradio Client Dynamically for TTS
|
| 981 |
window.initGradioEngine = async function() {
|
| 982 |
try {
|
| 983 |
const module = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js");
|
| 984 |
AppState.gradioClient = await module.Client.connect("Vedika66/Edge-TTS");
|
| 985 |
console.log("Neural TTS Engine connected successfully to Gradio.");
|
| 986 |
} catch(err) {
|
| 987 |
+
console.warn("Gradio Connection Warning:", err.message);
|
| 988 |
+
// Non-blocking failure, app still works
|
| 989 |
}
|
| 990 |
};
|
| 991 |
window.initGradioEngine();
|
|
|
|
| 994 |
// 2. UTILITY ENGINE (Robust Copy, Notifications, and Text Cleaners)
|
| 995 |
// =======================================================================================
|
| 996 |
const Utils = {
|
| 997 |
+
showToast: (message, type = 'info') => {
|
| 998 |
const container = document.getElementById('toast-container');
|
| 999 |
const toast = document.createElement('div');
|
| 1000 |
+
toast.className = `toast-notification ${type}`;
|
| 1001 |
+
|
| 1002 |
+
let icon = '<polyline points="20 6 9 17 4 12"></polyline>'; // Checkmark
|
| 1003 |
+
if (type === 'error') icon = '<line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line>';
|
| 1004 |
+
|
| 1005 |
+
toast.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">${icon}</svg> ${message}`;
|
| 1006 |
container.appendChild(toast);
|
| 1007 |
+
setTimeout(() => { if (toast.parentNode) toast.remove(); }, 3500);
|
| 1008 |
},
|
| 1009 |
|
| 1010 |
escapeHTML: (str) => {
|
| 1011 |
+
if (!str) return '';
|
| 1012 |
return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');
|
| 1013 |
},
|
| 1014 |
|
| 1015 |
+
// ✨ Intelligent Text Cleaner for TTS (Ignores code, links, images)
|
| 1016 |
cleanTextForVoice: (rawText) => {
|
| 1017 |
if(!rawText) return "";
|
| 1018 |
let cleaned = rawText;
|
| 1019 |
|
| 1020 |
+
// Remove Markdown Elements sequentially to prevent regex conflicts
|
| 1021 |
+
cleaned = cleaned.replace(/```[\s\S]*?```/g, ''); // Code blocks
|
| 1022 |
+
cleaned = cleaned.replace(/`.*?`/g, ''); // Inline code
|
| 1023 |
+
cleaned = cleaned.replace(/!\[.*?\]\(.*?\)/g, ''); // Images
|
| 1024 |
+
cleaned = cleaned.replace(/\[(.*?)\]\(.*?\)/g, '$1'); // Links
|
| 1025 |
+
cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, ''); // Think Tags
|
| 1026 |
+
cleaned = cleaned.replace(/https?:\/\/[^\s]+/g, ''); // Bare URLs
|
| 1027 |
+
cleaned = cleaned.replace(/[*_~#|-]/g, ''); // Formatting symbols
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1028 |
|
| 1029 |
return cleaned.trim();
|
| 1030 |
},
|
|
|
|
| 1034 |
const textArea = document.createElement("textarea");
|
| 1035 |
textArea.value = text;
|
| 1036 |
textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.position = "fixed";
|
| 1037 |
+
textArea.style.opacity = "0";
|
| 1038 |
document.body.appendChild(textArea);
|
| 1039 |
textArea.focus(); textArea.select();
|
| 1040 |
|
|
|
|
| 1042 |
const successful = document.execCommand('copy');
|
| 1043 |
if (successful) {
|
| 1044 |
const originalHTML = btnElement.innerHTML;
|
| 1045 |
+
btnElement.innerHTML = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied Data`;
|
| 1046 |
setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500);
|
| 1047 |
} else {
|
| 1048 |
+
Utils.showToast("Clipboard access denied by browser.", "error");
|
| 1049 |
}
|
| 1050 |
} catch (err) {
|
| 1051 |
+
Utils.showToast("System error copying text.", "error");
|
| 1052 |
}
|
| 1053 |
document.body.removeChild(textArea);
|
| 1054 |
},
|
|
|
|
| 1060 |
}
|
| 1061 |
navigator.clipboard.writeText(textToCopy).then(() => {
|
| 1062 |
const originalHTML = btnElement.innerHTML;
|
| 1063 |
+
btnElement.innerHTML = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied Data`;
|
| 1064 |
setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500);
|
| 1065 |
}).catch((err) => {
|
| 1066 |
console.warn('Modern API failed, utilizing fallback...', err);
|
|
|
|
| 1070 |
};
|
| 1071 |
|
| 1072 |
// =======================================================================================
|
| 1073 |
+
// 3. AUTHENTICATION & SYNC MANAGER
|
| 1074 |
// =======================================================================================
|
| 1075 |
const AuthManager = {
|
| 1076 |
initUI: () => {
|
|
|
|
| 1109 |
document.getElementById(nextId).classList.add('active');
|
| 1110 |
},
|
| 1111 |
|
|
|
|
| 1112 |
process: async (actionType) => {
|
| 1113 |
+
if (!AppState.isOnline) return Utils.showToast("System is offline. Cannot authenticate.", "error");
|
| 1114 |
+
|
| 1115 |
const payload = { action: actionType };
|
| 1116 |
let buttonId = '';
|
| 1117 |
|
|
|
|
| 1119 |
payload.name = document.getElementById('authRegName').value.trim();
|
| 1120 |
payload.email = document.getElementById('authRegEmail').value.trim();
|
| 1121 |
payload.phone = "0000000000"; payload.organization = "Arjun Base";
|
| 1122 |
+
if (!payload.email || !payload.name) return Utils.showToast("Identification details required.", "error");
|
| 1123 |
buttonId = 'btn-fire-reg-otp';
|
| 1124 |
}
|
| 1125 |
else if (actionType === 'login_send_otp') {
|
| 1126 |
payload.email = document.getElementById('authLogEmail').value.trim();
|
| 1127 |
+
if (!payload.email) return Utils.showToast("Email required for validation.", "error");
|
| 1128 |
buttonId = 'btn-fire-log-otp';
|
| 1129 |
}
|
| 1130 |
else if (actionType === 'register_verify' || actionType === 'login_verify') {
|
| 1131 |
payload.email = document.getElementById(actionType === 'register_verify' ? 'authRegEmail' : 'authLogEmail').value.trim();
|
| 1132 |
payload.otp = document.getElementById(actionType === 'register_verify' ? 'authRegOTP' : 'authLogOTP').value.trim();
|
| 1133 |
+
if (!payload.otp) return Utils.showToast("Security token required.", "error");
|
| 1134 |
buttonId = actionType === 'register_verify' ? 'btn-fire-reg-verify' : 'btn-fire-log-verify';
|
| 1135 |
}
|
| 1136 |
|
|
|
|
| 1140 |
triggerBtn.innerText = 'Establishing Connection...';
|
| 1141 |
|
| 1142 |
try {
|
|
|
|
| 1143 |
const response = await fetch(Config.GAS_URL, {
|
| 1144 |
method: 'POST',
|
| 1145 |
mode: 'cors',
|
|
|
|
| 1150 |
const result = await response.json();
|
| 1151 |
|
| 1152 |
if (result.status === 'success') {
|
| 1153 |
+
Utils.showToast(result.message, "success");
|
| 1154 |
if (actionType === 'register_send_otp') AuthManager.switchPhase('auth-reg-phase-1', 'auth-reg-phase-2');
|
| 1155 |
else if (actionType === 'login_send_otp') AuthManager.switchPhase('auth-log-phase-1', 'auth-log-phase-2');
|
| 1156 |
else if (actionType === 'register_verify' || actionType === 'login_verify') {
|
|
|
|
| 1161 |
ChatEngine.syncHistoryFromCloud();
|
| 1162 |
}
|
| 1163 |
} else {
|
| 1164 |
+
Utils.showToast(result.message, "error");
|
| 1165 |
}
|
| 1166 |
} catch (error) {
|
| 1167 |
console.error("Auth Network Error: ", error);
|
| 1168 |
+
Utils.showToast("Encrypted gateway error. Check network.", "error");
|
| 1169 |
}
|
| 1170 |
|
| 1171 |
triggerBtn.disabled = false;
|
|
|
|
| 1214 |
|
| 1215 |
toggleAttachMenu: () => {
|
| 1216 |
document.getElementById('attachDropdownMenu').classList.toggle('visible');
|
| 1217 |
+
},
|
| 1218 |
+
|
| 1219 |
+
updateNetworkStatus: () => {
|
| 1220 |
+
AppState.isOnline = navigator.onLine;
|
| 1221 |
+
const banner = document.getElementById('networkStatusBanner');
|
| 1222 |
+
const pill = document.getElementById('mainStatusPill');
|
| 1223 |
+
const pillText = document.getElementById('mainStatusText');
|
| 1224 |
+
|
| 1225 |
+
if (AppState.isOnline) {
|
| 1226 |
+
banner.classList.remove('active');
|
| 1227 |
+
pill.classList.remove('offline');
|
| 1228 |
+
pillText.innerText = 'System Online';
|
| 1229 |
+
if(!AppState.isProcessing && AppState.history.length > 0) {
|
| 1230 |
+
Utils.showToast("Network connection restored.", "success");
|
| 1231 |
+
}
|
| 1232 |
+
} else {
|
| 1233 |
+
banner.classList.add('active');
|
| 1234 |
+
pill.classList.add('offline');
|
| 1235 |
+
pillText.innerText = 'System Offline';
|
| 1236 |
+
}
|
| 1237 |
}
|
| 1238 |
};
|
| 1239 |
|
| 1240 |
// =======================================================================================
|
| 1241 |
+
// 5. SPEECH & TTS ENGINE (FIXED STT DUPLICATION & PREMATURE DISPATCH)
|
| 1242 |
// =======================================================================================
|
| 1243 |
const VoiceEngine = {
|
| 1244 |
recognition: null,
|
| 1245 |
currentLanguage: 'en-IN',
|
| 1246 |
+
baseInputText: '', // ✨ KEY FIX: Stores text existing BEFORE mic turns on
|
| 1247 |
+
silenceTimer: null, // ✨ KEY FIX: Detects silence for Live Mode dispatch
|
| 1248 |
+
|
| 1249 |
init: () => {
|
| 1250 |
const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
|
| 1251 |
+
if (!SR) {
|
| 1252 |
+
console.warn("Speech Recognition API not supported in this browser.");
|
| 1253 |
+
document.getElementById('masterVoiceBtn').style.display = 'none';
|
| 1254 |
+
return;
|
| 1255 |
+
}
|
| 1256 |
|
| 1257 |
VoiceEngine.recognition = new SR();
|
| 1258 |
+
// 'continuous = true' prevents random stops while user is speaking
|
| 1259 |
VoiceEngine.recognition.continuous = true;
|
| 1260 |
VoiceEngine.recognition.interimResults = true;
|
| 1261 |
VoiceEngine.recognition.lang = VoiceEngine.currentLanguage;
|
|
|
|
| 1263 |
VoiceEngine.recognition.onstart = () => {
|
| 1264 |
AppState.isMicRecording = true;
|
| 1265 |
document.getElementById('masterVoiceBtn').classList.add('active-listening');
|
| 1266 |
+
|
| 1267 |
+
// SAVE BASE TEXT: So we don't blindly append duplicates
|
| 1268 |
+
VoiceEngine.baseInputText = document.getElementById('mainChatInput').value;
|
| 1269 |
+
|
| 1270 |
if (AppState.isLiveModeEngaged) {
|
| 1271 |
+
VoiceEngine.baseInputText = ''; // Clear for live mode
|
| 1272 |
document.getElementById('liveMicBtn').classList.add('is-recording');
|
| 1273 |
document.getElementById('liveStatusText').innerText = "Listening...";
|
| 1274 |
document.getElementById('liveOrb').classList.remove('speaking');
|
|
|
|
| 1277 |
};
|
| 1278 |
|
| 1279 |
VoiceEngine.recognition.onresult = (event) => {
|
| 1280 |
+
// Clear the silence timer because the user is currently generating sound
|
| 1281 |
+
if (VoiceEngine.silenceTimer) clearTimeout(VoiceEngine.silenceTimer);
|
| 1282 |
+
|
| 1283 |
+
let finalSpeech = '';
|
| 1284 |
+
let interimSpeech = '';
|
| 1285 |
|
| 1286 |
+
// REBUILD the entire speech buffer from index 0. This PREVENTS duplicates
|
| 1287 |
+
// entirely because we construct the absolute truth string every tick.
|
| 1288 |
+
for (let i = 0; i < event.results.length; ++i) {
|
| 1289 |
if (event.results[i].isFinal) {
|
| 1290 |
+
finalSpeech += event.results[i].transcript;
|
| 1291 |
} else {
|
| 1292 |
+
interimSpeech += event.results[i].transcript;
|
| 1293 |
}
|
| 1294 |
}
|
| 1295 |
|
| 1296 |
+
// Clean formatting
|
| 1297 |
+
let totalNewSpeech = (finalSpeech + interimSpeech).replace(/\s+/g, ' ');
|
| 1298 |
+
if (totalNewSpeech.length > 0) {
|
| 1299 |
+
totalNewSpeech = totalNewSpeech.charAt(0).toUpperCase() + totalNewSpeech.slice(1);
|
| 1300 |
+
}
|
| 1301 |
+
|
| 1302 |
+
if (AppState.isLiveModeEngaged) {
|
| 1303 |
+
// Update UI for Live Mode
|
| 1304 |
+
document.getElementById('liveTranscriptText').innerText = totalNewSpeech ? `"${totalNewSpeech}"` : "Listening...";
|
| 1305 |
+
document.getElementById('mainChatInput').value = totalNewSpeech;
|
| 1306 |
|
| 1307 |
+
// ✨ VAD / SILENCE DETECTOR: Wait 1.8 seconds of complete silence before dispatching
|
| 1308 |
+
VoiceEngine.silenceTimer = setTimeout(() => {
|
| 1309 |
+
if (totalNewSpeech.trim().length > 0) {
|
| 1310 |
+
VoiceEngine.recognition.stop();
|
| 1311 |
+
// stop() will naturally trigger onend, which handles dispatch
|
| 1312 |
+
}
|
| 1313 |
+
}, 1800);
|
| 1314 |
+
|
| 1315 |
+
} else {
|
| 1316 |
+
// Regular chat mode: append intelligently to base text
|
| 1317 |
+
const separator = (VoiceEngine.baseInputText && totalNewSpeech && !VoiceEngine.baseInputText.endsWith(' ')) ? ' ' : '';
|
| 1318 |
+
const combinedText = VoiceEngine.baseInputText + separator + totalNewSpeech;
|
| 1319 |
+
|
| 1320 |
+
document.getElementById('mainChatInput').value = combinedText;
|
| 1321 |
+
UIManager.autoGrowTextArea(document.getElementById('mainChatInput'));
|
| 1322 |
+
|
| 1323 |
+
// Show visual feedback for interim vs final
|
| 1324 |
+
const interimOverlay = document.getElementById('interimTextOverlay');
|
| 1325 |
+
if (interimSpeech.length > 0) {
|
| 1326 |
+
interimOverlay.innerText = interimSpeech;
|
| 1327 |
+
interimOverlay.classList.add('active');
|
| 1328 |
} else {
|
| 1329 |
+
interimOverlay.classList.remove('active');
|
|
|
|
|
|
|
| 1330 |
}
|
| 1331 |
}
|
| 1332 |
};
|
|
|
|
| 1334 |
VoiceEngine.recognition.onend = () => {
|
| 1335 |
AppState.isMicRecording = false;
|
| 1336 |
document.getElementById('masterVoiceBtn').classList.remove('active-listening');
|
| 1337 |
+
document.getElementById('interimTextOverlay').classList.remove('active');
|
| 1338 |
|
| 1339 |
+
if (AppState.isLiveModeEngaged) {
|
| 1340 |
document.getElementById('liveMicBtn').classList.remove('is-recording');
|
|
|
|
| 1341 |
document.getElementById('liveOrb').style.transform = "scale(1)";
|
| 1342 |
+
|
| 1343 |
+
const currentInput = document.getElementById('mainChatInput').value.trim();
|
| 1344 |
+
if (currentInput !== '' && !AppState.isProcessing) {
|
| 1345 |
+
// The silence timer finished, user stopped talking, auto-dispatch!
|
| 1346 |
+
document.getElementById('liveStatusText').innerText = "Processing...";
|
| 1347 |
+
ChatEngine.initiateDispatch();
|
| 1348 |
+
} else if (!AppState.isProcessing) {
|
| 1349 |
+
document.getElementById('liveStatusText').innerText = "Tap mic to interact";
|
| 1350 |
+
}
|
| 1351 |
}
|
| 1352 |
};
|
| 1353 |
|
| 1354 |
VoiceEngine.recognition.onerror = (event) => {
|
| 1355 |
AppState.isMicRecording = false;
|
| 1356 |
+
if(event.error !== 'no-speech' && event.error !== 'aborted') {
|
| 1357 |
+
Utils.showToast("Voice module error: " + event.error, "error");
|
| 1358 |
}
|
| 1359 |
document.getElementById('masterVoiceBtn').classList.remove('active-listening');
|
| 1360 |
+
document.getElementById('interimTextOverlay').classList.remove('active');
|
| 1361 |
+
|
| 1362 |
if (AppState.isLiveModeEngaged) {
|
| 1363 |
document.getElementById('liveMicBtn').classList.remove('is-recording');
|
| 1364 |
document.getElementById('liveStatusText').innerText = "Audio Failure. Retry.";
|
|
|
|
| 1374 |
VoiceEngine.currentLanguage = 'hi-IN';
|
| 1375 |
badge.innerText = 'हि';
|
| 1376 |
btn.classList.add('hindi-active');
|
| 1377 |
+
Utils.showToast("Listening Protocol: Hindi (देवनागरी)", "success");
|
| 1378 |
} else {
|
| 1379 |
VoiceEngine.currentLanguage = 'en-IN';
|
| 1380 |
badge.innerText = 'EN';
|
| 1381 |
btn.classList.remove('hindi-active');
|
| 1382 |
+
Utils.showToast("Listening Protocol: English", "success");
|
| 1383 |
}
|
| 1384 |
|
| 1385 |
if (VoiceEngine.recognition) {
|
| 1386 |
VoiceEngine.recognition.lang = VoiceEngine.currentLanguage;
|
| 1387 |
if(AppState.isMicRecording) {
|
| 1388 |
VoiceEngine.recognition.stop();
|
| 1389 |
+
// Slight delay to allow system to halt before restarting
|
| 1390 |
+
setTimeout(() => { try{VoiceEngine.recognition.start();}catch(e){} }, 400);
|
| 1391 |
}
|
| 1392 |
}
|
| 1393 |
},
|
| 1394 |
|
| 1395 |
toggleDictation: () => {
|
| 1396 |
+
if (!VoiceEngine.recognition) return Utils.showToast("Hardware dictation unsupported in this browser.", "error");
|
| 1397 |
try {
|
| 1398 |
if(AppState.isMicRecording) {
|
| 1399 |
VoiceEngine.recognition.stop();
|
|
|
|
| 1403 |
} catch (e) { console.error("Mic Start Error", e); }
|
| 1404 |
},
|
| 1405 |
|
|
|
|
| 1406 |
executeTTS: async (rawText) => {
|
| 1407 |
// Apply smart text cleaner so AI doesn't read code or image links
|
| 1408 |
const cleanSpeechText = Utils.cleanTextForVoice(rawText);
|
| 1409 |
|
| 1410 |
if(!cleanSpeechText.trim()) {
|
| 1411 |
+
Utils.showToast("No readable conversational text found in this block.", "error");
|
| 1412 |
return;
|
| 1413 |
}
|
| 1414 |
|
|
|
|
| 1419 |
|
| 1420 |
try {
|
| 1421 |
if(!AppState.gradioClient) {
|
|
|
|
| 1422 |
const module = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js");
|
| 1423 |
AppState.gradioClient = await module.Client.connect("Vedika66/Edge-TTS");
|
| 1424 |
}
|
|
|
|
| 1428 |
voice_type: "प्रभात (भारी और प्राकृतिक आवाज़ - हिंग्लिश के लिए)"
|
| 1429 |
});
|
| 1430 |
|
| 1431 |
+
if (result.data && result.data) {
|
| 1432 |
+
const audioUrl = typeof result.data === 'string' ? result.data : result.data.url;
|
| 1433 |
const audio = new Audio(audioUrl);
|
| 1434 |
|
| 1435 |
audio.onended = () => {
|
|
|
|
| 1444 |
throw new Error("No audio payload returned.");
|
| 1445 |
}
|
| 1446 |
} catch (error) {
|
| 1447 |
+
Utils.showToast("Voice Neural Matrix is currently overloaded.", "error");
|
| 1448 |
console.error("Gradio TTS Error:", error);
|
| 1449 |
if (AppState.isLiveModeEngaged) {
|
| 1450 |
document.getElementById('liveOrb').classList.remove('speaking');
|
|
|
|
| 1463 |
if (VoiceEngine.recognition && !AppState.isMicRecording) {
|
| 1464 |
setTimeout(() => {
|
| 1465 |
try { VoiceEngine.recognition.start(); } catch(e){}
|
| 1466 |
+
}, 600);
|
| 1467 |
}
|
| 1468 |
},
|
| 1469 |
terminate: () => {
|
|
|
|
| 1476 |
};
|
| 1477 |
|
| 1478 |
// =======================================================================================
|
| 1479 |
+
// 6. FILE & DOCUMENT PROCESSOR (Robust & Size Validated)
|
| 1480 |
// =======================================================================================
|
| 1481 |
const FileProcessor = {
|
| 1482 |
triggerFileSelector: (type) => {
|
|
|
|
| 1485 |
},
|
| 1486 |
|
| 1487 |
processFile: async (inputElement, typeContext) => {
|
| 1488 |
+
const file = inputElement.files;
|
| 1489 |
if (!file) return;
|
| 1490 |
|
| 1491 |
+
// Size Validation to prevent UI thread lock
|
| 1492 |
+
const fileSizeMB = file.size / (1024 * 1024);
|
| 1493 |
+
if (fileSizeMB > Config.MAX_FILE_SIZE_MB) {
|
| 1494 |
+
Utils.showToast(`File exceeds ${Config.MAX_FILE_SIZE_MB}MB enterprise limit.`, "error");
|
| 1495 |
+
inputElement.value = '';
|
| 1496 |
+
return;
|
| 1497 |
+
}
|
| 1498 |
+
|
| 1499 |
const fName = file.name.toLowerCase();
|
| 1500 |
const mime = file.type;
|
| 1501 |
|
| 1502 |
+
if (typeContext === 'image' && !mime.startsWith('image/')) { Utils.showToast("Visual media required.", "error"); inputElement.value=''; return; }
|
| 1503 |
+
if (typeContext === 'video' && !mime.startsWith('video/')) { Utils.showToast("Video media required.", "error"); inputElement.value=''; return; }
|
| 1504 |
+
if (typeContext === 'audio' && !mime.startsWith('audio/')) { Utils.showToast("Audio media required.", "error"); inputElement.value=''; return; }
|
| 1505 |
|
| 1506 |
+
Utils.showToast(`Analyzing ${file.name}...`, "info");
|
| 1507 |
|
| 1508 |
const thumbImg = document.getElementById('previewImgThumb');
|
| 1509 |
const docIcon = document.getElementById('previewDocIcon');
|
|
|
|
| 1517 |
|
| 1518 |
if (typeContext === 'image') {
|
| 1519 |
fileReader.onload = (e) => {
|
| 1520 |
+
AppState.pendingAttachment = { type: 'image', data: e.target.result.split(','), name: file.name };
|
| 1521 |
thumbImg.src = e.target.result;
|
| 1522 |
thumbImg.style.display = 'flex';
|
| 1523 |
};
|
|
|
|
| 1525 |
}
|
| 1526 |
else if (typeContext === 'video' || typeContext === 'audio') {
|
| 1527 |
fileReader.onload = (e) => {
|
| 1528 |
+
AppState.pendingAttachment = { type: typeContext, data: e.target.result.split(','), name: file.name };
|
| 1529 |
docIcon.innerText = typeContext === 'video' ? '🎥' : '🎵';
|
| 1530 |
docIcon.style.display = 'flex';
|
| 1531 |
};
|
|
|
|
| 1547 |
extractedText += content.items.map(s => s.str).join(' ') + "\n";
|
| 1548 |
}
|
| 1549 |
AppState.pendingAttachment = { type: 'text', data: extractedText.trim() || "No readable text found.", name: file.name };
|
| 1550 |
+
Utils.showToast("Document payload secured.", "success");
|
| 1551 |
} catch (e) {
|
| 1552 |
+
Utils.showToast("PDF architecture parsing failed.", "error");
|
| 1553 |
FileProcessor.discardFile();
|
| 1554 |
}
|
| 1555 |
};
|
|
|
|
| 1561 |
const buffer = this.result;
|
| 1562 |
const resultData = await mammoth.extractRawText({ arrayBuffer: buffer });
|
| 1563 |
AppState.pendingAttachment = { type: 'text', data: resultData.value.trim() || "No readable text found.", name: file.name };
|
| 1564 |
+
Utils.showToast("Document payload secured.", "success");
|
| 1565 |
} catch(e) {
|
| 1566 |
+
Utils.showToast("Document architecture parsing failed.", "error");
|
| 1567 |
FileProcessor.discardFile();
|
| 1568 |
}
|
| 1569 |
};
|
|
|
|
| 1572 |
else {
|
| 1573 |
fileReader.onload = (e) => {
|
| 1574 |
AppState.pendingAttachment = { type: 'text', data: e.target.result, name: file.name };
|
| 1575 |
+
Utils.showToast("File payload secured.", "success");
|
| 1576 |
};
|
| 1577 |
fileReader.readAsText(file);
|
| 1578 |
}
|
|
|
|
| 1583 |
discardFile: () => {
|
| 1584 |
AppState.pendingAttachment = null;
|
| 1585 |
document.getElementById('filePreviewBar').classList.remove('visible');
|
| 1586 |
+
document.getElementById('previewImgThumb').src = '';
|
| 1587 |
}
|
| 1588 |
};
|
| 1589 |
|
| 1590 |
// =======================================================================================
|
| 1591 |
+
// 7. WORKSPACE ENGINE & ADVANCED MARKDOWN RENDERING
|
| 1592 |
// =======================================================================================
|
| 1593 |
const WorkspaceEngine = {
|
| 1594 |
launch: (codeBlockId) => {
|
| 1595 |
AppState.currentWorkspaceId = codeBlockId;
|
| 1596 |
const codeData = AppState.generatedCodeBlocks[codeBlockId];
|
| 1597 |
|
| 1598 |
+
document.getElementById('wsTitleText').innerText = codeData.lang ? `Generated ${codeData.lang.toUpperCase()}` : "Compiled Logic";
|
| 1599 |
const blockDisplay = document.getElementById('workspaceCodeBlock');
|
| 1600 |
blockDisplay.className = `hljs language-${codeData.lang || 'plaintext'}`;
|
| 1601 |
blockDisplay.textContent = codeData.code;
|
| 1602 |
|
| 1603 |
hljs.highlightElement(blockDisplay);
|
| 1604 |
+
|
| 1605 |
+
// Add Line Numbers dynamically
|
| 1606 |
+
const lineCount = codeData.code.split('\n').length;
|
| 1607 |
+
let numbersHTML = '';
|
| 1608 |
+
for(let i=1; i<=lineCount; i++) { numbersHTML += `${i}<br>`; }
|
| 1609 |
+
document.getElementById('wsLineNumbers').innerHTML = numbersHTML;
|
| 1610 |
+
|
| 1611 |
document.getElementById('workspacePanel').classList.add('expanded');
|
| 1612 |
|
| 1613 |
if (window.innerWidth <= 1024) {
|
|
|
|
| 1625 |
}
|
| 1626 |
};
|
| 1627 |
|
| 1628 |
+
// Custom Marked Renderer for UI perfection
|
| 1629 |
const MD_Renderer = new marked.Renderer();
|
| 1630 |
MD_Renderer.code = function(token) {
|
| 1631 |
const rawCode = typeof token === 'string' ? token : (token.text || '');
|
| 1632 |
+
const langClass = typeof token === 'string' ? arguments : (token.lang || '');
|
| 1633 |
|
| 1634 |
if (AppState.isStreamingActive) {
|
| 1635 |
return `
|
| 1636 |
<div class="streaming-code-container">
|
| 1637 |
<div class="streaming-code-header">
|
| 1638 |
<div class="loading-ring"></div>
|
| 1639 |
+
Generating Secure Architecture...
|
| 1640 |
</div>
|
| 1641 |
<pre><code class="language-${langClass}">${Utils.escapeHTML(rawCode)}</code></pre>
|
| 1642 |
</div>`;
|
|
|
|
| 1653 |
</div>
|
| 1654 |
<div class="ws-card-details">
|
| 1655 |
<h4>Code Module Compiled</h4>
|
| 1656 |
+
<p>${langClass || 'Syntax Module'}</p>
|
| 1657 |
</div>
|
| 1658 |
</div>
|
| 1659 |
<button class="btn-launch-workspace btn-launch-ws" data-id="${internalId}">Open Workspace</button>
|
| 1660 |
</div>`;
|
| 1661 |
};
|
|
|
|
| 1662 |
|
| 1663 |
+
MD_Renderer.table = function(token) {
|
| 1664 |
+
let html = '<table>';
|
| 1665 |
+
if (token.header) {
|
| 1666 |
+
html += '<thead><tr>';
|
| 1667 |
+
token.header.forEach(th => { html += `<th>${marked.parseInline(th.text)}</th>`; });
|
| 1668 |
+
html += '</tr></thead>';
|
| 1669 |
+
}
|
| 1670 |
+
if (token.rows) {
|
| 1671 |
+
html += '<tbody>';
|
| 1672 |
+
token.rows.forEach(tr => {
|
| 1673 |
+
html += '<tr>';
|
| 1674 |
+
tr.forEach(td => { html += `<td>${marked.parseInline(td.text)}</td>`; });
|
| 1675 |
+
html += '</tr>';
|
| 1676 |
+
});
|
| 1677 |
+
html += '</tbody>';
|
| 1678 |
+
}
|
| 1679 |
+
html += '</table>';
|
| 1680 |
+
return html;
|
| 1681 |
+
};
|
| 1682 |
+
|
| 1683 |
+
marked.use({ renderer: MD_Renderer, breaks: true, gfm: true });
|
| 1684 |
+
|
| 1685 |
+
// Event delegation for dynamically generated workspace buttons
|
| 1686 |
document.getElementById('msgs').addEventListener('click', function(e) {
|
| 1687 |
if (e.target && e.target.classList.contains('btn-launch-ws')) {
|
| 1688 |
const blockId = e.target.getAttribute('data-id');
|
|
|
|
| 1707 |
},
|
| 1708 |
|
| 1709 |
clearMemory: async () => {
|
| 1710 |
+
if(!confirm("Are you sure you want to wipe the neural context? This action cannot be undone.")) return;
|
| 1711 |
+
|
| 1712 |
if (AppState.currentUser) {
|
| 1713 |
+
Utils.showToast("Purging neural link memory...", "info");
|
| 1714 |
try {
|
| 1715 |
await fetch(Config.GAS_URL, {
|
| 1716 |
method: 'POST',
|
|
|
|
| 1724 |
},
|
| 1725 |
|
| 1726 |
syncHistoryFromCloud: async () => {
|
| 1727 |
+
if (!AppState.isOnline) return;
|
| 1728 |
+
|
| 1729 |
const messageZone = document.getElementById('msgs');
|
| 1730 |
document.getElementById('welcomeState').style.display = 'none';
|
| 1731 |
messageZone.innerHTML = '<div style="text-align:center; padding:80px; font-weight:800; color:var(--brand-saffron); font-size:18px;">Decrypting Historic Archives...</div>';
|
|
|
|
| 1761 |
},
|
| 1762 |
|
| 1763 |
saveHistoryToCloud: () => {
|
| 1764 |
+
if (AppState.currentUser && AppState.isOnline) {
|
| 1765 |
fetch(Config.GAS_URL, {
|
| 1766 |
method: 'POST',
|
| 1767 |
mode: 'cors',
|
|
|
|
| 1774 |
buildPreviewHTML: (attachmentObj) => {
|
| 1775 |
if (!attachmentObj) return '';
|
| 1776 |
if (attachmentObj.type === 'image') {
|
| 1777 |
+
return `<img src="data:image/jpeg;base64,${attachmentObj.data}" class="chat-in-bubble-img" alt="Attached Media">`;
|
| 1778 |
} else if (attachmentObj.type === 'video') {
|
| 1779 |
+
return `<div class="chat-file-card"><div class="icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><polygon points="23 7 16 12 23 17 23 7"></polygon><rect x="1" y="5" width="15" height="14" rx="2" ry="2"></rect></svg></div><span class="name">${Utils.escapeHTML(attachmentObj.name)}</span></div>`;
|
| 1780 |
} else if (attachmentObj.type === 'audio') {
|
| 1781 |
+
return `<div class="chat-file-card"><div class="icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 18V5l12-2v13"></path><circle cx="6" cy="18" r="3"></circle><circle cx="18" cy="16" r="3"></circle></svg></div><span class="name">${Utils.escapeHTML(attachmentObj.name)}</span></div>`;
|
| 1782 |
} else {
|
| 1783 |
+
return `<div class="chat-file-card"><div class="icon"><svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path><polyline points="14 2 14 8 20 8"></polyline><line x1="16" y1="13" x2="8" y2="13"></line><line x1="16" y1="17" x2="8" y2="17"></line><polyline points="10 9 9 9 8 9"></polyline></svg></div><span class="name">${Utils.escapeHTML(attachmentObj.name)}</span></div>`;
|
| 1784 |
}
|
| 1785 |
},
|
| 1786 |
|
|
|
|
| 1790 |
let visibleText = textContent;
|
| 1791 |
if(textContent.includes('---DATA BEGIN---')) {
|
| 1792 |
const parts = textContent.split('User Prompt:');
|
| 1793 |
+
if(parts.length > 1) visibleText = parts.trim();
|
| 1794 |
}
|
| 1795 |
|
| 1796 |
const bubbleBox = document.createElement('div');
|
|
|
|
| 1835 |
let actionTray = bubbleDOM.querySelector('.bot-actions-row');
|
| 1836 |
if (!isCurrentlyStreaming && !actionTray && strippedMarkup.trim().length > 0) {
|
| 1837 |
const safeRawCopy = strippedMarkup.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, '');
|
| 1838 |
+
// Safe injection for TTS payload
|
| 1839 |
const safeRawTTS = strippedMarkup.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, ' ');
|
| 1840 |
|
| 1841 |
const actionsHTML = `
|
|
|
|
| 1857 |
},
|
| 1858 |
|
| 1859 |
initiateDispatch: async () => {
|
| 1860 |
+
if (!AppState.isOnline) return Utils.showToast("Cannot process request. System is Offline.", "error");
|
| 1861 |
if (AppState.isProcessing) return;
|
| 1862 |
|
| 1863 |
const inputField = document.getElementById('mainChatInput');
|
|
|
|
| 1869 |
if (!AppState.currentUser) {
|
| 1870 |
if (AppState.guestCount >= 3) {
|
| 1871 |
AuthManager.openModal();
|
| 1872 |
+
return Utils.showToast("Query limit reached. Secure Login required.", "error");
|
| 1873 |
}
|
| 1874 |
AppState.guestCount++;
|
| 1875 |
localStorage.setItem('arjun_guest', AppState.guestCount.toString());
|
| 1876 |
document.getElementById('guestCountDisplay').innerText = AppState.guestCount;
|
| 1877 |
}
|
| 1878 |
|
| 1879 |
+
// Prepare UI for Dispatch
|
| 1880 |
inputField.value = '';
|
| 1881 |
inputField.style.height = 'auto';
|
| 1882 |
document.getElementById('masterSendBtn').disabled = true;
|
|
|
|
| 1919 |
const activeBotBubble = ChatEngine.renderBotMessage('', false);
|
| 1920 |
UIManager.forceScrollDown();
|
| 1921 |
|
| 1922 |
+
// Dynamic Pollinations Seed Generator
|
| 1923 |
const cryptographicSeed = Math.floor(Math.random() * 999999999);
|
| 1924 |
|
| 1925 |
+
// Advanced Cognitive System Instructions
|
| 1926 |
+
let cognitivePrompt = `You are Arjun 2.O, a highly advanced enterprise AI system architected and engineered by Abhay Kumar.
|
| 1927 |
+
LANGUAGE PROTOCOL: Your primary logic is English. If the user interacts in Hindi/Hinglish, you must dynamically switch to a professional Hindi response. Never force translation on pure English prompts.
|
| 1928 |
|
| 1929 |
IMAGE SYNTHESIS OVERRIDE:
|
| 1930 |
When commanded to render, generate, or create an image/photograph, you MUST reply with a precise markdown image node targeting Pollinations AI.
|
| 1931 |
Formula: 
|
| 1932 |
Available Models: flux, flux-realism, flux-anime, flux-3d.
|
| 1933 |
+
CRITICAL: The seed parameter is mandatory to bypass cache mechanics. Always render this markdown node inline.`;
|
| 1934 |
|
| 1935 |
if (AppState.isLiveModeEngaged) {
|
| 1936 |
+
cognitivePrompt += `\n\n[LIVE ACOUSTIC OVERRIDE ACTIVE]: You are currently engaged in a LIVE, REAL-TIME VOCAL INTERFACE.
|
| 1937 |
MANDATORY direct answers. Maximum brevity. Eliminate complex markdown, massive code blocks, or extended lists. Replicate human telephonic brevity perfectly.`;
|
| 1938 |
}
|
| 1939 |
|
|
|
|
| 1944 |
attachments: payloadAttachments,
|
| 1945 |
system_prompt: cognitivePrompt,
|
| 1946 |
history: secureHistory,
|
| 1947 |
+
max_tokens: AppState.isLiveModeEngaged ? 150 : 4096,
|
| 1948 |
temperature: 0.75
|
| 1949 |
};
|
| 1950 |
|
|
|
|
| 1978 |
|
| 1979 |
try {
|
| 1980 |
const jsonSegment = JSON.parse(parseableStr);
|
| 1981 |
+
if (jsonSegment.choices && jsonSegment.choices.delta.content) {
|
| 1982 |
+
responseBuffer += jsonSegment.choices.delta.content;
|
| 1983 |
ChatEngine.updateBotBubble(activeBotBubble, responseBuffer, true);
|
| 1984 |
UIManager.autoScroll();
|
| 1985 |
}
|
|
|
|
| 2016 |
// 9. EVENT DELEGATION & BOOTSTRAP INVOCATION
|
| 2017 |
// =======================================================================================
|
| 2018 |
|
|
|
|
| 2019 |
window.startArjunApp = function() {
|
| 2020 |
document.getElementById('welcomeOverlay').classList.add('hidden');
|
| 2021 |
localStorage.setItem('arjun_welcome_seen', 'true');
|
| 2022 |
setTimeout(() => { document.getElementById('welcomeOverlay').style.display = 'none'; }, 400);
|
| 2023 |
};
|
| 2024 |
|
|
|
|
| 2025 |
document.addEventListener('DOMContentLoaded', () => {
|
| 2026 |
// Sidebar Controls
|
| 2027 |
document.getElementById('btnSidebarOpen').addEventListener('click', UIManager.toggleSidebar);
|
|
|
|
| 2083 |
document.getElementById('msgs').addEventListener('scroll', UIManager.checkScrollState);
|
| 2084 |
document.getElementById('scrollTriggerBtn').addEventListener('click', UIManager.forceScrollDown);
|
| 2085 |
|
| 2086 |
+
// Network Monitoring
|
| 2087 |
+
window.addEventListener('online', UIManager.updateNetworkStatus);
|
| 2088 |
+
window.addEventListener('offline', UIManager.updateNetworkStatus);
|
| 2089 |
+
|
| 2090 |
// Prevent unwanted default mobile behaviours on double-tap
|
| 2091 |
document.addEventListener('dblclick', function(event) {
|
| 2092 |
event.preventDefault();
|
|
|
|
| 2100 |
}
|
| 2101 |
|
| 2102 |
// Fire Boot Processes
|
| 2103 |
+
UIManager.updateNetworkStatus();
|
| 2104 |
VoiceEngine.init();
|
| 2105 |
AuthManager.initUI();
|
| 2106 |
if(AppState.currentUser) { ChatEngine.syncHistoryFromCloud(); }
|
|
|
|
| 2109 |
</script>
|
| 2110 |
</body>
|
| 2111 |
</html>
|
|
|