Spaces:
Sleeping
Sleeping
| <html lang="en" dir="ltr"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta http-equiv="X-UA-Compatible" content="IE=edge"> | |
| <!-- Viewport Settings: Disables zoom, removes blue tap highlights on mobile devices --> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"> | |
| <meta name="description" content="Arjun 2.O Ultimate Enterprise Artificial Intelligence System engineered by Abhay Kumar."> | |
| <meta name="theme-color" content="#FF9933"> | |
| <title>Arjun 2.O - Professional Advanced AI</title> | |
| <!-- ========================================================================================= | |
| [1] EXTERNAL RESOURCES, FONTS & LIBRARIES (बाहरी स्रोत) | |
| ========================================================================================= --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <!-- Professional Typography: Inter for UI, Geist Mono for Code, Instrument Serif for Headers --> | |
| <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600;700;800;900&family=Geist+Mono:wght@400;500;600;700&display=swap" rel="stylesheet"> | |
| <!-- Syntax Highlighting (Atom One Dark for Workspace Environment) --> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"> | |
| <!-- Data Extraction & Rendering Utilities --> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script> | |
| <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script> | |
| <!-- ========================================================================================= | |
| [2] MASSIVE ENTERPRISE CSS ARCHITECTURE (विस्तृत और उन्नत स्टाइलिंग) | |
| ========================================================================================= --> | |
| <style> | |
| /* --------------------------------------------------------------------------------------- | |
| A. CSS VARIABLES, DESIGN TOKENS & TIRANGA BRANDING | |
| --------------------------------------------------------------------------------------- */ | |
| :root { | |
| /* Brand Identity: Saffron, White, Green (Tiranga) */ | |
| --brand-saffron: #FF9933; | |
| --brand-saffron-light: rgba(255, 153, 51, 0.12); | |
| --brand-saffron-dark: #e68a2e; | |
| --brand-saffron-glow: rgba(255, 153, 51, 0.4); | |
| --brand-white: #FFFFFF; | |
| --brand-white-glass: rgba(255, 255, 255, 0.88); | |
| --brand-green: #138808; | |
| --brand-green-light: rgba(19, 136, 8, 0.12); | |
| --brand-green-dark: #0f6e06; | |
| --brand-green-glow: rgba(19, 136, 8, 0.4); | |
| /* Structural Colors & Backgrounds */ | |
| --bg-primary: #ffffff; | |
| --bg-secondary: #f8f9fa; | |
| --bg-tertiary: #f1f3f5; | |
| --bg-glass: rgba(255, 255, 255, 0.75); | |
| --bg-glass-heavy: rgba(255, 255, 255, 0.95); | |
| --bg-overlay: rgba(0, 0, 0, 0.5); | |
| /* Text & Typography Colors */ | |
| --text-primary: #111827; | |
| --text-secondary: #4b5563; | |
| --text-tertiary: #9ca3af; | |
| --text-inverse: #ffffff; | |
| --text-error: #ef4444; | |
| /* Borders & Dividers */ | |
| --border-light: rgba(0, 0, 0, 0.08); | |
| --border-medium: rgba(0, 0, 0, 0.15); | |
| --border-focus: rgba(255, 153, 51, 0.4); | |
| /* Shadows & Elevation */ | |
| --shadow-xs: 0 1px 2px rgba(0,0,0,0.04); | |
| --shadow-sm: 0 1px 3px rgba(0,0,0,0.05), 0 1px 2px rgba(0,0,0,0.03); | |
| --shadow-md: 0 4px 6px -1px rgba(0,0,0,0.05), 0 2px 4px -1px rgba(0,0,0,0.03); | |
| --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.05), 0 4px 6px -2px rgba(0,0,0,0.025); | |
| --shadow-xl: 0 20px 25px -5px rgba(0,0,0,0.05), 0 10px 10px -5px rgba(0,0,0,0.02); | |
| --shadow-inner: inset 0 2px 4px rgba(0,0,0,0.06); | |
| /* Font Families */ | |
| --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; | |
| --font-mono: 'Geist Mono', Consolas, Monaco, monospace; | |
| --font-serif: 'Instrument Serif', Georgia, serif; | |
| /* Radii */ | |
| --radius-xs: 4px; | |
| --radius-sm: 8px; | |
| --radius-md: 12px; | |
| --radius-lg: 16px; | |
| --radius-xl: 24px; | |
| --radius-full: 9999px; | |
| /* Animation Easing */ | |
| --ease-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1); | |
| --ease-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| --ease-spring: 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); | |
| /* Z-Indices */ | |
| --z-ambient: -1; | |
| --z-base: 1; | |
| --z-header: 10; | |
| --z-dock: 20; | |
| --z-workspace: 25; | |
| --z-dropdown: 50; | |
| --z-sidebar: 100; | |
| --z-modal: 5000; | |
| --z-toast: 9000; | |
| --z-live: 9999; | |
| } | |
| /* --------------------------------------------------------------------------------------- | |
| B. GLOBAL RESET & ANTI-BLUE-HIGHLIGHT (सम्पूर्ण यूज़र इंटरफ़ेस फिक्स) | |
| --------------------------------------------------------------------------------------- */ | |
| *, *::before, *::after { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| /* CRITICAL: REMOVE BLUE TAP HIGHLIGHT ON MOBILE/BROWSERS */ | |
| -webkit-tap-highlight-color: transparent ; | |
| -webkit-touch-callout: none ; | |
| } | |
| html { font-size: 16px; -webkit-text-size-adjust: 100%; scroll-behavior: smooth; } | |
| body { | |
| height: 100dvh; | |
| width: 100vw; | |
| overflow: hidden; | |
| background-color: var(--bg-primary); | |
| color: var(--text-primary); | |
| font-family: var(--font-sans); | |
| line-height: 1.6; | |
| -webkit-font-smoothing: antialiased; | |
| -moz-osx-font-smoothing: grayscale; | |
| } | |
| /* Remove default outlines and use custom focus states */ | |
| *:focus { outline: none ; } | |
| button, input, textarea, select { | |
| font-family: inherit; | |
| color: inherit; | |
| border: none; | |
| background: transparent; | |
| } | |
| button { cursor: pointer; user-select: none; } | |
| a { color: inherit; text-decoration: none; } | |
| img { max-width: 100%; height: auto; display: block; } | |
| svg { flex-shrink: 0; } | |
| /* Highly Customized Scrollbars */ | |
| ::-webkit-scrollbar { width: 6px; height: 6px; } | |
| ::-webkit-scrollbar-track { background: transparent; } | |
| ::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.12); border-radius: 10px; transition: background var(--ease-smooth); } | |
| ::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.25); } | |
| /* --------------------------------------------------------------------------------------- | |
| C. AMBIENT BACKGROUND SYSTEM (प्रीमियम बैकग्राउंड इफ़ेक्ट्स) | |
| --------------------------------------------------------------------------------------- */ | |
| .ambient-environment { | |
| position: fixed; inset: 0; z-index: var(--z-ambient); overflow: hidden; pointer-events: none; | |
| background: linear-gradient(135deg, var(--brand-saffron-light) 0%, var(--bg-primary) 50%, var(--brand-green-light) 100%); | |
| } | |
| .ambient-orb { | |
| position: absolute; border-radius: 50%; filter: blur(120px); opacity: 0.35; | |
| animation: orbFloat 40s ease-in-out infinite alternate; will-change: transform; | |
| } | |
| .orb-top-left { | |
| width: 650px; height: 650px; | |
| background: radial-gradient(circle, rgba(255,153,51,0.2) 0%, transparent 70%); | |
| top: -200px; left: -150px; | |
| } | |
| .orb-bottom-right { | |
| width: 550px; height: 550px; | |
| background: radial-gradient(circle, rgba(19,136,8,0.15) 0%, transparent 70%); | |
| bottom: -150px; right: -100px; animation-delay: -20s; | |
| } | |
| @keyframes orbFloat { | |
| 0% { transform: translate(0, 0) scale(1); } | |
| 33% { transform: translate(50px, 70px) scale(1.05); } | |
| 66% { transform: translate(-30px, 40px) scale(0.95); } | |
| 100% { transform: translate(60px, -20px) scale(1.1); } | |
| } | |
| /* --------------------------------------------------------------------------------------- | |
| D. TOAST NOTIFICATION ENGINE (सुंदर और स्पष्ट सूचनाएं) | |
| --------------------------------------------------------------------------------------- */ | |
| .toast-wrapper { | |
| position: absolute; top: 24px; left: 50%; transform: translateX(-50%); | |
| z-index: var(--z-toast); display: flex; flex-direction: column; gap: 12px; | |
| pointer-events: none; width: 90%; max-width: 400px; align-items: center; | |
| } | |
| .toast-notification { | |
| background: var(--text-primary); color: var(--text-inverse); padding: 14px 24px; | |
| border-radius: var(--radius-full); font-size: 14px; font-weight: 600; box-shadow: var(--shadow-xl); | |
| animation: toastSlideIn var(--ease-spring) forwards, toastFadeOut 0.4s 3.5s forwards; | |
| display: flex; align-items: center; gap: 10px; width: fit-content; max-width: 100%; word-break: break-word; | |
| border: 1px solid rgba(255,255,255,0.1); | |
| } | |
| .toast-notification svg { width: 18px; height: 18px; flex-shrink: 0; color: var(--brand-saffron); } | |
| @keyframes toastSlideIn { from { opacity: 0; transform: translateY(-30px) scale(0.9); } to { opacity: 1; transform: translateY(0) scale(1); } } | |
| @keyframes toastFadeOut { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-20px); } } | |
| /* --------------------------------------------------------------------------------------- | |
| E. INITIAL WELCOME OVERLAY (स्वागत स्क्रीन) | |
| --------------------------------------------------------------------------------------- */ | |
| .startup-overlay { | |
| position: fixed; inset: 0; background: var(--brand-white-glass); | |
| backdrop-filter: blur(25px); -webkit-backdrop-filter: blur(25px); z-index: calc(var(--z-toast) + 100); | |
| display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 24px; | |
| transition: opacity var(--ease-smooth), visibility var(--ease-smooth); | |
| } | |
| .startup-overlay.hidden { opacity: 0; visibility: hidden; pointer-events: none; } | |
| .startup-logo-container { position: relative; margin-bottom: 32px; animation: popIn var(--ease-spring) 0.3s both; } | |
| .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; } | |
| .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; } | |
| @keyframes pulseGlow { from { opacity: 0.3; transform: scale(0.95); } to { opacity: 0.6; transform: scale(1.05); } } | |
| @keyframes popIn { from { opacity: 0; transform: scale(0.8); } to { opacity: 1; transform: scale(1); } } | |
| .startup-title { font-family: var(--font-sans); font-size: 2.5rem; font-weight: 900; color: var(--text-primary); letter-spacing: -1px; margin-bottom: 16px; animation: slideUp var(--ease-smooth) 0.4s both; } | |
| .startup-desc { font-size: 1.1rem; color: var(--text-secondary); line-height: 1.6; max-width: 550px; margin-bottom: 40px; animation: slideUp var(--ease-smooth) 0.5s both; font-weight: 500; } | |
| .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.6s both; display: flex; align-items: center; gap: 12px; position: relative; overflow: hidden; } | |
| .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; } | |
| .btn-startup-launch:hover { background: #000000; transform: translateY(-4px) scale(1.02); box-shadow: 0 15px 35px rgba(0,0,0,0.25); } | |
| .btn-startup-launch:hover::before { left: 150%; } | |
| @keyframes slideUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } } | |
| /* --------------------------------------------------------------------------------------- | |
| F. CORE APPLICATION LAYOUT & SIDEBAR | |
| --------------------------------------------------------------------------------------- */ | |
| .app-root { position: relative; z-index: var(--z-base); display: flex; height: 100dvh; width: 100vw; overflow: hidden; background: var(--bg-glass); backdrop-filter: blur(30px); -webkit-backdrop-filter: blur(30px); } | |
| /* Sidebar Navigation System */ | |
| .sidebar-container { width: 320px; display: flex; flex-direction: column; background: rgba(248, 249, 250, 0.85); border-right: 1px solid var(--border-light); flex-shrink: 0; z-index: var(--z-sidebar); transition: transform var(--ease-smooth), box-shadow var(--ease-smooth); backdrop-filter: blur(15px); } | |
| .sidebar-header { padding: 28px 24px; border-bottom: 1px solid var(--border-light); display: flex; justify-content: space-between; align-items: center; } | |
| .brand-identity { display: flex; align-items: center; gap: 14px; text-decoration: none; } | |
| .brand-identity img { width: 50px; height: 50px; border-radius: var(--radius-md); object-fit: cover; box-shadow: var(--shadow-sm); border: 2px solid var(--bg-primary); transition: transform var(--ease-spring); } | |
| .brand-identity:hover img { transform: scale(1.08) rotate(-3deg); } | |
| .brand-typography { display: flex; flex-direction: column; } | |
| .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; } | |
| .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; } | |
| .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); } | |
| .btn-mobile-close:hover { background: rgba(239, 68, 68, 0.15); color: var(--text-error); transform: rotate(90deg); } | |
| .sidebar-body { flex: 1; overflow-y: auto; padding: 24px; display: flex; flex-direction: column; gap: 28px; } | |
| /* Advanced Auth Widget in Sidebar */ | |
| .auth-widget { background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 20px; box-shadow: var(--shadow-sm); transition: var(--ease-smooth); } | |
| .auth-widget:hover { box-shadow: var(--shadow-md); border-color: var(--border-medium); } | |
| .btn-secure-connect { width: 100%; padding: 14px; background: var(--text-primary); color: var(--text-inverse); border: none; border-radius: var(--radius-md); font-size: 14px; font-weight: 700; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 10px; transition: var(--ease-fast); } | |
| .btn-secure-connect:hover { background: #000000; transform: translateY(-2px); box-shadow: var(--shadow-md); } | |
| .user-profile-display { display: none; align-items: center; gap: 14px; } | |
| .avatar-badge { width: 46px; height: 46px; border-radius: var(--radius-full); background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); color: var(--brand-white); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 18px; box-shadow: var(--shadow-sm); border: 2px solid var(--bg-primary); text-shadow: 0 1px 2px rgba(0,0,0,0.3); } | |
| .user-details { flex: 1; overflow: hidden; } | |
| .user-email-id { font-size: 14px; font-weight: 700; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .user-clearance-level { font-size: 11px; color: var(--brand-green); font-weight: 600; margin-top: 2px; display: flex; align-items: center; gap: 4px; } | |
| .user-clearance-level::before { content: ''; display: block; width: 6px; height: 6px; border-radius: 50%; background: var(--brand-green); box-shadow: var(--shadow-glow-green); } | |
| .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); } | |
| .btn-system-logout:hover { background: var(--text-error); color: #ffffff; transform: scale(1.05); } | |
| /* Developer Box with Tiranga Accent */ | |
| .developer-credit-box { background: var(--bg-primary); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 28px 20px; text-align: center; box-shadow: var(--shadow-sm); margin-top: auto; position: relative; overflow: hidden; } | |
| .developer-credit-box::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 4px; background: linear-gradient(90deg, var(--brand-saffron), var(--brand-white), var(--brand-green)); } | |
| .developer-credit-box img { width: 72px; height: 72px; border-radius: var(--radius-lg); object-fit: cover; margin-bottom: 16px; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); margin-left: auto; margin-right: auto; transition: var(--ease-spring); } | |
| .developer-credit-box:hover img { transform: scale(1.05); } | |
| .dev-box-title { font-size: 18px; font-weight: 800; color: var(--text-primary); margin-bottom: 6px; } | |
| .dev-box-subtitle { font-size: 11px; font-weight: 800; color: var(--text-tertiary); letter-spacing: 1px; text-transform: uppercase; } | |
| /* Dangerous Action Button */ | |
| .btn-erase-memory { width: 100%; padding: 16px; border-radius: var(--radius-md); background: var(--bg-primary); border: 1px solid var(--border-light); color: var(--text-secondary); font-size: 14px; font-weight: 700; cursor: pointer; transition: var(--ease-fast); display: flex; align-items: center; justify-content: center; gap: 10px; } | |
| .btn-erase-memory:hover { background: #fee2e2; color: var(--text-error); border-color: #fca5a5; } | |
| /* --------------------------------------------------------------------------------------- | |
| G. MAIN CHAT AREA & HEADER | |
| --------------------------------------------------------------------------------------- */ | |
| .main-chat-wrapper { flex: 1; display: flex; flex-direction: column; position: relative; min-width: 320px; transition: width var(--ease-smooth); background: transparent; } | |
| .chat-top-bar { padding: 18px 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); } | |
| .top-bar-left { display: flex; align-items: center; gap: 16px; } | |
| .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); } | |
| .btn-hamburger:hover { background: var(--bg-secondary); } | |
| .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); } | |
| .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; } | |
| @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); } } | |
| /* DYNAMIC LIVE MODE ACTIVATOR */ | |
| .btn-activate-live { background: linear-gradient(135deg, #ef4444 0%, #b91c1c 100%); color: white; border: none; padding: 10px 22px; border-radius: var(--radius-full); font-size: 14px; font-weight: 800; display: flex; align-items: center; gap: 8px; cursor: pointer; box-shadow: 0 4px 15px rgba(239, 68, 68, 0.35); transition: var(--ease-spring); letter-spacing: 0.5px; } | |
| .btn-activate-live:hover { transform: translateY(-2px) scale(1.03); box-shadow: 0 8px 25px rgba(239, 68, 68, 0.45); } | |
| .btn-activate-live svg { width: 18px; height: 18px; animation: liveBlink 1.5s infinite; } | |
| @keyframes liveBlink { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } } | |
| /* Chat Scrollable Area */ | |
| .chat-scroll-area { flex: 1; overflow-y: auto; padding: 40px 10%; display: flex; flex-direction: column; gap: 36px; scroll-behavior: smooth; position: relative; } | |
| /* Internal Welcome Hero Box */ | |
| .chat-hero-section { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 60px 20px; margin: auto; animation: slideUpFade 0.7s var(--ease-spring) both; max-width: 750px; } | |
| .hero-avatar-wrapper { position: relative; margin-bottom: 36px; } | |
| .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; } | |
| .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;} | |
| @keyframes rotateGlow { 100% { transform: rotate(360deg); } } | |
| .hero-greeting { font-size: 42px; font-weight: 900; letter-spacing: -1.5px; margin-bottom: 18px; color: var(--text-primary); } | |
| .hero-description { font-size: 17px; color: var(--text-secondary); line-height: 1.6; margin-bottom: 40px; font-weight: 500; } | |
| /* RESTORED: GUEST BADGE LIMIT DISPLAY */ | |
| .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); } | |
| .guest-warning-badge svg { width: 18px; height: 18px; } | |
| .prompt-suggestions-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 20px; width: 100%; } | |
| .prompt-card { background: rgba(255, 255, 255, 0.7); backdrop-filter: blur(15px); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 24px; cursor: pointer; text-align: left; transition: var(--ease-smooth); box-shadow: var(--shadow-sm); position: relative; overflow: hidden; } | |
| .prompt-card::after { content: ''; position: absolute; top: 0; left: 0; width: 4px; height: 100%; background: var(--brand-saffron); opacity: 0; transition: var(--ease-fast); } | |
| .prompt-card:hover { transform: translateY(-5px); box-shadow: var(--shadow-lg); border-color: var(--brand-saffron); background: rgba(255, 255, 255, 0.95); } | |
| .prompt-card:hover::after { opacity: 1; } | |
| .prompt-icon { font-size: 28px; margin-bottom: 14px; display: inline-block; background: var(--bg-tertiary); padding: 10px; border-radius: var(--radius-md); } | |
| .prompt-title { font-size: 15px; font-weight: 800; color: var(--text-primary); margin-bottom: 8px; } | |
| .prompt-desc { font-size: 13px; color: var(--text-secondary); line-height: 1.5; } | |
| /* --------------------------------------------------------------------------------------- | |
| H. CHAT MESSAGE BUBBLES & PREVIEWS | |
| --------------------------------------------------------------------------------------- */ | |
| .chat-message-row { display: flex; gap: 20px; max-width: 88%; animation: messagePopIn 0.5s var(--ease-spring) both; } | |
| @keyframes messagePopIn { from { opacity: 0; transform: translateY(25px) scale(0.97); } to { opacity: 1; transform: none; } } | |
| .chat-message-row.user-msg { flex-direction: row-reverse; align-self: flex-end; } | |
| .chat-message-row.bot-msg { align-self: flex-start; } | |
| .msg-avatar { width: 42px; height: 42px; border-radius: var(--radius-md); flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 15px; font-weight: 800; box-shadow: var(--shadow-sm); } | |
| .user-msg .msg-avatar { background: var(--text-primary); color: var(--brand-white); } | |
| .bot-msg .msg-avatar { background: var(--bg-primary); border: 1px solid var(--border-light); padding: 3px; } | |
| .bot-msg .msg-avatar img { width: 100%; height: 100%; border-radius: 8px; object-fit: cover; } | |
| .msg-content-column { display: flex; flex-direction: column; gap: 8px; max-width: 100%; min-width: 0; } | |
| .user-msg .msg-content-column { align-items: flex-end; } | |
| .file-badge { font-size: 11px; font-weight: 700; color: var(--brand-saffron-dark); background: var(--brand-saffron-light); padding: 4px 12px; border-radius: var(--radius-full); margin-bottom: 4px; display: inline-flex; align-items: center; gap: 6px; border: 1px solid rgba(255,153,51,0.2); } | |
| .file-badge svg { width: 12px; height: 12px; } | |
| /* In-Bubble File Previews */ | |
| .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; } | |
| .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); } | |
| .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); } | |
| .chat-file-card .name { font-size: 14px; font-weight: 600; color: var(--brand-white); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .chat-bubble { padding: 18px 24px; font-size: 16px; line-height: 1.6; position: relative; word-wrap: break-word; overflow-wrap: anywhere; } | |
| .user-msg .chat-bubble { background: var(--text-primary); color: var(--text-inverse); border-radius: 24px 24px 6px 24px; box-shadow: var(--shadow-md); } | |
| .bot-msg .chat-bubble { background: var(--bg-primary); color: var(--text-primary); border: 1px solid var(--border-light); border-radius: 6px 24px 24px 24px; box-shadow: var(--shadow-sm); width: 100%; } | |
| /* Bot Action Buttons (Copy & TTS) */ | |
| .bot-actions-row { display: flex; gap: 10px; margin-top: 8px; padding-left: 14px; opacity: 0; transition: opacity var(--ease-smooth); flex-wrap: wrap; } | |
| .bot-msg:hover .bot-actions-row { opacity: 1; } | |
| .btn-bot-action { background: var(--bg-secondary); border: 1px solid var(--border-light); color: var(--text-secondary); font-size: 13px; font-weight: 700; cursor: pointer; display: flex; align-items: center; gap: 6px; padding: 8px 16px; border-radius: var(--radius-full); transition: var(--ease-fast); } | |
| .btn-bot-action:hover { background: var(--bg-primary); color: var(--text-primary); transform: translateY(-2px); box-shadow: var(--shadow-sm); border-color: var(--border-medium); } | |
| .btn-bot-action svg { width: 16px; height: 16px; } | |
| .btn-bot-action.action-tts { color: var(--brand-green-dark); border-color: rgba(19, 136, 8, 0.25); background: var(--brand-green-light); } | |
| .btn-bot-action.action-tts:hover { background: rgba(19, 136, 8, 0.2); } | |
| /* Markdown Rendering inside Bubbles */ | |
| .chat-bubble p { margin-bottom: 16px; } | |
| .chat-bubble p:last-child { margin-bottom: 0; } | |
| .chat-bubble strong { font-weight: 800; color: inherit; } | |
| .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); } | |
| .user-msg .chat-bubble code { background: rgba(255,255,255,0.2); color: var(--brand-white); border-color: rgba(255,255,255,0.1); } | |
| .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; } | |
| .chat-bubble pre { background: #282c34; color: #abb2bf; padding: 20px; border-radius: var(--radius-md); overflow-x: auto; margin: 20px 0; font-family: var(--font-mono); font-size: 14px; line-height: 1.6; border: 1px solid #181a1f; box-shadow: var(--shadow-inner); } | |
| .chat-bubble pre code { background: transparent; color: inherit; padding: 0; font-weight: 400; border: none; } | |
| /* Streaming Loading UI Component */ | |
| .streaming-code-container { background: #1e1e2e; border-radius: var(--radius-md); border: 1px solid #313244; overflow: hidden; margin: 20px 0; box-shadow: var(--shadow-lg); } | |
| .streaming-code-header { background: #11111b; padding: 14px 20px; font-size: 12px; font-weight: 700; color: #a6adc8; display: flex; align-items: center; gap: 12px; border-bottom: 1px solid #313244; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 1.5px; } | |
| .loading-ring { width: 16px; height: 16px; border: 2px solid rgba(255,255,255,0.1); border-top-color: var(--brand-saffron); border-radius: 50%; animation: ringSpin 1s cubic-bezier(0.68, -0.55, 0.265, 1.55) infinite; } | |
| @keyframes ringSpin { to { transform: rotate(360deg); } } | |
| .streaming-code-container pre { margin: 0; background: transparent; border: none; border-radius: 0; box-shadow: none; } | |
| /* Workspace Execute Card */ | |
| .workspace-execute-card { background: var(--bg-secondary); border: 1px solid var(--border-light); border-radius: var(--radius-md); padding: 20px; margin: 20px 0; display: flex; align-items: center; justify-content: space-between; box-shadow: var(--shadow-sm); transition: var(--ease-smooth); } | |
| .workspace-execute-card:hover { border-color: var(--brand-saffron-light); box-shadow: var(--shadow-md); background: var(--bg-primary); } | |
| .ws-card-left { display: flex; align-items: center; gap: 16px; } | |
| .ws-card-icon-box { width: 54px; height: 54px; border-radius: var(--radius-sm); background: var(--bg-primary); display: flex; align-items: center; justify-content: center; font-size: 26px; box-shadow: var(--shadow-sm); border: 1px solid var(--border-light); color: var(--text-primary); } | |
| .ws-card-icon-box svg { color: var(--brand-saffron); } | |
| .ws-card-details h4 { font-size: 16px; font-weight: 800; margin-bottom: 4px; color: var(--text-primary); } | |
| .ws-card-details p { font-size: 12px; color: var(--text-secondary); font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 600; } | |
| .btn-launch-workspace { padding: 12px 24px; background: var(--text-primary); color: var(--brand-white); border: none; border-radius: var(--radius-sm); font-size: 14px; font-weight: 700; cursor: pointer; transition: var(--ease-spring); box-shadow: var(--shadow-sm); } | |
| .btn-launch-workspace:hover { transform: scale(1.05); background: var(--brand-saffron); box-shadow: 0 6px 15px rgba(255,153,51,0.35); } | |
| /* --------------------------------------------------------------------------------------- | |
| I. BOTTOM INPUT DOCK & TOOLS | |
| --------------------------------------------------------------------------------------- */ | |
| .input-dock { flex-shrink: 0; padding: 0 40px 32px; background: transparent; position: relative; z-index: var(--z-dock); } | |
| .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); } | |
| .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); } | |
| /* Upload Preview Header (Non-Technical Vocabulary) */ | |
| .upload-preview-bar { display: none; padding: 20px 28px 0; align-items: center; gap: 16px; } | |
| .upload-preview-bar.visible { display: flex; animation: fadeIn 0.3s ease; } | |
| .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); } | |
| .upload-meta { flex: 1; } | |
| .upload-filename { font-size: 14px; font-weight: 700; color: var(--text-primary); max-width: 250px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .upload-status-text { font-size: 12px; color: var(--brand-green); font-weight: 600; margin-top: 2px; display: flex; align-items: center; gap: 6px; } | |
| .upload-status-text svg { width: 14px; height: 14px; } | |
| .btn-discard-upload { background: rgba(239, 68, 68, 0.1); border: none; width: 32px; height: 32px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; color: var(--text-error); transition: var(--ease-fast); } | |
| .btn-discard-upload:hover { background: var(--text-error); color: #fff; } | |
| .input-tools-row { display: flex; align-items: flex-end; gap: 12px; padding: 14px 20px 14px 24px; } | |
| .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; } | |
| .main-textarea::placeholder { color: var(--text-tertiary); font-weight: 400; } | |
| /* Attachment Menu Dropdown */ | |
| .attachment-module { position: relative; } | |
| .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); } | |
| .btn-tool-icon:hover { background: var(--bg-tertiary); color: var(--text-primary); } | |
| .btn-tool-icon svg { width: 24px; height: 24px; stroke-width: 2; } | |
| .attachment-dropdown { position: absolute; bottom: 60px; left: 0; background: rgba(255,255,255,0.95); backdrop-filter: blur(25px); -webkit-backdrop-filter: blur(25px); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 10px; box-shadow: var(--shadow-xl); display: none; flex-direction: column; gap: 6px; z-index: var(--z-dropdown); min-width: 240px; transform: translateY(15px) scale(0.95); opacity: 0; transition: all var(--ease-fast); transform-origin: bottom left; } | |
| .attachment-dropdown.visible { display: flex; transform: translateY(0) scale(1); opacity: 1; } | |
| .dropdown-item { display: flex; align-items: center; gap: 14px; padding: 14px 18px; border-radius: var(--radius-sm); cursor: pointer; font-size: 15px; font-weight: 600; color: var(--text-primary); transition: var(--ease-fast); } | |
| .dropdown-item:hover { background: var(--bg-tertiary); color: var(--brand-saffron-dark); } | |
| .dropdown-item svg { width: 20px; height: 20px; color: var(--text-secondary); } | |
| .dropdown-item:hover svg { color: var(--brand-saffron); } | |
| .stealth-input { display: none; } | |
| /* Smart Language Toggle & Mic */ | |
| .language-toggle-chip { font-size: 13px; font-weight: 800; color: var(--text-secondary); border: 2px solid var(--border-light); background: var(--bg-primary); border-radius: 10px; padding: 6px 12px; cursor: pointer; transition: var(--ease-smooth); display: flex; align-items: center; gap: 6px; user-select: none; } | |
| .language-toggle-chip:hover { background: var(--brand-saffron-light); color: var(--brand-saffron-dark); border-color: var(--brand-saffron); } | |
| .language-toggle-chip.hindi-active { background: var(--brand-green-light); color: var(--brand-green-dark); border-color: var(--brand-green); } | |
| .language-toggle-chip svg { width: 16px; height: 16px; } | |
| .btn-mic-tool.active-listening { color: #ffffff; background: var(--text-error); animation: recordPulse 1.5s infinite; border-radius: var(--radius-full); } | |
| @keyframes recordPulse { 0% { box-shadow: 0 0 0 0 rgba(239,68,68,0.5); } 70% { box-shadow: 0 0 0 12px rgba(239,68,68,0); } 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); } } | |
| /* Master Send Button */ | |
| .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); } | |
| .btn-master-send svg { width: 20px; height: 20px; fill: white; transition: transform 0.2s; } | |
| .btn-master-send:hover { transform: translateY(-3px); box-shadow: 0 10px 25px rgba(255, 153, 51, 0.4); } | |
| .btn-master-send:hover svg { transform: translate(2px, -2px); } | |
| .btn-master-send:disabled { opacity: 0.4; cursor: not-allowed; filter: grayscale(100%); transform: none; box-shadow: none; } | |
| .bottom-branding-text { text-align: center; font-size: 12px; color: var(--text-tertiary); margin-top: 16px; font-weight: 600; letter-spacing: 0.5px; } | |
| /* --------------------------------------------------------------------------------------- | |
| J. WORKSPACE PRO (VS-CODE LIKE EDITOR PANEL) | |
| --------------------------------------------------------------------------------------- */ | |
| .code-workspace-panel { width: 0; background: #1e1e1e; border-left: 1px solid var(--border-light); display: flex; flex-direction: column; transition: width var(--ease-smooth); overflow: hidden; box-shadow: -15px 0 40px rgba(0,0,0,0.15); z-index: var(--z-workspace); } | |
| .code-workspace-panel.expanded { width: 55%; } | |
| .workspace-header-bar { padding: 16px 24px; border-bottom: 1px solid #333; background: #252526; display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; } | |
| .workspace-title-group { font-size: 14px; font-weight: 700; display: flex; align-items: center; gap: 10px; color: #abb2bf; font-family: var(--font-mono); letter-spacing: 0.5px;} | |
| .workspace-tools { display: flex; gap: 10px; } | |
| .btn-workspace-tool { padding: 8px 16px; border-radius: var(--radius-sm); background: #3e4451; border: 1px solid #181a1f; cursor: pointer; display: flex; align-items: center; gap: 8px; color: #fff; transition: var(--ease-fast); font-size: 13px; font-weight: 600; } | |
| .btn-workspace-tool:hover { background: #4b5263; } | |
| .btn-workspace-tool.close { padding: 8px; background: transparent; border-color: transparent; color: #abb2bf; } | |
| .btn-workspace-tool.close:hover { color: #e06c75; background: rgba(224, 108, 117, 0.1); } | |
| .workspace-editor-surface { flex: 1; overflow-y: auto; background: #282c34; padding: 24px; position: relative; } | |
| .workspace-editor-surface pre { margin: 0; padding: 0; background: transparent; font-family: var(--font-mono); font-size: 15px; line-height: 1.6; overflow-x: auto; color: #abb2bf; border: none; box-shadow: none;} | |
| /* --------------------------------------------------------------------------------------- | |
| K. LIVE MODE FULLSCREEN OVERLAY | |
| --------------------------------------------------------------------------------------- */ | |
| .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); } | |
| .live-mode-viewport.active { opacity: 1; pointer-events: auto; } | |
| .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); } | |
| .btn-close-live:hover { background: rgba(239, 68, 68, 0.15); color: var(--text-error); transform: rotate(90deg); } | |
| .pulsing-orb-wrapper { position: relative; width: 250px; height: 250px; display: flex; align-items: center; justify-content: center; margin-bottom: 50px; } | |
| .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; } | |
| .energy-core.speaking { animation: coreSpeak 0.5s infinite alternate; } | |
| @keyframes coreSpeak { 0% { transform: scale(1); box-shadow: 0 0 30px rgba(19,136,8,0.4); } 100% { transform: scale(1.1); box-shadow: 0 0 60px rgba(255,153,51,0.6); } } | |
| .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; } | |
| .energy-ring:nth-child(2) { animation-delay: 0.8s; border-color: var(--brand-green); } | |
| .energy-ring:nth-child(3) { animation-delay: 1.6s; border-color: var(--text-primary); } | |
| @keyframes energyRipple { 0% { transform: scale(0.4); opacity: 1; border-width: 6px; } 100% { transform: scale(1.6); opacity: 0; border-width: 1px; } } | |
| .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; } | |
| .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; } | |
| .live-control-deck { display: flex; gap: 24px; margin-top: 50px; } | |
| .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); } | |
| .btn-live-deck.mic-control { background: var(--bg-primary); border: 3px solid var(--border-light); color: var(--text-primary); } | |
| .btn-live-deck.mic-control:hover { transform: translateY(-6px); border-color: var(--brand-saffron); color: var(--brand-saffron); box-shadow: var(--shadow-xl); } | |
| .btn-live-deck.mic-control.is-recording { background: var(--text-error); color: white; border-color: var(--text-error); animation: pulseMicLive 1.2s infinite; } | |
| @keyframes pulseMicLive { 0% { box-shadow: 0 0 0 0 rgba(239,68,68,0.6); } 70% { box-shadow: 0 0 0 20px rgba(239,68,68,0); } 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); } } | |
| /* Scroll Trigger Button */ | |
| .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; } | |
| .btn-scroll-trigger.visible { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); } | |
| .btn-scroll-trigger:hover { background: var(--text-primary); color: var(--text-inverse); transform: translateX(-50%) translateY(-5px); box-shadow: var(--shadow-xl); } | |
| /* --------------------------------------------------------------------------------------- | |
| L. AUTHENTICATION SECURE GATEWAY (MODAL) | |
| --------------------------------------------------------------------------------------- */ | |
| .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; } | |
| .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); } | |
| .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; } | |
| .btn-modal-dismiss:hover { background: #fee2e2; color: var(--text-error); } | |
| .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; } | |
| .auth-tab-switch { display: flex; background: var(--bg-secondary); padding: 6px; border-radius: var(--radius-md); margin-bottom: 28px; } | |
| .auth-tab-item { flex: 1; padding: 14px; text-align: center; border-radius: var(--radius-sm); font-size: 15px; font-weight: 800; color: var(--text-secondary); cursor: pointer; transition: var(--ease-fast); } | |
| .auth-tab-item.active { background: var(--bg-primary); color: var(--text-primary); box-shadow: var(--shadow-sm); } | |
| .auth-form-view { display: none; flex-direction: column; animation: slideIn 0.3s ease; } | |
| .auth-form-view.active { display: flex; } | |
| .auth-input-element { width: 100%; padding: 18px; border: 2px solid var(--border-light); border-radius: var(--radius-md); font-size: 16px; outline: none; background: var(--bg-secondary); transition: var(--ease-fast); font-family: var(--font-sans); margin-bottom: 20px; font-weight: 500; } | |
| .auth-input-element:focus { border-color: var(--brand-saffron); background: var(--bg-primary); box-shadow: 0 0 0 4px var(--brand-saffron-light); } | |
| .btn-auth-execute { width: 100%; padding: 18px; background: var(--text-primary); color: var(--brand-white); border: none; border-radius: var(--radius-md); font-size: 16px; font-weight: 800; cursor: pointer; transition: var(--ease-fast); margin-top: 10px; } | |
| .btn-auth-execute:hover { background: #000000; transform: translateY(-2px); box-shadow: var(--shadow-md); } | |
| .btn-auth-return { width: 100%; padding: 14px; background: transparent; color: var(--text-tertiary); border: none; font-size: 15px; font-weight: 700; cursor: pointer; margin-top: 10px; transition: 0.2s; } | |
| .btn-auth-return:hover { color: var(--text-primary); } | |
| @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } | |
| @keyframes slideIn { from { opacity: 0; transform: translateX(25px); } to { opacity: 1; transform: none; } } | |
| /* Cursor & Loading UI */ | |
| .typing-cursor { display: inline-block; width: 10px; height: 18px; background: var(--brand-saffron); margin-left: 4px; animation: cursorBlink 1s step-end infinite; vertical-align: middle; } | |
| @keyframes cursorBlink { 50% { opacity: 0; } } | |
| .loading-dots-container { display: flex; align-items: center; gap: 6px; padding: 6px; } | |
| .loading-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--text-tertiary); opacity: 0.4; animation: dotBounce 1.4s infinite ease-in-out both; } | |
| .loading-dot:nth-child(1) { animation-delay: -0.32s; } .loading-dot:nth-child(2) { animation-delay: -0.16s; } | |
| @keyframes dotBounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1); } } | |
| /* --------------------------------------------------------------------------------------- | |
| M. RESPONSIVE DESIGN (MOBILE & TABLET ADAPTATIONS) | |
| --------------------------------------------------------------------------------------- */ | |
| @media (max-width: 1024px) { | |
| .code-workspace-panel.expanded { width: 100%; position: absolute; top: 0; left: 0; height: 100%; z-index: 2000; } | |
| } | |
| @media (max-width: 900px) { | |
| .sidebar-container { position: absolute; left: -320px; z-index: 1000; height: 100%; box-shadow: var(--shadow-float); } | |
| .sidebar-container.active { left: 0; } | |
| .btn-hamburger { display: flex; } | |
| .btn-mobile-close { display: flex; } | |
| .chat-scroll-area { padding: 24px 15px; } | |
| .input-dock { padding: 10px 15px 20px; } | |
| .chat-message-row { max-width: 98%; } | |
| .btn-scroll-trigger { bottom: 130px; } | |
| .hero-greeting { font-size: 32px; } | |
| .prompt-suggestions-grid { grid-template-columns: 1fr; } | |
| .input-tools-row { flex-wrap: wrap; } | |
| .main-textarea { min-width: 100%; order: -1; margin-bottom: 10px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- ========================================================================================= | |
| DOM STRUCTURE: NOTIFICATIONS | |
| ========================================================================================= --> | |
| <div id="toast-container" class="toast-wrapper"></div> | |
| <!-- ========================================================================================= | |
| DOM STRUCTURE: STARTUP OVERLAY | |
| ========================================================================================= --> | |
| <div class="startup-overlay" id="welcomeOverlay"> | |
| <div class="startup-logo-container"> | |
| <div class="startup-logo-glow"></div> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Core Identity"> | |
| </div> | |
| <h1 class="startup-title">Welcome to Arjun 2.O</h1> | |
| <p class="startup-desc">Professional Enterprise Edition. Experience secure workspace logic generation, high-fidelity visual rendering, and zero-latency live conversational mode.</p> | |
| <button class="btn-startup-launch" id="btnLaunchSystem"> | |
| Initialize System | |
| <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg> | |
| </button> | |
| </div> | |
| <!-- ========================================================================================= | |
| DOM STRUCTURE: LIVE MODE FULLSCREEN OVERLAY | |
| ========================================================================================= --> | |
| <div class="live-mode-viewport" id="liveOverlay"> | |
| <button class="btn-close-live" id="btnExitLiveMode" title="Exit Live Mode"> | |
| <svg viewBox="0 0 24 24" 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> | |
| </button> | |
| <div class="pulsing-orb-wrapper"> | |
| <div class="energy-ring"></div> | |
| <div class="energy-ring"></div> | |
| <div class="energy-ring"></div> | |
| <div class="energy-core" id="liveOrb"></div> | |
| </div> | |
| <div class="live-feedback-status" id="liveStatusText">Initializing Audio Stream...</div> | |
| <div class="live-feedback-text" id="liveTranscriptText">Speak directly to Arjun. Natural language processing active.</div> | |
| <div class="live-control-deck"> | |
| <button class="btn-live-deck mic-control" id="liveMicBtn"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" x2="12" y1="19" y2="22"></line></svg> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- ========================================================================================= | |
| DOM STRUCTURE: AMBIENT BACKGROUND | |
| ========================================================================================= --> | |
| <div class="ambient-environment"> | |
| <div class="ambient-orb orb-top-left"></div> | |
| <div class="ambient-orb orb-bottom-right"></div> | |
| </div> | |
| <!-- ========================================================================================= | |
| DOM STRUCTURE: MASTER APPLICATION CONTAINER | |
| ========================================================================================= --> | |
| <div class="app-root"> | |
| <!-- A. NAVIGATION SIDEBAR --> | |
| <aside class="sidebar-container" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <a href="#" class="brand-identity"> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo"> | |
| <div class="brand-typography"> | |
| <span class="brand-name">ARJUN 2.O</span> | |
| <span class="brand-edition">Professional</span> | |
| </div> | |
| </a> | |
| <button class="btn-mobile-close" id="btnSidebarClose"> | |
| <svg viewBox="0 0 24 24" width="20" height="20" stroke="currentColor" stroke-width="2.5" fill="none"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| </div> | |
| <div class="sidebar-body"> | |
| <!-- Authentication Widget --> | |
| <div class="auth-widget"> | |
| <button id="loginPromptBtn" class="btn-secure-connect"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect><path d="M7 11V7a5 5 0 0 1 10 0v4"></path></svg> | |
| Secure Login | |
| </button> | |
| <div id="userProfile" class="user-profile-display"> | |
| <div class="avatar-badge" id="uAvatar">U</div> | |
| <div class="user-details"> | |
| <div class="user-email-id" id="uEmail">user@network.com</div> | |
| <div class="user-clearance-level">Verified Clearance</div> | |
| </div> | |
| <button class="btn-system-logout" id="btnLogout" title="Terminate Session"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2.5"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- Branding Box --> | |
| <div class="developer-credit-box"> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Core Image"> | |
| <h3 class="dev-box-title">Arjun Intelligence</h3> | |
| <p class="dev-box-subtitle">ENGINEERED BY ABHAY KUMAR</p> | |
| </div> | |
| <!-- Wipe Memory Button --> | |
| <button class="btn-erase-memory" id="btnClearMemory"> | |
| <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> | |
| Wipe Memory Context | |
| </button> | |
| </div> | |
| </aside> | |
| <!-- B. MAIN CHAT ENGINE INTERFACE --> | |
| <main class="main-chat-wrapper"> | |
| <!-- Top Status Bar --> | |
| <header class="chat-top-bar"> | |
| <div class="top-bar-left"> | |
| <button class="btn-hamburger" id="btnSidebarOpen"> | |
| <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> | |
| </button> | |
| <div class="system-status-pill"> | |
| <div class="pulse-dot"></div> | |
| System Online | |
| </div> | |
| </div> | |
| <!-- LIVE MODE ACTIVATOR --> | |
| <button class="btn-activate-live" id="btnStartLiveMode"> | |
| <svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5" fill="none"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4" fill="currentColor"></circle></svg> | |
| LIVE MODE | |
| </button> | |
| </header> | |
| <!-- Scrollable Conversation Area --> | |
| <div class="chat-scroll-area" id="msgs"> | |
| <!-- Internal Welcome / Guest Warning State --> | |
| <div class="chat-hero-section" id="welcomeState"> | |
| <div class="hero-avatar-wrapper"> | |
| <div class="hero-avatar-glow"></div> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo Large"> | |
| </div> | |
| <h2 class="hero-greeting">How can I assist you today?</h2> | |
| <p class="hero-description">Experience next-generation reasoning, seamless multi-file analysis, and secure workspace code generation engineered by Abhay Kumar.</p> | |
| <!-- GUEST BADGE LIMIT DISPLAY --> | |
| <div id="guestBadge" class="guest-warning-badge"> | |
| <svg viewBox="0 0 24 24" 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> | |
| Guest Mode Active: <span id="guestCountDisplay">0</span>/3 Queries Used. Please login. | |
| </div> | |
| <div class="prompt-suggestions-grid"> | |
| <div class="prompt-card" id="suggCodeBtn"> | |
| <span class="prompt-icon">💻</span> | |
| <div class="prompt-title">Generate Production Code</div> | |
| <div class="prompt-desc">Create secure, workspace-ready architecture instantly.</div> | |
| </div> | |
| <div class="prompt-card" id="suggImgBtn"> | |
| <span class="prompt-icon">🎨</span> | |
| <div class="prompt-title">Render Stunning Visions</div> | |
| <div class="prompt-desc">Utilize advanced AI to paint your imagination.</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Floating Scroll Down Button --> | |
| <button class="btn-scroll-trigger" id="scrollTriggerBtn"> | |
| <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="6 9 12 15 18 9"></polyline></svg> | |
| </button> | |
| <!-- Bottom Input Dock --> | |
| <div class="input-dock"> | |
| <div class="input-master-container"> | |
| <!-- File Preview Component --> | |
| <div class="upload-preview-bar" id="filePreviewBar"> | |
| <img class="upload-thumb" id="previewImgThumb" src="" style="display:none;"> | |
| <div class="upload-thumb" id="previewDocIcon" style="display:none;">📄</div> | |
| <div class="upload-meta"> | |
| <div class="upload-filename" id="previewFileName">document.pdf</div> | |
| <div class="upload-status-text"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg> | |
| File ready to be transmitted | |
| </div> | |
| </div> | |
| <button class="btn-discard-upload" id="btnDiscardFile"> | |
| <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> | |
| </button> | |
| </div> | |
| <!-- Main Tool & Input Row --> | |
| <div class="input-tools-row"> | |
| <!-- Attachment Dropdown Module --> | |
| <div class="attachment-module"> | |
| <button class="btn-tool-icon" id="btnAttachMenuToggle" title="Attach File"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg> | |
| </button> | |
| <div class="attachment-dropdown" id="attachDropdownMenu"> | |
| <div class="dropdown-item" id="opt-image"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect><circle cx="8.5" cy="8.5" r="1.5"></circle><polyline points="21 15 16 10 5 21"></polyline></svg> | |
| High-Res Image | |
| </div> | |
| <div class="dropdown-item" id="opt-video"> | |
| <svg viewBox="0 0 24 24" 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> | |
| Video Media | |
| </div> | |
| <div class="dropdown-item" id="opt-audio"> | |
| <svg viewBox="0 0 24 24" 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> | |
| Audio Track | |
| </div> | |
| <div class="dropdown-item" id="opt-document"> | |
| <svg viewBox="0 0 24 24" 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> | |
| Document / Code | |
| </div> | |
| </div> | |
| <!-- Hidden File Inputs --> | |
| <input type="file" id="sys-inp-image" class="stealth-input" accept="image/*"> | |
| <input type="file" id="sys-inp-video" class="stealth-input" accept="video/*"> | |
| <input type="file" id="sys-inp-audio" class="stealth-input" accept="audio/*"> | |
| <input type="file" id="sys-inp-document" class="stealth-input" accept=".pdf,.doc,.docx,.txt,.md,.csv,.json,.py,.js,.html,.css,.cpp,.c,.java"> | |
| </div> | |
| <!-- Smart Language Toggle Button (Bilingual STT logic) --> | |
| <button class="language-toggle-chip" id="smartLangToggle" title="Toggle Voice Engine Language"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="2" y1="12" x2="22" y2="12"></line><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"></path></svg> | |
| <span id="langDisplayBadge">EN</span> | |
| </button> | |
| <!-- Voice Dictation Button --> | |
| <button class="btn-tool-icon btn-mic-tool" id="masterVoiceBtn" title="Voice Dictation"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"></path><path d="M19 10v2a7 7 0 0 1-14 0v-2"></path><line x1="12" x2="12" y1="19" y2="22"></line></svg> | |
| </button> | |
| <!-- Expanding Text Area --> | |
| <textarea class="main-textarea" id="mainChatInput" placeholder="Message Arjun Intelligence..." rows="1"></textarea> | |
| <!-- Master Send Button --> | |
| <button class="btn-master-send" id="masterSendBtn"> | |
| <svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="bottom-branding-text">Secured by Arjun Core Architecture • Developed by Abhay Kumar</div> | |
| </div> | |
| </main> | |
| <!-- C. WORKSPACE CODE EDITOR PANEL --> | |
| <aside class="code-workspace-panel" id="workspacePanel"> | |
| <div class="workspace-header-bar"> | |
| <div class="workspace-title-group"> | |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg> | |
| <span id="wsTitleText">Workspace Logic</span> | |
| </div> | |
| <div class="workspace-tools"> | |
| <button class="btn-workspace-tool" id="btnWsCopy"> | |
| <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> | |
| Copy Source | |
| </button> | |
| <button class="btn-workspace-tool close" id="btnWsClose" title="Close Editor"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" 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> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="workspace-editor-surface"> | |
| <pre><code id="workspaceCodeBlock" class="hljs"></code></pre> | |
| </div> | |
| </aside> | |
| </div> | |
| <!-- ========================================================================================= | |
| DOM STRUCTURE: SECURE AUTHENTICATION MODAL | |
| ========================================================================================= --> | |
| <div class="secure-modal-backdrop" id="secureAuthModal"> | |
| <div class="secure-modal-surface"> | |
| <button class="btn-modal-dismiss" id="btnAuthClose"> | |
| <svg viewBox="0 0 24 24" 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> | |
| </button> | |
| <div class="modal-brand-header">Arjun Network</div> | |
| <div class="auth-tab-switch"> | |
| <div class="auth-tab-item active" id="btn-tab-login">Connect</div> | |
| <div class="auth-tab-item" id="btn-tab-register">Enroll</div> | |
| </div> | |
| <!-- Login Flow --> | |
| <div id="auth-flow-login"> | |
| <div class="auth-form-view active" id="auth-log-phase-1"> | |
| <input type="email" class="auth-input-element" id="authLogEmail" placeholder="Authorized Email Address"> | |
| <button class="btn-auth-execute" id="btn-fire-log-otp">Generate Secure Token (OTP)</button> | |
| </div> | |
| <div class="auth-form-view" id="auth-log-phase-2"> | |
| <input type="number" class="auth-input-element" id="authLogOTP" placeholder="Validation Code (OTP)"> | |
| <button class="btn-auth-execute" id="btn-fire-log-verify">Authenticate Session</button> | |
| <button class="btn-auth-return" id="btnLogBack">← Return</button> | |
| </div> | |
| </div> | |
| <!-- Register Flow --> | |
| <div id="auth-flow-register" style="display:none;"> | |
| <div class="auth-form-view active" id="auth-reg-phase-1"> | |
| <input type="text" class="auth-input-element" id="authRegName" placeholder="Full Legal Name"> | |
| <input type="email" class="auth-input-element" id="authRegEmail" placeholder="Communication Email"> | |
| <button class="btn-auth-execute" id="btn-fire-reg-otp">Verify Identity</button> | |
| </div> | |
| <div class="auth-form-view" id="auth-reg-phase-2"> | |
| <input type="number" class="auth-input-element" id="authRegOTP" placeholder="Identity Token (OTP)"> | |
| <button class="btn-auth-execute" id="btn-fire-reg-verify">Initialize Account</button> | |
| <button class="btn-auth-return" id="btnRegBack">← Return</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ========================================================================================= | |
| [3] MASSIVE ENTERPRISE JAVASCRIPT ARCHITECTURE | |
| ========================================================================================= --> | |
| <!-- CRITICAL FIX: Removed type="module" from the script block to ensure all elements | |
| and functions remain within the window/global scope and are easily bound. | |
| Gradio Client will be dynamically imported. --> | |
| <script> | |
| // ======================================================================================= | |
| // 1. CONFIGURATION & STATE MANAGEMENT | |
| // ======================================================================================= | |
| const Config = { | |
| GAS_URL: "https://script.google.com/macros/s/AKfycbzTsXGoVh97JjtFAAG825GF4WUZqf7FDB3Zbxyf0nrLXt_1HjJSSqA829UcdoamRPvg0A/exec", | |
| LOGO_URL: "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png", | |
| PDF_WORKER: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js', | |
| API_ENDPOINT: '/api/chat', // Uses relative path to connect to Flask App.py | |
| }; | |
| // Initialize PDF worker | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = Config.PDF_WORKER; | |
| const AppState = { | |
| currentUser: localStorage.getItem('arjun_user') || null, | |
| guestCount: parseInt(localStorage.getItem('arjun_guest') || '0', 10), | |
| history: [], | |
| pendingAttachment: null, | |
| isProcessing: false, | |
| isUserScrolling: false, | |
| generatedCodeBlocks: [], | |
| currentWorkspaceId: null, | |
| isStreamingActive: false, | |
| isLiveModeEngaged: false, | |
| gradioClient: null, | |
| isMicRecording: false | |
| }; | |
| // Initialize Gradio Client Dynamically (Fixes the Module Bug) | |
| window.initGradioEngine = async function() { | |
| try { | |
| const module = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"); | |
| AppState.gradioClient = await module.Client.connect("Vedika66/Edge-TTS"); | |
| console.log("Neural TTS Engine connected successfully to Gradio."); | |
| } catch(err) { | |
| console.error("Gradio Connection Error:", err); | |
| } | |
| }; | |
| window.initGradioEngine(); | |
| // ======================================================================================= | |
| // 2. UTILITY ENGINE (Robust Copy, Notifications, and Text Cleaners) | |
| // ======================================================================================= | |
| const Utils = { | |
| showToast: (message) => { | |
| const container = document.getElementById('toast-container'); | |
| const toast = document.createElement('div'); | |
| toast.className = 'toast-notification'; | |
| toast.innerHTML = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"></polyline></svg> ${message}`; | |
| container.appendChild(toast); | |
| setTimeout(() => toast.remove(), 3000); | |
| }, | |
| escapeHTML: (str) => { | |
| return str.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | |
| }, | |
| // ✨ CRITICAL FIX: Intelligent Text Cleaner for TTS (Ignores code, links, images) | |
| cleanTextForVoice: (rawText) => { | |
| if(!rawText) return ""; | |
| let cleaned = rawText; | |
| // 1. Remove Markdown Code Blocks (``` ... ```) | |
| cleaned = cleaned.replace(/```[\s\S]*?```/g, ''); | |
| // 2. Remove Inline Code (`...`) | |
| cleaned = cleaned.replace(/`.*?`/g, ''); | |
| // 3. Remove Markdown Images () | |
| cleaned = cleaned.replace(/!\[.*?\]\(.*?\)/g, ''); | |
| // 4. Remove Markdown Links ([text](url)) - Extract just the text | |
| cleaned = cleaned.replace(/\[(.*?)\]\(.*?\)/g, '$1'); | |
| // 5. Remove Think Tags (<think>...</think>) | |
| cleaned = cleaned.replace(/<think>[\s\S]*?<\/think>/gi, ''); | |
| // 6. Remove URLs/HTTP links explicitly | |
| cleaned = cleaned.replace(/https?:\/\/[^\s]+/g, ''); | |
| // 7. Remove stray Markdown formatting symbols | |
| cleaned = cleaned.replace(/[*_~#]/g, ''); | |
| return cleaned.trim(); | |
| }, | |
| // Robust Fallback Copy Mechanism | |
| executeFallbackCopy: (text, btnElement) => { | |
| const textArea = document.createElement("textarea"); | |
| textArea.value = text; | |
| textArea.style.top = "0"; textArea.style.left = "0"; textArea.style.position = "fixed"; | |
| document.body.appendChild(textArea); | |
| textArea.focus(); textArea.select(); | |
| try { | |
| const successful = document.execCommand('copy'); | |
| if (successful) { | |
| const originalHTML = btnElement.innerHTML; | |
| 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 Successfully`; | |
| setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500); | |
| } else { | |
| Utils.showToast("Clipboard access denied by browser."); | |
| } | |
| } catch (err) { | |
| Utils.showToast("System error copying text."); | |
| } | |
| document.body.removeChild(textArea); | |
| }, | |
| copyToClipboard: (btnElement, textToCopy) => { | |
| if (!navigator.clipboard) { | |
| Utils.executeFallbackCopy(textToCopy, btnElement); | |
| return; | |
| } | |
| navigator.clipboard.writeText(textToCopy).then(() => { | |
| const originalHTML = btnElement.innerHTML; | |
| 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 Successfully`; | |
| setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500); | |
| }).catch((err) => { | |
| console.warn('Modern API failed, utilizing fallback...', err); | |
| Utils.executeFallbackCopy(textToCopy, btnElement); | |
| }); | |
| } | |
| }; | |
| // ======================================================================================= | |
| // 3. AUTHENTICATION & SYNC MANAGER (Robust Fetch) | |
| // ======================================================================================= | |
| const AuthManager = { | |
| initUI: () => { | |
| const promptBtn = document.getElementById('loginPromptBtn'); | |
| const profileCard = document.getElementById('userProfile'); | |
| const guestBadge = document.getElementById('guestBadge'); | |
| if (AppState.currentUser) { | |
| promptBtn.style.display = 'none'; | |
| profileCard.style.display = 'flex'; | |
| document.getElementById('uEmail').innerText = AppState.currentUser; | |
| document.getElementById('uAvatar').innerText = AppState.currentUser.charAt(0).toUpperCase(); | |
| if(guestBadge) guestBadge.style.display = 'none'; | |
| } else { | |
| promptBtn.style.display = 'flex'; | |
| profileCard.style.display = 'none'; | |
| if(guestBadge) { | |
| guestBadge.style.display = 'inline-flex'; | |
| document.getElementById('guestCountDisplay').innerText = AppState.guestCount; | |
| } | |
| } | |
| }, | |
| openModal: () => { document.getElementById('secureAuthModal').style.display = 'flex'; }, | |
| closeModal: () => { document.getElementById('secureAuthModal').style.display = 'none'; }, | |
| switchTab: (tabName) => { | |
| document.querySelectorAll('.auth-tab-item').forEach(el => el.classList.remove('active')); | |
| document.getElementById(tabName === 'login' ? 'btn-tab-login' : 'btn-tab-register').classList.add('active'); | |
| document.getElementById('auth-flow-login').style.display = tabName === 'login' ? 'block' : 'none'; | |
| document.getElementById('auth-flow-register').style.display = tabName === 'register' ? 'block' : 'none'; | |
| }, | |
| switchPhase: (currentId, nextId) => { | |
| document.getElementById(currentId).classList.remove('active'); | |
| document.getElementById(nextId).classList.add('active'); | |
| }, | |
| // ✨ FIXED GAS SYNC LOGIC | |
| process: async (actionType) => { | |
| const payload = { action: actionType }; | |
| let buttonId = ''; | |
| if (actionType === 'register_send_otp') { | |
| payload.name = document.getElementById('authRegName').value.trim(); | |
| payload.email = document.getElementById('authRegEmail').value.trim(); | |
| payload.phone = "0000000000"; payload.organization = "Arjun Base"; | |
| if (!payload.email || !payload.name) return Utils.showToast("Identification details required."); | |
| buttonId = 'btn-fire-reg-otp'; | |
| } | |
| else if (actionType === 'login_send_otp') { | |
| payload.email = document.getElementById('authLogEmail').value.trim(); | |
| if (!payload.email) return Utils.showToast("Email required for validation."); | |
| buttonId = 'btn-fire-log-otp'; | |
| } | |
| else if (actionType === 'register_verify' || actionType === 'login_verify') { | |
| payload.email = document.getElementById(actionType === 'register_verify' ? 'authRegEmail' : 'authLogEmail').value.trim(); | |
| payload.otp = document.getElementById(actionType === 'register_verify' ? 'authRegOTP' : 'authLogOTP').value.trim(); | |
| if (!payload.otp) return Utils.showToast("Security token required."); | |
| buttonId = actionType === 'register_verify' ? 'btn-fire-reg-verify' : 'btn-fire-log-verify'; | |
| } | |
| const triggerBtn = document.getElementById(buttonId); | |
| const originalText = triggerBtn.innerHTML; | |
| triggerBtn.disabled = true; | |
| triggerBtn.innerText = 'Establishing Connection...'; | |
| try { | |
| // mode: 'cors' handles redirect issues from GAS effectively | |
| const response = await fetch(Config.GAS_URL, { | |
| method: 'POST', | |
| mode: 'cors', | |
| headers: { 'Content-Type': 'text/plain;charset=utf-8' }, | |
| body: JSON.stringify(payload) | |
| }); | |
| const result = await response.json(); | |
| if (result.status === 'success') { | |
| Utils.showToast(result.message); | |
| if (actionType === 'register_send_otp') AuthManager.switchPhase('auth-reg-phase-1', 'auth-reg-phase-2'); | |
| else if (actionType === 'login_send_otp') AuthManager.switchPhase('auth-log-phase-1', 'auth-log-phase-2'); | |
| else if (actionType === 'register_verify' || actionType === 'login_verify') { | |
| AppState.currentUser = payload.email; | |
| localStorage.setItem('arjun_user', AppState.currentUser); | |
| AuthManager.closeModal(); | |
| AuthManager.initUI(); | |
| ChatEngine.syncHistoryFromCloud(); | |
| } | |
| } else { | |
| Utils.showToast(result.message); | |
| } | |
| } catch (error) { | |
| console.error("Auth Network Error: ", error); | |
| Utils.showToast("Encrypted gateway error. Check network."); | |
| } | |
| triggerBtn.disabled = false; | |
| triggerBtn.innerHTML = originalText; | |
| }, | |
| logout: () => { | |
| localStorage.removeItem('arjun_user'); | |
| location.reload(); | |
| } | |
| }; | |
| // ======================================================================================= | |
| // 4. UI & DOM MANAGER | |
| // ======================================================================================= | |
| const UIManager = { | |
| toggleSidebar: () => { document.getElementById('sidebar').classList.toggle('active'); }, | |
| checkScrollState: () => { | |
| const msgArea = document.getElementById('msgs'); | |
| const isAtBottom = Math.abs((msgArea.scrollHeight - msgArea.scrollTop) - msgArea.clientHeight) < 80; | |
| AppState.isUserScrolling = !isAtBottom; | |
| const scrollBtn = document.getElementById('scrollTriggerBtn'); | |
| if (AppState.isUserScrolling) scrollBtn.classList.add('visible'); | |
| else scrollBtn.classList.remove('visible'); | |
| }, | |
| forceScrollDown: () => { | |
| const msgArea = document.getElementById('msgs'); | |
| msgArea.scrollTo({ top: msgArea.scrollHeight, behavior: 'smooth' }); | |
| AppState.isUserScrolling = false; | |
| document.getElementById('scrollTriggerBtn').classList.remove('visible'); | |
| }, | |
| autoScroll: () => { | |
| if (!AppState.isUserScrolling) { | |
| const msgArea = document.getElementById('msgs'); | |
| msgArea.scrollTop = msgArea.scrollHeight; | |
| } | |
| }, | |
| autoGrowTextArea: (element) => { | |
| element.style.height = 'auto'; | |
| element.style.height = Math.min(element.scrollHeight, 200) + 'px'; | |
| }, | |
| toggleAttachMenu: () => { | |
| document.getElementById('attachDropdownMenu').classList.toggle('visible'); | |
| } | |
| }; | |
| // ======================================================================================= | |
| // 5. SPEECH & TTS ENGINE (Bilingual STT + Gradio Edge-TTS + Smart Cleaner) | |
| // ======================================================================================= | |
| const VoiceEngine = { | |
| recognition: null, | |
| currentLanguage: 'en-IN', | |
| init: () => { | |
| const SR = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| if (!SR) return; | |
| VoiceEngine.recognition = new SR(); | |
| // FIX: Setting continuous to true prevents mic from stopping abruptly | |
| VoiceEngine.recognition.continuous = true; | |
| VoiceEngine.recognition.interimResults = true; | |
| VoiceEngine.recognition.lang = VoiceEngine.currentLanguage; | |
| VoiceEngine.recognition.onstart = () => { | |
| AppState.isMicRecording = true; | |
| document.getElementById('masterVoiceBtn').classList.add('active-listening'); | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveMicBtn').classList.add('is-recording'); | |
| document.getElementById('liveStatusText').innerText = "Listening..."; | |
| document.getElementById('liveOrb').classList.remove('speaking'); | |
| document.getElementById('liveOrb').style.transform = "scale(1.25)"; | |
| } | |
| }; | |
| VoiceEngine.recognition.onresult = (event) => { | |
| let finalTranscript = ''; | |
| let interimTranscript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; ++i) { | |
| if (event.results[i].isFinal) { | |
| finalTranscript += event.results[i][0].transcript; | |
| } else { | |
| interimTranscript += event.results[i][0].transcript; | |
| } | |
| } | |
| if (finalTranscript.trim().length > 0) { | |
| let formattedText = finalTranscript.trim(); | |
| formattedText = formattedText.charAt(0).toUpperCase() + formattedText.slice(1); | |
| if (!/[.!?।]$/.test(formattedText)) formattedText += '.'; | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveTranscriptText').innerText = `"${formattedText}"`; | |
| document.getElementById('liveStatusText').innerText = "Synthesizing Response..."; | |
| document.getElementById('mainChatInput').value = formattedText; | |
| // Stop mic manually to trigger dispatch in Live Mode | |
| VoiceEngine.recognition.stop(); | |
| ChatEngine.initiateDispatch(); | |
| } else { | |
| const inputField = document.getElementById('mainChatInput'); | |
| inputField.value = (inputField.value + ' ' + formattedText + ' ').trim(); | |
| UIManager.autoGrowTextArea(inputField); | |
| } | |
| } | |
| }; | |
| VoiceEngine.recognition.onend = () => { | |
| AppState.isMicRecording = false; | |
| document.getElementById('masterVoiceBtn').classList.remove('active-listening'); | |
| if (AppState.isLiveModeEngaged && !AppState.isProcessing) { | |
| document.getElementById('liveMicBtn').classList.remove('is-recording'); | |
| document.getElementById('liveStatusText').innerText = "Tap mic to interact"; | |
| document.getElementById('liveOrb').style.transform = "scale(1)"; | |
| } | |
| }; | |
| VoiceEngine.recognition.onerror = (event) => { | |
| AppState.isMicRecording = false; | |
| if(event.error !== 'no-speech') { | |
| Utils.showToast("Voice module error: " + event.error); | |
| } | |
| document.getElementById('masterVoiceBtn').classList.remove('active-listening'); | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveMicBtn').classList.remove('is-recording'); | |
| document.getElementById('liveStatusText').innerText = "Audio Failure. Retry."; | |
| } | |
| }; | |
| }, | |
| switchLanguage: () => { | |
| const btn = document.getElementById('smartLangToggle'); | |
| const badge = document.getElementById('langDisplayBadge'); | |
| if (VoiceEngine.currentLanguage === 'en-IN') { | |
| VoiceEngine.currentLanguage = 'hi-IN'; | |
| badge.innerText = 'हि'; | |
| btn.classList.add('hindi-active'); | |
| Utils.showToast("Listening Protocol: Hindi (देवनागरी)"); | |
| } else { | |
| VoiceEngine.currentLanguage = 'en-IN'; | |
| badge.innerText = 'EN'; | |
| btn.classList.remove('hindi-active'); | |
| Utils.showToast("Listening Protocol: English"); | |
| } | |
| if (VoiceEngine.recognition) { | |
| VoiceEngine.recognition.lang = VoiceEngine.currentLanguage; | |
| if(AppState.isMicRecording) { | |
| VoiceEngine.recognition.stop(); | |
| setTimeout(() => { VoiceEngine.recognition.start(); }, 400); | |
| } | |
| } | |
| }, | |
| toggleDictation: () => { | |
| if (!VoiceEngine.recognition) return Utils.showToast("Hardware dictation unsupported."); | |
| try { | |
| if(AppState.isMicRecording) { | |
| VoiceEngine.recognition.stop(); | |
| } else { | |
| VoiceEngine.recognition.start(); | |
| } | |
| } catch (e) { console.error("Mic Start Error", e); } | |
| }, | |
| // ✨ GRADIO TTS INTEGRATION WITH SMART CLEANER | |
| executeTTS: async (rawText) => { | |
| // Apply smart text cleaner so AI doesn't read code or image links | |
| const cleanSpeechText = Utils.cleanTextForVoice(rawText); | |
| if(!cleanSpeechText.trim()) { | |
| Utils.showToast("No readable text found."); | |
| return; | |
| } | |
| Utils.showToast("🔊 Linking with Neural Voice Matrix..."); | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveOrb').classList.add('speaking'); | |
| } | |
| try { | |
| if(!AppState.gradioClient) { | |
| // Fallback dynamically import if not connected | |
| const module = await import("https://cdn.jsdelivr.net/npm/@gradio/client/dist/index.min.js"); | |
| AppState.gradioClient = await module.Client.connect("Vedika66/Edge-TTS"); | |
| } | |
| const result = await AppState.gradioClient.predict("/smart_hybrid_tts", { | |
| text: cleanSpeechText, | |
| voice_type: "प्रभात (भारी और प्राकृतिक आवाज़ - हिंग्लिश के लिए)" | |
| }); | |
| if (result.data && result.data[0]) { | |
| const audioUrl = typeof result.data[0] === 'string' ? result.data[0] : result.data[0].url; | |
| const audio = new Audio(audioUrl); | |
| audio.onended = () => { | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveOrb').classList.remove('speaking'); | |
| document.getElementById('liveStatusText').innerText = "Tap mic to respond"; | |
| } | |
| }; | |
| audio.play(); | |
| } else { | |
| throw new Error("No audio payload returned."); | |
| } | |
| } catch (error) { | |
| Utils.showToast("Voice Neural Matrix is currently overloaded."); | |
| console.error("Gradio TTS Error:", error); | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveOrb').classList.remove('speaking'); | |
| } | |
| } | |
| } | |
| }; | |
| /** | |
| * Live Mode Controller | |
| */ | |
| const LiveModeController = { | |
| initialize: () => { | |
| AppState.isLiveModeEngaged = true; | |
| document.getElementById('liveOverlay').classList.add('active'); | |
| if (VoiceEngine.recognition && !AppState.isMicRecording) { | |
| setTimeout(() => { | |
| try { VoiceEngine.recognition.start(); } catch(e){} | |
| }, 500); | |
| } | |
| }, | |
| terminate: () => { | |
| AppState.isLiveModeEngaged = false; | |
| document.getElementById('liveOverlay').classList.remove('active'); | |
| if (VoiceEngine.recognition && AppState.isMicRecording) { | |
| try { VoiceEngine.recognition.stop(); } catch(e){} | |
| } | |
| } | |
| }; | |
| // ======================================================================================= | |
| // 6. FILE & DOCUMENT PROCESSOR (Non-Technical Vocabulary) | |
| // ======================================================================================= | |
| const FileProcessor = { | |
| triggerFileSelector: (type) => { | |
| document.getElementById('attachDropdownMenu').classList.remove('visible'); | |
| document.getElementById('sys-inp-' + type).click(); | |
| }, | |
| processFile: async (inputElement, typeContext) => { | |
| const file = inputElement.files[0]; | |
| if (!file) return; | |
| const fName = file.name.toLowerCase(); | |
| const mime = file.type; | |
| if (typeContext === 'image' && !mime.startsWith('image/')) { Utils.showToast("Visual media required."); inputElement.value=''; return; } | |
| if (typeContext === 'video' && !mime.startsWith('video/')) { Utils.showToast("Video media required."); inputElement.value=''; return; } | |
| if (typeContext === 'audio' && !mime.startsWith('audio/')) { Utils.showToast("Audio media required."); inputElement.value=''; return; } | |
| Utils.showToast(`Uploading your file...`); | |
| const thumbImg = document.getElementById('previewImgThumb'); | |
| const docIcon = document.getElementById('previewDocIcon'); | |
| thumbImg.style.display = 'none'; | |
| docIcon.style.display = 'none'; | |
| document.getElementById('previewFileName').innerText = file.name; | |
| document.getElementById('filePreviewBar').classList.add('visible'); | |
| const fileReader = new FileReader(); | |
| if (typeContext === 'image') { | |
| fileReader.onload = (e) => { | |
| AppState.pendingAttachment = { type: 'image', data: e.target.result.split(',')[1], name: file.name }; | |
| thumbImg.src = e.target.result; | |
| thumbImg.style.display = 'flex'; | |
| }; | |
| fileReader.readAsDataURL(file); | |
| } | |
| else if (typeContext === 'video' || typeContext === 'audio') { | |
| fileReader.onload = (e) => { | |
| AppState.pendingAttachment = { type: typeContext, data: e.target.result.split(',')[1], name: file.name }; | |
| docIcon.innerText = typeContext === 'video' ? '🎥' : '🎵'; | |
| docIcon.style.display = 'flex'; | |
| }; | |
| fileReader.readAsDataURL(file); | |
| } | |
| else if (typeContext === 'document') { | |
| docIcon.innerText = '📄'; | |
| docIcon.style.display = 'flex'; | |
| if (fName.endsWith('.pdf')) { | |
| fileReader.onload = async function() { | |
| try { | |
| const typedArray = new Uint8Array(this.result); | |
| const pdfDoc = await pdfjsLib.getDocument(typedArray).promise; | |
| let extractedText = ""; | |
| for (let i = 1; i <= pdfDoc.numPages; i++) { | |
| const page = await pdfDoc.getPage(i); | |
| const content = await page.getTextContent(); | |
| extractedText += content.items.map(s => s.str).join(' ') + "\n"; | |
| } | |
| AppState.pendingAttachment = { type: 'text', data: extractedText.trim() || "No readable text found.", name: file.name }; | |
| Utils.showToast("Your PDF is ready!"); | |
| } catch (e) { | |
| Utils.showToast("PDF parsing encountered an error."); | |
| FileProcessor.discardFile(); | |
| } | |
| }; | |
| fileReader.readAsArrayBuffer(file); | |
| } | |
| else if (fName.endsWith('.docx') || fName.endsWith('.doc')) { | |
| fileReader.onload = async function() { | |
| try { | |
| const buffer = this.result; | |
| const resultData = await mammoth.extractRawText({ arrayBuffer: buffer }); | |
| AppState.pendingAttachment = { type: 'text', data: resultData.value.trim() || "No readable text found.", name: file.name }; | |
| Utils.showToast("Your Word document is ready!"); | |
| } catch(e) { | |
| Utils.showToast("Document parsing encountered an error."); | |
| FileProcessor.discardFile(); | |
| } | |
| }; | |
| fileReader.readAsArrayBuffer(file); | |
| } | |
| else { | |
| fileReader.onload = (e) => { | |
| AppState.pendingAttachment = { type: 'text', data: e.target.result, name: file.name }; | |
| Utils.showToast("Your file is ready!"); | |
| }; | |
| fileReader.readAsText(file); | |
| } | |
| } | |
| inputElement.value = ''; | |
| }, | |
| discardFile: () => { | |
| AppState.pendingAttachment = null; | |
| document.getElementById('filePreviewBar').classList.remove('visible'); | |
| } | |
| }; | |
| // ======================================================================================= | |
| // 7. WORKSPACE ENGINE & MARKDOWN RENDERING | |
| // ======================================================================================= | |
| const WorkspaceEngine = { | |
| launch: (codeBlockId) => { | |
| AppState.currentWorkspaceId = codeBlockId; | |
| const codeData = AppState.generatedCodeBlocks[codeBlockId]; | |
| document.getElementById('wsTitleText').innerText = codeData.lang ? `Generated ${codeData.lang.toUpperCase()}` : "Compiled Code"; | |
| const blockDisplay = document.getElementById('workspaceCodeBlock'); | |
| blockDisplay.className = `hljs language-${codeData.lang || 'plaintext'}`; | |
| blockDisplay.textContent = codeData.code; | |
| hljs.highlightElement(blockDisplay); | |
| document.getElementById('workspacePanel').classList.add('expanded'); | |
| if (window.innerWidth <= 1024) { | |
| document.getElementById('sidebar').classList.remove('active'); | |
| } | |
| }, | |
| closePanel: () => { document.getElementById('workspacePanel').classList.remove('expanded'); }, | |
| executeCopy: (btnElement) => { | |
| if (AppState.currentWorkspaceId !== null) { | |
| const srcCode = AppState.generatedCodeBlocks[AppState.currentWorkspaceId].code; | |
| Utils.copyToClipboard(btnElement, srcCode); | |
| } | |
| } | |
| }; | |
| const MD_Renderer = new marked.Renderer(); | |
| MD_Renderer.code = function(token) { | |
| const rawCode = typeof token === 'string' ? token : (token.text || ''); | |
| const langClass = typeof token === 'string' ? arguments[1] : (token.lang || ''); | |
| if (AppState.isStreamingActive) { | |
| return ` | |
| <div class="streaming-code-container"> | |
| <div class="streaming-code-header"> | |
| <div class="loading-ring"></div> | |
| Generating Architecture... | |
| </div> | |
| <pre><code class="language-${langClass}">${Utils.escapeHTML(rawCode)}</code></pre> | |
| </div>`; | |
| } | |
| const internalId = AppState.generatedCodeBlocks.length; | |
| AppState.generatedCodeBlocks.push({ code: rawCode, lang: langClass }); | |
| return ` | |
| <div class="workspace-execute-card"> | |
| <div class="ws-card-left"> | |
| <div class="ws-card-icon-box"> | |
| <svg viewBox="0 0 24 24" width="28" height="28" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg> | |
| </div> | |
| <div class="ws-card-details"> | |
| <h4>Code Module Compiled</h4> | |
| <p>${langClass || 'Syntax Detected'}</p> | |
| </div> | |
| </div> | |
| <button class="btn-launch-workspace btn-launch-ws" data-id="${internalId}">Open Workspace</button> | |
| </div>`; | |
| }; | |
| marked.use({ renderer: MD_Renderer }); | |
| // Event delegation for workspace buttons | |
| document.getElementById('msgs').addEventListener('click', function(e) { | |
| if (e.target && e.target.classList.contains('btn-launch-ws')) { | |
| const blockId = e.target.getAttribute('data-id'); | |
| WorkspaceEngine.launch(blockId); | |
| } | |
| }); | |
| // ======================================================================================= | |
| // 8. CORE CHAT ENGINE & LLM LOGIC | |
| // ======================================================================================= | |
| const ChatEngine = { | |
| handleKeyboard: (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| ChatEngine.initiateDispatch(); | |
| } | |
| }, | |
| sendExample: (queryText) => { | |
| document.getElementById('mainChatInput').value = queryText; | |
| ChatEngine.initiateDispatch(); | |
| }, | |
| clearMemory: async () => { | |
| if (AppState.currentUser) { | |
| Utils.showToast("Purging neural link memory..."); | |
| try { | |
| await fetch(Config.GAS_URL, { | |
| method: 'POST', | |
| mode: 'cors', | |
| headers: { 'Content-Type': 'text/plain;charset=utf-8' }, | |
| body: JSON.stringify({ action: "delete_history", email: AppState.currentUser }) | |
| }); | |
| } catch(e) {} | |
| } | |
| location.reload(); | |
| }, | |
| syncHistoryFromCloud: async () => { | |
| const messageZone = document.getElementById('msgs'); | |
| document.getElementById('welcomeState').style.display = 'none'; | |
| messageZone.innerHTML = '<div style="text-align:center; padding:80px; font-weight:800; color:var(--brand-saffron); font-size:18px;">Decrypting Historic Archives...</div>'; | |
| try { | |
| const response = await fetch(Config.GAS_URL, { | |
| method: 'POST', | |
| mode: 'cors', | |
| headers: { 'Content-Type': 'text/plain;charset=utf-8' }, | |
| body: JSON.stringify({ action: "get_history", email: AppState.currentUser }) | |
| }); | |
| const resData = await response.json(); | |
| messageZone.innerHTML = ''; | |
| if (resData.status === 'success' && resData.historyJSON !== "[]") { | |
| AppState.history = JSON.parse(resData.historyJSON); | |
| AppState.history.forEach(msgObj => { | |
| if (msgObj.role === 'user') { | |
| ChatEngine.renderUserMessage(msgObj.content, msgObj.badge, msgObj.previewHTML); | |
| } else { | |
| AppState.isStreamingActive = false; | |
| ChatEngine.renderBotMessage(msgObj.content, true); | |
| } | |
| }); | |
| setTimeout(UIManager.forceScrollDown, 150); | |
| } else { | |
| document.getElementById('welcomeState').style.display = 'flex'; | |
| } | |
| } catch (err) { | |
| messageZone.innerHTML = ''; | |
| document.getElementById('welcomeState').style.display = 'flex'; | |
| } | |
| }, | |
| saveHistoryToCloud: () => { | |
| if (AppState.currentUser) { | |
| fetch(Config.GAS_URL, { | |
| method: 'POST', | |
| mode: 'cors', | |
| headers: { 'Content-Type': 'text/plain;charset=utf-8' }, | |
| body: JSON.stringify({ action: "save_history", email: AppState.currentUser, historyJSON: JSON.stringify(AppState.history) }) | |
| }); | |
| } | |
| }, | |
| buildPreviewHTML: (attachmentObj) => { | |
| if (!attachmentObj) return ''; | |
| if (attachmentObj.type === 'image') { | |
| return `<img src="data:image/jpeg;base64,${attachmentObj.data}" class="chat-in-bubble-img" alt="Attached Image">`; | |
| } else if (attachmentObj.type === 'video') { | |
| 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>`; | |
| } else if (attachmentObj.type === 'audio') { | |
| 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>`; | |
| } else { | |
| 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>`; | |
| } | |
| }, | |
| renderUserMessage: (textContent, badgeText, previewHTMLString = '') => { | |
| let badgeNode = badgeText ? `<div class="file-badge"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"></path></svg> ${Utils.escapeHTML(badgeText)}</div>` : ''; | |
| let visibleText = textContent; | |
| if(textContent.includes('---DATA BEGIN---')) { | |
| const parts = textContent.split('User Prompt:'); | |
| if(parts.length > 1) visibleText = parts[1].trim(); | |
| } | |
| const bubbleBox = document.createElement('div'); | |
| bubbleBox.className = 'chat-message-row user-msg'; | |
| bubbleBox.innerHTML = ` | |
| <div class="msg-avatar">U</div> | |
| <div class="msg-content-column"> | |
| ${badgeNode} | |
| <div class="chat-bubble"> | |
| ${previewHTMLString} | |
| ${Utils.escapeHTML(visibleText)} | |
| </div> | |
| </div>`; | |
| document.getElementById('msgs').appendChild(bubbleBox); | |
| }, | |
| renderBotMessage: (textContent, bypassStreamFormatting = false) => { | |
| const bubbleBox = document.createElement('div'); | |
| bubbleBox.className = 'chat-message-row bot-msg'; | |
| bubbleBox.innerHTML = ` | |
| <div class="msg-avatar"><img src="${Config.LOGO_URL}" alt="AI"></div> | |
| <div class="msg-content-column"> | |
| <div class="chat-bubble"></div> | |
| </div>`; | |
| document.getElementById('msgs').appendChild(bubbleBox); | |
| ChatEngine.updateBotBubble(bubbleBox, textContent, !bypassStreamFormatting); | |
| return bubbleBox; | |
| }, | |
| updateBotBubble: (bubbleDOM, rawMarkup, isCurrentlyStreaming) => { | |
| const contentTarget = bubbleDOM.querySelector('.chat-bubble'); | |
| const strippedMarkup = rawMarkup.replace(/<think>([\s\S]*?)(<\/think>|$)/gi, ''); | |
| let renderHTML = marked.parse(strippedMarkup.trim()); | |
| if (isCurrentlyStreaming) { | |
| renderHTML += `<div class="loading-dots-container"><div class="loading-dot"></div><div class="loading-dot"></div><div class="loading-dot"></div></div>`; | |
| } | |
| contentTarget.innerHTML = renderHTML; | |
| let actionTray = bubbleDOM.querySelector('.bot-actions-row'); | |
| if (!isCurrentlyStreaming && !actionTray && strippedMarkup.trim().length > 0) { | |
| const safeRawCopy = strippedMarkup.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, ''); | |
| // safeRawTTS is passed as is, because the executeTTS function now handles the Smart Cleaning internally | |
| const safeRawTTS = strippedMarkup.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, ' '); | |
| const actionsHTML = ` | |
| <div class="bot-actions-row"> | |
| <button class="btn-bot-action btn-cpy-action" data-cpy="${safeRawCopy}"> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> | |
| Copy Data | |
| </button> | |
| <button class="btn-bot-action action-tts btn-tts-action" data-tts="${safeRawTTS}"> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"></polygon><path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07"></path></svg> | |
| Read Aloud | |
| </button> | |
| </div>`; | |
| bubbleDOM.querySelector('.msg-content-column').insertAdjacentHTML('beforeend', actionsHTML); | |
| bubbleDOM.querySelector('.btn-cpy-action').addEventListener('click', function() { Utils.copyToClipboard(this, this.getAttribute('data-cpy')); }); | |
| bubbleDOM.querySelector('.btn-tts-action').addEventListener('click', function() { VoiceEngine.executeTTS(this.getAttribute('data-tts')); }); | |
| } | |
| }, | |
| initiateDispatch: async () => { | |
| if (AppState.isProcessing) return; | |
| const inputField = document.getElementById('mainChatInput'); | |
| const rawUserText = inputField.value.trim(); | |
| if (!rawUserText && !AppState.pendingAttachment) return; | |
| // VALIDATE GUEST LIMIT | |
| if (!AppState.currentUser) { | |
| if (AppState.guestCount >= 3) { | |
| AuthManager.openModal(); | |
| return Utils.showToast("Query limit reached. Secure Login required."); | |
| } | |
| AppState.guestCount++; | |
| localStorage.setItem('arjun_guest', AppState.guestCount.toString()); | |
| document.getElementById('guestCountDisplay').innerText = AppState.guestCount; | |
| } | |
| // Prepare UI | |
| inputField.value = ''; | |
| inputField.style.height = 'auto'; | |
| document.getElementById('masterSendBtn').disabled = true; | |
| AppState.isProcessing = true; | |
| const wState = document.getElementById('welcomeState'); | |
| if (wState) wState.style.display = 'none'; | |
| let payloadStr = rawUserText; | |
| let payloadAttachments = []; | |
| let visualBadge = null; | |
| let previewHTMLForChat = ''; | |
| // Attachment Processing Logic | |
| if (AppState.pendingAttachment) { | |
| previewHTMLForChat = ChatEngine.buildPreviewHTML(AppState.pendingAttachment); | |
| if (AppState.pendingAttachment.type === 'text') { | |
| payloadStr = `[Document Analyzed: ${AppState.pendingAttachment.name}]\n\n---DATA BEGIN---\n${AppState.pendingAttachment.data}\n---DATA END---\n\nUser Prompt: ${rawUserText}`; | |
| visualBadge = 'File Appended'; | |
| } else { | |
| payloadAttachments.push({ | |
| type: AppState.pendingAttachment.type, | |
| data: AppState.pendingAttachment.data, | |
| name: AppState.pendingAttachment.name | |
| }); | |
| visualBadge = "Media Appended"; | |
| } | |
| ChatEngine.renderUserMessage(rawUserText || '[Transmitting Media Context]', visualBadge, previewHTMLForChat); | |
| AppState.history.push({ role: 'user', content: payloadStr, badge: visualBadge, previewHTML: previewHTMLForChat }); | |
| } else { | |
| ChatEngine.renderUserMessage(rawUserText, null, ''); | |
| AppState.history.push({ role: 'user', content: rawUserText }); | |
| } | |
| FileProcessor.discardFile(); | |
| ChatEngine.saveHistoryToCloud(); | |
| const activeBotBubble = ChatEngine.renderBotMessage('', false); | |
| UIManager.forceScrollDown(); | |
| // 🎲 Dynamic Pollinations Generator | |
| const cryptographicSeed = Math.floor(Math.random() * 999999999); | |
| // 🧠 Advanced Cognitive System Instructions | |
| let cognitivePrompt = `You are Arjun 2.O, a sophisticated enterprise AI system architected by Abhay Kumar. | |
| LANGUAGE REGULATION: Your default cognitive language is ENGLISH. If the user writes in English, reply in purely professional English. If the user writes in Hindi or Hinglish, reply gracefully in Hindi. Never force Hindi translation if the prompt is English. | |
| IMAGE SYNTHESIS OVERRIDE: | |
| When commanded to render, generate, or create an image/photograph, you MUST reply with a precise markdown image node targeting Pollinations AI. | |
| Formula:  | |
| Available Models: flux, flux-realism, flux-anime, flux-3d. | |
| CRITICAL: The seed parameter is mandatory to bypass cache mechanics. Always render this markdown node.`; | |
| if (AppState.isLiveModeEngaged) { | |
| cognitivePrompt += `\n\n[LIVE ACOUSTIC OVERRIDE]: You are currently engaged in a LIVE, REAL-TIME VOCAL INTERFACE. | |
| MANDATORY direct answers. Maximum brevity. Eliminate complex markdown, massive code blocks, or extended lists. Replicate human telephonic brevity perfectly.`; | |
| } | |
| const secureHistory = AppState.history.slice(0, -1).map(h => ({ role: h.role, content: h.content })); | |
| const transmissionPayload = { | |
| message: payloadStr, | |
| attachments: payloadAttachments, | |
| system_prompt: cognitivePrompt, | |
| history: secureHistory, | |
| max_tokens: AppState.isLiveModeEngaged ? 120 : 4096, | |
| temperature: 0.75 | |
| }; | |
| // Network Transmission Logic | |
| try { | |
| const fetchOp = await fetch(Config.API_ENDPOINT, { | |
| method: 'POST', | |
| headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify(transmissionPayload) | |
| }); | |
| if (!fetchOp.ok) throw new Error("API Matrix Disconnected."); | |
| const networkReader = fetchOp.body.getReader(); | |
| const textDecoder = new TextDecoder(); | |
| let responseBuffer = ""; | |
| AppState.isStreamingActive = true; | |
| while (true) { | |
| const { done, value } = await networkReader.read(); | |
| if (done) break; | |
| const decodedChunk = textDecoder.decode(value, { stream: true }); | |
| const fragmentedLines = decodedChunk.split('\n'); | |
| for (let line of fragmentedLines) { | |
| if (line.startsWith('data: ')) { | |
| const parseableStr = line.substring(6); | |
| if (parseableStr.trim() === '[DONE]') continue; | |
| try { | |
| const jsonSegment = JSON.parse(parseableStr); | |
| if (jsonSegment.choices && jsonSegment.choices[0].delta.content) { | |
| responseBuffer += jsonSegment.choices[0].delta.content; | |
| ChatEngine.updateBotBubble(activeBotBubble, responseBuffer, true); | |
| UIManager.autoScroll(); | |
| } | |
| } catch (e) { } | |
| } | |
| } | |
| } | |
| AppState.isStreamingActive = false; | |
| ChatEngine.updateBotBubble(activeBotBubble, responseBuffer, false); | |
| AppState.history.push({ role: 'bot', content: responseBuffer }); | |
| ChatEngine.saveHistoryToCloud(); | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveStatusText').innerText = "Arjun Transmitting..."; | |
| await VoiceEngine.executeTTS(responseBuffer); | |
| } | |
| } catch(error) { | |
| AppState.isStreamingActive = false; | |
| ChatEngine.updateBotBubble(activeBotBubble, `**System Error:** Communication array offline. Verification needed.`, false); | |
| if (AppState.isLiveModeEngaged) { | |
| document.getElementById('liveStatusText').innerText = "Neural Link Severed."; | |
| } | |
| } | |
| AppState.isProcessing = false; | |
| document.getElementById('masterSendBtn').disabled = false; | |
| UIManager.autoScroll(); | |
| } | |
| }; | |
| // ======================================================================================= | |
| // 9. EVENT DELEGATION & BOOTSTRAP INVOCATION | |
| // ======================================================================================= | |
| // Define global function for welcome screen overlay | |
| window.startArjunApp = function() { | |
| document.getElementById('welcomeOverlay').classList.add('hidden'); | |
| localStorage.setItem('arjun_welcome_seen', 'true'); | |
| setTimeout(() => { document.getElementById('welcomeOverlay').style.display = 'none'; }, 400); | |
| }; | |
| // Bind all interactive elements safely using Event Listeners to avoid scope issues | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // Sidebar Controls | |
| document.getElementById('btnSidebarOpen').addEventListener('click', UIManager.toggleSidebar); | |
| document.getElementById('btnSidebarClose').addEventListener('click', UIManager.toggleSidebar); | |
| document.getElementById('btnLaunchSystem').addEventListener('click', window.startArjunApp); | |
| // Auth Flow Controls | |
| document.getElementById('loginPromptBtn').addEventListener('click', AuthManager.openModal); | |
| document.getElementById('btnAuthClose').addEventListener('click', AuthManager.closeModal); | |
| document.getElementById('btn-tab-login').addEventListener('click', () => AuthManager.switchTab('login')); | |
| document.getElementById('btn-tab-register').addEventListener('click', () => AuthManager.switchTab('register')); | |
| document.getElementById('btn-fire-log-otp').addEventListener('click', () => AuthManager.process('login_send_otp')); | |
| document.getElementById('btn-fire-log-verify').addEventListener('click', () => AuthManager.process('login_verify')); | |
| document.getElementById('btn-fire-reg-otp').addEventListener('click', () => AuthManager.process('register_send_otp')); | |
| document.getElementById('btn-fire-reg-verify').addEventListener('click', () => AuthManager.process('register_verify')); | |
| document.getElementById('btnLogBack').addEventListener('click', () => AuthManager.switchPhase('auth-log-phase-2', 'auth-log-phase-1')); | |
| document.getElementById('btnRegBack').addEventListener('click', () => AuthManager.switchPhase('auth-reg-phase-2', 'auth-reg-phase-1')); | |
| document.getElementById('btnLogout').addEventListener('click', AuthManager.logout); | |
| document.getElementById('btnClearMemory').addEventListener('click', ChatEngine.clearMemory); | |
| // File Attachment Routing | |
| document.getElementById('btnAttachMenuToggle').addEventListener('click', UIManager.toggleAttachMenu); | |
| document.getElementById('opt-image').addEventListener('click', () => FileProcessor.triggerFileSelector('image')); | |
| document.getElementById('opt-video').addEventListener('click', () => FileProcessor.triggerFileSelector('video')); | |
| document.getElementById('opt-audio').addEventListener('click', () => FileProcessor.triggerFileSelector('audio')); | |
| document.getElementById('opt-document').addEventListener('click', () => FileProcessor.triggerFileSelector('document')); | |
| document.getElementById('sys-inp-image').addEventListener('change', function() { FileProcessor.processFile(this, 'image'); }); | |
| document.getElementById('sys-inp-video').addEventListener('change', function() { FileProcessor.processFile(this, 'video'); }); | |
| document.getElementById('sys-inp-audio').addEventListener('change', function() { FileProcessor.processFile(this, 'audio'); }); | |
| document.getElementById('sys-inp-document').addEventListener('change', function() { FileProcessor.processFile(this, 'document'); }); | |
| document.getElementById('btnDiscardFile').addEventListener('click', FileProcessor.discardFile); | |
| // Primary Message Controls | |
| document.getElementById('masterSendBtn').addEventListener('click', ChatEngine.initiateDispatch); | |
| document.getElementById('mainChatInput').addEventListener('keydown', ChatEngine.handleKeyboard); | |
| document.getElementById('mainChatInput').addEventListener('input', function() { UIManager.autoGrowTextArea(this); }); | |
| // Example Prompt Controls | |
| document.getElementById('suggCodeBtn').addEventListener('click', () => ChatEngine.sendExample('Write a secure python script for a modern Flask backend server.')); | |
| document.getElementById('suggImgBtn').addEventListener('click', () => ChatEngine.sendExample('Create a hyper-realistic 8k image of a futuristic Indian smart city.')); | |
| // Voice & STT Options | |
| document.getElementById('smartLangToggle').addEventListener('click', VoiceEngine.switchLanguage); | |
| document.getElementById('masterVoiceBtn').addEventListener('click', VoiceEngine.toggleDictation); | |
| // Live Mode Options | |
| document.getElementById('btnStartLiveMode').addEventListener('click', LiveModeController.initialize); | |
| document.getElementById('btnExitLiveMode').addEventListener('click', LiveModeController.terminate); | |
| document.getElementById('liveMicBtn').addEventListener('click', VoiceEngine.toggleDictation); | |
| // Workspace Logic Hooks | |
| document.getElementById('btnWsClose').addEventListener('click', WorkspaceEngine.closePanel); | |
| document.getElementById('btnWsCopy').addEventListener('click', function() { WorkspaceEngine.executeCopy(this); }); | |
| // Interaction Scroll Listeners | |
| document.getElementById('msgs').addEventListener('scroll', UIManager.checkScrollState); | |
| document.getElementById('scrollTriggerBtn').addEventListener('click', UIManager.forceScrollDown); | |
| // Prevent unwanted default mobile behaviours on double-tap | |
| document.addEventListener('dblclick', function(event) { | |
| event.preventDefault(); | |
| }, { passive: false }); | |
| // Ensure startup screen displays if memory cleared | |
| if(!localStorage.getItem('arjun_welcome_seen')) { | |
| document.getElementById('welcomeOverlay').style.display = 'flex'; | |
| } else { | |
| document.getElementById('welcomeOverlay').style.display = 'none'; | |
| } | |
| // Fire Boot Processes | |
| VoiceEngine.init(); | |
| AuthManager.initUI(); | |
| if(AppState.currentUser) { ChatEngine.syncHistoryFromCloud(); } | |
| }); | |
| </script> | |
| </body> | |
| </html> |