Spaces:
Sleeping
Sleeping
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> | |
| <title>Arjun 2.O - Professional Advanced AI</title> | |
| <!-- ========================================== | |
| EXTERNAL LIBRARIES & FONTS (बाहरी स्रोत) | |
| ========================================== --> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600;700;800&family=Geist+Mono:wght@400;500;600&display=swap" rel="stylesheet"> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css"> | |
| <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> | |
| <!-- ========================================== | |
| MASSIVE CSS STYLESHEET (विस्तृत स्टाइलिंग) | |
| ========================================== --> | |
| <style> | |
| /* --- 1. CSS VARIABLES & RESET --- */ | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| :root { | |
| /* Professional Tiranga Brand Colors */ | |
| --brand-saffron: #FF9933; | |
| --brand-saffron-light: rgba(255, 153, 51, 0.1); | |
| --brand-saffron-dark: #e68a2e; | |
| --brand-white: #FFFFFF; | |
| --brand-green: #138808; | |
| --brand-green-light: rgba(19, 136, 8, 0.1); | |
| --brand-green-dark: #0f6e06; | |
| /* UI Colors */ | |
| --bg-main: #ffffff; | |
| --bg-secondary: #f8f9fa; | |
| --bg-tertiary: #f1f3f5; | |
| --text-primary: #111827; | |
| --text-secondary: #4b5563; | |
| --text-tertiary: #9ca3af; | |
| /* Borders & Shadows */ | |
| --border-light: rgba(0, 0, 0, 0.08); | |
| --border-hover: rgba(0, 0, 0, 0.15); | |
| --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-float: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04); | |
| /* Typography */ | |
| --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-sm: 8px; | |
| --radius-md: 12px; | |
| --radius-lg: 16px; | |
| --radius-xl: 24px; | |
| --radius-full: 9999px; | |
| /* Transitions */ | |
| --trans-fast: 0.15s cubic-bezier(0.4, 0, 0.2, 1); | |
| --trans-smooth: 0.3s cubic-bezier(0.4, 0, 0.2, 1); | |
| --trans-spring: 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275); | |
| } | |
| html, body { | |
| height: 100dvh; | |
| width: 100vw; | |
| overflow: hidden; | |
| background-color: var(--bg-main); | |
| color: var(--text-primary); | |
| font-family: var(--font-sans); | |
| -webkit-font-smoothing: antialiased; | |
| -moz-osx-font-smoothing: grayscale; | |
| } | |
| /* --- 2. BACKGROUND & AMBIENT EFFECTS --- */ | |
| .ambient-bg { | |
| position: fixed; | |
| inset: 0; | |
| z-index: 0; | |
| overflow: hidden; | |
| pointer-events: none; | |
| background: linear-gradient(135deg, var(--brand-saffron-light) 0%, rgba(255,255,255,1) 50%, var(--brand-green-light) 100%); | |
| } | |
| .orb { | |
| position: absolute; | |
| border-radius: 50%; | |
| filter: blur(120px); | |
| opacity: 0.4; | |
| animation: floatOrb 30s ease-in-out infinite alternate; | |
| } | |
| .orb-saffron { | |
| width: 600px; height: 600px; | |
| background: radial-gradient(circle, rgba(255,153,51,0.25), transparent 70%); | |
| top: -150px; left: -100px; | |
| } | |
| .orb-green { | |
| width: 500px; height: 500px; | |
| background: radial-gradient(circle, rgba(19,136,8,0.2), transparent 70%); | |
| bottom: -100px; right: -50px; | |
| animation-delay: -15s; | |
| } | |
| @keyframes floatOrb { | |
| 0% { transform: translate(0, 0) scale(1); } | |
| 33% { transform: translate(40px, 60px) scale(1.1); } | |
| 66% { transform: translate(-30px, 40px) scale(0.95); } | |
| 100% { transform: translate(50px, -20px) scale(1.05); } | |
| } | |
| /* --- 3. LAYOUT ARCHITECTURE --- */ | |
| .app-container { | |
| position: relative; | |
| z-index: 1; | |
| display: flex; | |
| height: 100dvh; | |
| width: 100vw; | |
| overflow: hidden; | |
| background: rgba(255, 255, 255, 0.4); | |
| backdrop-filter: blur(20px); | |
| -webkit-backdrop-filter: blur(20px); | |
| } | |
| /* --- 4. SIDEBAR STYLES --- */ | |
| .sidebar { | |
| width: 300px; | |
| display: flex; | |
| flex-direction: column; | |
| background: rgba(248, 249, 250, 0.85); | |
| border-right: 1px solid var(--border-light); | |
| flex-shrink: 0; | |
| z-index: 100; | |
| transition: transform var(--trans-smooth); | |
| backdrop-filter: blur(10px); | |
| } | |
| .sidebar-header { | |
| padding: 24px 20px; | |
| border-bottom: 1px solid var(--border-light); | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| } | |
| .brand-logo { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| text-decoration: none; | |
| } | |
| .brand-logo img { | |
| width: 48px; | |
| height: 48px; | |
| border-radius: var(--radius-md); | |
| object-fit: cover; | |
| box-shadow: var(--shadow-sm); | |
| border: 2px solid var(--bg-main); | |
| transition: transform var(--trans-spring); | |
| } | |
| .brand-logo:hover img { transform: scale(1.05) rotate(-2deg); } | |
| .brand-text { display: flex; flex-direction: column; } | |
| .brand-title { font-family: var(--font-sans); font-weight: 800; font-size: 18px; color: var(--text-primary); letter-spacing: -0.5px; line-height: 1.1; } | |
| .brand-subtitle { font-size: 11px; font-weight: 700; 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: 2px; } | |
| .sidebar-content { flex: 1; overflow-y: auto; padding: 20px; display: flex; flex-direction: column; gap: 24px; } | |
| .sidebar-content::-webkit-scrollbar { width: 4px; } | |
| .sidebar-content::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.1); border-radius: 10px; } | |
| /* Auth UI */ | |
| .auth-section { background: var(--bg-main); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 16px; box-shadow: var(--shadow-sm); } | |
| .btn-secure-login { width: 100%; padding: 12px; background: var(--text-primary); color: var(--brand-white); border: none; border-radius: var(--radius-md); font-size: 14px; font-weight: 600; cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px; transition: var(--trans-fast); } | |
| .btn-secure-login:hover { background: #000; transform: translateY(-2px); box-shadow: var(--shadow-md); } | |
| .user-profile-card { display: none; align-items: center; gap: 12px; } | |
| .user-avatar-circle { width: 40px; height: 40px; 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: 700; font-size: 16px; box-shadow: var(--shadow-sm); } | |
| .user-info-text { flex: 1; overflow: hidden; } | |
| .user-email-text { font-size: 13px; font-weight: 600; color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .user-status-text { font-size: 11px; color: var(--brand-green); font-weight: 500; } | |
| .btn-logout { background: rgba(239, 68, 68, 0.1); color: #ef4444; border: none; width: 32px; height: 32px; border-radius: var(--radius-md); display: flex; align-items: center; justify-content: center; cursor: pointer; transition: var(--trans-fast); } | |
| .btn-logout:hover { background: #ef4444; color: #fff; } | |
| /* Settings & Info */ | |
| .brand-info-box { background: var(--bg-main); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 24px 16px; text-align: center; box-shadow: var(--shadow-sm); margin-top: auto; position: relative; overflow: hidden; } | |
| .brand-info-box::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3px; background: linear-gradient(90deg, var(--brand-saffron), var(--brand-white), var(--brand-green)); } | |
| .brand-info-box img { width: 64px; height: 64px; border-radius: var(--radius-lg); object-fit: cover; margin-bottom: 12px; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); } | |
| .brand-info-box h3 { font-size: 16px; font-weight: 700; color: var(--text-primary); margin-bottom: 4px; } | |
| .brand-info-box p { font-size: 10px; font-weight: 700; color: var(--text-tertiary); letter-spacing: 0.5px; } | |
| .btn-clear-chat { width: 100%; padding: 14px; border-radius: var(--radius-md); background: var(--bg-main); border: 1px solid var(--border-light); color: var(--text-secondary); font-size: 13px; font-weight: 600; cursor: pointer; transition: var(--trans-fast); display: flex; align-items: center; justify-content: center; gap: 8px; } | |
| .btn-clear-chat:hover { background: #fee2e2; color: #ef4444; border-color: #fca5a5; } | |
| /* --- 5. MAIN CHAT INTERFACE --- */ | |
| .chat-container { flex: 1; display: flex; flex-direction: column; position: relative; min-width: 300px; transition: width var(--trans-smooth); } | |
| .chat-header { padding: 16px 24px; border-bottom: 1px solid var(--border-light); background: rgba(255, 255, 255, 0.8); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); display: flex; align-items: center; justify-content: space-between; z-index: 10; } | |
| .header-left { display: flex; align-items: center; gap: 12px; } | |
| .mobile-toggle { display: none; background: none; border: none; cursor: pointer; color: var(--text-primary); padding: 4px; } | |
| .ai-status-badge { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 600; color: var(--text-primary); padding: 6px 14px; background: var(--bg-secondary); border-radius: var(--radius-full); border: 1px solid var(--border-light); } | |
| .status-indicator { width: 8px; height: 8px; border-radius: 50%; background: var(--brand-green); box-shadow: 0 0 0 3px rgba(19, 136, 8, 0.2); animation: pulseStatus 2s infinite; } | |
| @keyframes pulseStatus { 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); } } | |
| /* LIVE BUTTON */ | |
| .btn-live-mode { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); color: white; border: none; padding: 8px 16px; border-radius: var(--radius-full); font-size: 13px; font-weight: 700; display: flex; align-items: center; gap: 8px; cursor: pointer; box-shadow: 0 4px 12px rgba(239, 68, 68, 0.3); transition: var(--trans-spring); } | |
| .btn-live-mode:hover { transform: translateY(-2px) scale(1.02); box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4); } | |
| .btn-live-mode svg { width: 16px; height: 16px; animation: blinkSVG 1.5s infinite; } | |
| @keyframes blinkSVG { 50% { opacity: 0.5; } } | |
| /* Messages Area */ | |
| .messages-area { flex: 1; overflow-y: auto; padding: 40px 10%; display: flex; flex-direction: column; gap: 32px; scroll-behavior: smooth; } | |
| .messages-area::-webkit-scrollbar { width: 6px; } | |
| .messages-area::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 10px; } | |
| /* Welcome Screen */ | |
| .welcome-hero { display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 60px 20px; margin: auto; animation: slideUpFade 0.6s cubic-bezier(0.16, 1, 0.3, 1) both; max-width: 700px; } | |
| @keyframes slideUpFade { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: none; } } | |
| .hero-icon-wrap { position: relative; margin-bottom: 32px; } | |
| .hero-icon-wrap img { width: 96px; height: 96px; border-radius: 28px; object-fit: cover; box-shadow: var(--shadow-lg); border: 2px solid var(--bg-main); position: relative; z-index: 2; } | |
| .hero-icon-glow { position: absolute; inset: -10px; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); filter: blur(20px); opacity: 0.3; border-radius: 35px; z-index: 1; } | |
| .hero-title { font-size: 38px; font-weight: 800; letter-spacing: -1.5px; margin-bottom: 16px; color: var(--text-primary); } | |
| .hero-subtitle { font-size: 16px; color: var(--text-secondary); line-height: 1.6; margin-bottom: 32px; } | |
| .suggestion-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 16px; width: 100%; } | |
| .suggestion-card { background: rgba(255, 255, 255, 0.6); backdrop-filter: blur(10px); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 20px; cursor: pointer; text-align: left; transition: var(--trans-smooth); box-shadow: var(--shadow-sm); } | |
| .suggestion-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-md); border-color: var(--brand-saffron); background: rgba(255, 255, 255, 0.9); } | |
| .sugg-icon { font-size: 24px; margin-bottom: 12px; display: inline-block; } | |
| .sugg-title { font-size: 14px; font-weight: 700; color: var(--text-primary); margin-bottom: 6px; } | |
| .sugg-desc { font-size: 13px; color: var(--text-secondary); line-height: 1.4; } | |
| /* Chat Bubbles */ | |
| .message-row { display: flex; gap: 16px; max-width: 85%; animation: msgPopIn 0.4s var(--trans-spring) both; } | |
| @keyframes msgPopIn { from { opacity: 0; transform: translateY(20px) scale(0.95); } to { opacity: 1; transform: none; } } | |
| .message-row.user-msg { flex-direction: row-reverse; align-self: flex-end; } | |
| .message-row.bot-msg { align-self: flex-start; } | |
| .avatar-box { width: 38px; height: 38px; border-radius: var(--radius-sm); flex-shrink: 0; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; box-shadow: var(--shadow-sm); } | |
| .user-msg .avatar-box { background: var(--text-primary); color: var(--brand-white); } | |
| .bot-msg .avatar-box { background: var(--bg-main); border: 1px solid var(--border-light); padding: 2px; } | |
| .bot-msg .avatar-box img { width: 100%; height: 100%; border-radius: 6px; object-fit: cover; } | |
| .msg-content-wrapper { display: flex; flex-direction: column; gap: 6px; max-width: 100%; } | |
| .user-msg .msg-content-wrapper { align-items: flex-end; } | |
| .file-badge { font-size: 11px; font-weight: 600; color: var(--brand-saffron); background: var(--brand-saffron-light); padding: 4px 10px; border-radius: var(--radius-full); margin-bottom: 4px; display: inline-flex; align-items: center; gap: 4px; } | |
| .bubble { padding: 16px 22px; font-size: 15px; line-height: 1.6; position: relative; word-wrap: break-word; } | |
| .user-msg .bubble { background: var(--bg-tertiary); color: var(--text-primary); border-radius: 20px 20px 4px 20px; border: 1px solid var(--border-light); } | |
| .bot-msg .bubble { background: var(--bg-main); border: 1px solid var(--border-light); border-radius: 4px 20px 20px 20px; box-shadow: var(--shadow-sm); width: 100%; } | |
| /* Bot Actions (TTS & Copy) */ | |
| .msg-actions-bar { display: flex; gap: 8px; margin-top: 6px; padding-left: 12px; opacity: 0; transition: opacity var(--trans-smooth); flex-wrap: wrap; } | |
| .bot-msg:hover .msg-actions-bar { opacity: 1; } | |
| .action-chip { background: var(--bg-secondary); border: 1px solid var(--border-light); color: var(--text-secondary); font-size: 12px; font-weight: 600; cursor: pointer; display: flex; align-items: center; gap: 6px; padding: 6px 14px; border-radius: var(--radius-full); transition: var(--trans-fast); } | |
| .action-chip:hover { background: var(--bg-tertiary); color: var(--text-primary); transform: translateY(-1px); } | |
| .action-chip.btn-tts { color: var(--brand-green); border-color: rgba(19, 136, 8, 0.2); background: var(--brand-green-light); } | |
| .action-chip.btn-tts:hover { background: rgba(19, 136, 8, 0.15); } | |
| /* Markdown Styling inside Bubble */ | |
| .bubble p { margin-bottom: 14px; } | |
| .bubble p:last-child { margin-bottom: 0; } | |
| .bubble strong { font-weight: 700; color: var(--text-primary); } | |
| .bubble code { font-family: var(--font-mono); background: var(--bg-tertiary); padding: 3px 6px; border-radius: 6px; font-size: 0.85em; color: #d946ef; } | |
| .bubble img { max-width: 100%; border-radius: 12px; margin: 12px 0; box-shadow: var(--shadow-md); border: 1px solid var(--border-light); display: block; } | |
| .bubble pre { background: #282c34; color: #abb2bf; padding: 16px; border-radius: 12px; overflow-x: auto; margin: 16px 0; font-family: var(--font-mono); font-size: 13.5px; line-height: 1.5; } | |
| .bubble pre code { background: transparent; color: inherit; padding: 0; } | |
| /* Generating Code UI */ | |
| .gen-code-box { background: #1e1e2e; border-radius: 12px; border: 1px solid #313244; overflow: hidden; margin: 16px 0; box-shadow: var(--shadow-md); } | |
| .gen-code-hdr { background: #11111b; padding: 10px 16px; font-size: 12px; font-weight: 600; color: #a6adc8; display: flex; align-items: center; gap: 10px; border-bottom: 1px solid #313244; font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 1px; } | |
| .spinner-mini { width: 14px; height: 14px; border: 2px solid rgba(255,255,255,0.1); border-top-color: var(--brand-saffron); border-radius: 50%; animation: spin 1s linear infinite; } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .gen-code-box pre { margin: 0; background: transparent; } | |
| /* Workspace Trigger Card */ | |
| .workspace-card { background: var(--bg-secondary); border: 1px solid var(--border-light); border-radius: 12px; padding: 16px; margin: 16px 0; display: flex; align-items: center; justify-content: space-between; box-shadow: var(--shadow-sm); } | |
| .ws-card-info { display: flex; align-items: center; gap: 14px; } | |
| .ws-card-icon { width: 48px; height: 48px; border-radius: 10px; background: var(--bg-main); display: flex; align-items: center; justify-content: center; font-size: 24px; box-shadow: var(--shadow-sm); border: 1px solid var(--border-light); color: var(--text-primary); } | |
| .ws-card-text h4 { font-size: 15px; font-weight: 700; margin-bottom: 2px; } | |
| .ws-card-text p { font-size: 12px; color: var(--text-tertiary); font-family: var(--font-mono); text-transform: uppercase; letter-spacing: 0.5px; } | |
| .btn-view-ws { padding: 10px 20px; background: var(--text-primary); color: var(--brand-white); border: none; border-radius: var(--radius-sm); font-size: 13px; font-weight: 600; cursor: pointer; transition: var(--trans-spring); } | |
| .btn-view-ws:hover { transform: scale(1.05); background: var(--brand-saffron); box-shadow: 0 4px 12px rgba(255,153,51,0.3); } | |
| /* Scroll Bottom Button */ | |
| .btn-scroll-down { position: absolute; bottom: 130px; left: 50%; transform: translateX(-50%) translateY(20px); width: 44px; height: 44px; border-radius: 50%; background: var(--glass-bg); backdrop-filter: blur(10px); border: 1px solid var(--border-light); box-shadow: var(--shadow-md); display: flex; align-items: center; justify-content: center; cursor: pointer; color: var(--text-secondary); opacity: 0; pointer-events: none; transition: var(--trans-smooth); z-index: 20; } | |
| .btn-scroll-down.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); } | |
| .btn-scroll-down:hover { background: var(--bg-main); color: var(--text-primary); transform: translateX(-50%) translateY(-5px); box-shadow: var(--shadow-lg); } | |
| /* --- 6. INPUT AREA & ATTACHMENTS --- */ | |
| .input-region { flex-shrink: 0; padding: 0 40px 30px; background: transparent; position: relative; z-index: 20; } | |
| .input-box-wrapper { display: flex; flex-direction: column; background: var(--glass-bg); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border: 1px solid var(--border-light); border-radius: var(--radius-xl); box-shadow: var(--shadow-float); transition: var(--trans-smooth); } | |
| .input-box-wrapper:focus-within { border-color: var(--brand-saffron); box-shadow: 0 15px 35px -5px rgba(255,153,51,0.15), 0 0 0 3px var(--brand-saffron-light); } | |
| /* Attachment Preview */ | |
| .file-preview-strip { display: none; padding: 16px 24px 0; align-items: center; gap: 12px; } | |
| .file-preview-strip.active { display: flex; animation: fadeIn 0.3s ease; } | |
| .preview-thumb { width: 44px; height: 44px; 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-secondary); font-size: 20px; } | |
| .preview-details { flex: 1; } | |
| .preview-name { font-size: 13px; font-weight: 600; color: var(--text-primary); max-width: 200px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } | |
| .btn-remove-file { background: rgba(239, 68, 68, 0.1); border: none; width: 28px; height: 28px; border-radius: 50%; cursor: pointer; display: flex; align-items: center; justify-content: center; color: #ef4444; transition: var(--trans-fast); } | |
| .btn-remove-file:hover { background: #ef4444; color: #fff; } | |
| .input-controls-row { display: flex; align-items: flex-end; gap: 10px; padding: 12px 16px 12px 20px; } | |
| .chat-textarea { flex: 1; border: none; outline: none; background: transparent; font-family: var(--font-sans); font-size: 15px; color: var(--text-primary); line-height: 1.5; resize: none; max-height: 180px; min-height: 24px; padding: 10px 0; margin-left: 8px;} | |
| .chat-textarea::placeholder { color: var(--text-tertiary); } | |
| /* Attachment Menu (Gemini Style) */ | |
| .attach-menu-container { position: relative; } | |
| .btn-icon { width: 40px; height: 40px; 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(--trans-fast); position: relative; } | |
| .btn-icon:hover { background: var(--bg-secondary); color: var(--text-primary); } | |
| .btn-icon svg { width: 22px; height: 22px; } | |
| .attach-popup { position: absolute; bottom: 55px; left: 0; background: rgba(255,255,255,0.95); backdrop-filter: blur(20px); border: 1px solid var(--border-light); border-radius: var(--radius-lg); padding: 8px; box-shadow: var(--shadow-float); display: none; flex-direction: column; gap: 4px; z-index: 100; min-width: 180px; transform: translateY(10px) scale(0.95); opacity: 0; transition: all var(--trans-fast); transform-origin: bottom left; } | |
| .attach-popup.show { display: flex; transform: translateY(0) scale(1); opacity: 1; } | |
| .attach-option { display: flex; align-items: center; gap: 12px; padding: 12px 16px; border-radius: var(--radius-sm); cursor: pointer; font-size: 14px; font-weight: 600; color: var(--text-primary); transition: var(--trans-fast); } | |
| .attach-option:hover { background: var(--bg-secondary); color: var(--brand-saffron); } | |
| .attach-option span.ico { font-size: 18px; } | |
| .hidden-file-input { display: none; } | |
| /* Voice Mic & SMART LANGUAGE TOGGLE */ | |
| .btn-lang-toggle { font-size: 12px; font-weight: 700; color: var(--text-secondary); border: 1px solid var(--border-light); background: var(--bg-main); border-radius: 8px; padding: 4px 8px; cursor: pointer; transition: 0.2s; display: flex; align-items: center; gap: 4px;} | |
| .btn-lang-toggle:hover { background: var(--brand-saffron-light); color: var(--brand-saffron-dark); border-color: var(--brand-saffron); } | |
| .btn-lang-toggle.active-hi { background: var(--brand-green-light); color: var(--brand-green-dark); border-color: var(--brand-green); } | |
| .btn-mic.is-listening { color: #ef4444; background: rgba(239, 68, 68, 0.1); animation: pulseMic 1.5s infinite; } | |
| @keyframes pulseMic { 0% { box-shadow: 0 0 0 0 rgba(239,68,68,0.4); } 70% { box-shadow: 0 0 0 10px rgba(239,68,68,0); } 100% { box-shadow: 0 0 0 0 rgba(239,68,68,0); } } | |
| /* Send Button */ | |
| .btn-send { width: 44px; height: 44px; border-radius: 14px; 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(--trans-spring); box-shadow: 0 4px 15px rgba(19, 136, 8, 0.2); } | |
| .btn-send svg { width: 18px; height: 18px; fill: white; transition: transform 0.2s; } | |
| .btn-send:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(255, 153, 51, 0.3); } | |
| .btn-send:hover svg { transform: translate(2px, -2px); } | |
| .btn-send:disabled { opacity: 0.5; cursor: not-allowed; filter: grayscale(100%); transform: none; box-shadow: none; } | |
| .footer-branding { text-align: center; font-size: 11px; color: var(--text-tertiary); margin-top: 14px; font-weight: 500; letter-spacing: 0.5px; } | |
| /* --- 7. WORKSPACE (CODE EDITOR PANEL) --- */ | |
| .workspace-panel { width: 0; background: var(--bg-main); border-left: 1px solid var(--border-light); display: flex; flex-direction: column; transition: width var(--trans-smooth); overflow: hidden; box-shadow: -10px 0 30px rgba(0,0,0,0.03); z-index: 25; } | |
| .workspace-panel.active { width: 50%; } | |
| .ws-header { padding: 16px 24px; border-bottom: 1px solid var(--border-light); background: var(--bg-main); display: flex; align-items: center; justify-content: space-between; flex-shrink: 0; } | |
| .ws-title { font-size: 15px; font-weight: 700; display: flex; align-items: center; gap: 10px; color: var(--text-primary); } | |
| .ws-actions-group { display: flex; gap: 10px; } | |
| .btn-ws-action { padding: 8px 16px; border-radius: var(--radius-sm); background: var(--bg-secondary); border: 1px solid var(--border-light); cursor: pointer; display: flex; align-items: center; gap: 8px; color: var(--text-primary); transition: var(--trans-fast); font-size: 13px; font-weight: 600; } | |
| .btn-ws-action:hover { background: var(--bg-tertiary); } | |
| .btn-ws-close { padding: 8px; } | |
| .btn-ws-close:hover { color: #ef4444; background: #fee2e2; border-color: #fca5a5; } | |
| .ws-editor-area { flex: 1; overflow-y: auto; background: var(--bg-secondary); padding: 24px; } | |
| .ws-editor-area pre { margin: 0; padding: 24px; background: #282c34; border: 1px solid var(--border-light); border-radius: var(--radius-lg); font-family: var(--font-mono); font-size: 14px; line-height: 1.6; overflow-x: auto; box-shadow: var(--shadow-md); color: #abb2bf; } | |
| /* --- 8. LIVE MODE OVERLAY (नया फीचर) --- */ | |
| .live-overlay { position: fixed; inset: 0; background: rgba(255, 255, 255, 0.98); z-index: 9000; display: flex; flex-direction: column; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity var(--trans-smooth); } | |
| .live-overlay.active { opacity: 1; pointer-events: auto; } | |
| .live-close-btn { position: absolute; top: 30px; right: 30px; background: rgba(0,0,0,0.05); border: none; width: 44px; height: 44px; border-radius: 50%; font-size: 20px; cursor: pointer; transition: 0.2s; } | |
| .live-close-btn:hover { background: rgba(239, 68, 68, 0.1); color: #ef4444; transform: rotate(90deg); } | |
| .live-orb-container { position: relative; width: 200px; height: 200px; display: flex; align-items: center; justify-content: center; margin-bottom: 40px; } | |
| .live-orb-core { width: 100px; height: 100px; border-radius: 50%; background: linear-gradient(135deg, var(--brand-saffron), var(--brand-green)); box-shadow: 0 0 40px rgba(19, 136, 8, 0.4); position: relative; z-index: 2; transition: transform 0.1s; } | |
| .live-orb-ring { position: absolute; inset: 0; border: 2px solid var(--brand-saffron); border-radius: 50%; opacity: 0; animation: ripple 2s cubic-bezier(0.4, 0, 0.2, 1) infinite; } | |
| .live-orb-ring:nth-child(2) { animation-delay: 0.6s; border-color: var(--brand-green); } | |
| .live-orb-ring:nth-child(3) { animation-delay: 1.2s; border-color: var(--text-primary); } | |
| @keyframes ripple { 0% { transform: scale(0.5); opacity: 1; } 100% { transform: scale(1.5); opacity: 0; } } | |
| .live-status-text { font-size: 24px; font-weight: 800; color: var(--text-primary); margin-bottom: 12px; font-family: var(--font-sans); } | |
| .live-transcript { font-size: 16px; color: var(--text-secondary); max-width: 600px; text-align: center; line-height: 1.5; min-height: 48px; } | |
| .live-controls { display: flex; gap: 20px; margin-top: 40px; } | |
| .btn-live-action { width: 64px; height: 64px; border-radius: 50%; border: none; display: flex; align-items: center; justify-content: center; cursor: pointer; box-shadow: var(--shadow-md); transition: var(--trans-spring); } | |
| .btn-live-action.mic { background: var(--bg-main); border: 2px solid var(--border-light); color: var(--text-primary); } | |
| .btn-live-action.mic:hover { transform: translateY(-5px); border-color: var(--brand-saffron); color: var(--brand-saffron); } | |
| .btn-live-action.mic.recording { background: #ef4444; color: white; border-color: #ef4444; animation: pulseMic 1s infinite; } | |
| .btn-live-action svg { width: 28px; height: 28px; } | |
| /* --- 9. AUTHENTICATION MODAL --- */ | |
| .modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,0.4); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); z-index: 5000; display: none; justify-content: center; align-items: center; animation: fadeIn 0.3s ease; } | |
| .modal-box { background: var(--bg-main); padding: 40px; border-radius: var(--radius-xl); width: 90%; max-width: 440px; box-shadow: var(--shadow-float); position: relative; border: 1px solid rgba(255,255,255,0.2); } | |
| .modal-close { position: absolute; top: 20px; right: 20px; background: var(--bg-secondary); border: none; width: 32px; height: 32px; border-radius: 50%; font-size: 16px; color: var(--text-secondary); cursor: pointer; transition: 0.2s; } | |
| .modal-close:hover { background: #fee2e2; color: #ef4444; } | |
| .modal-header-title { text-align: center; margin-bottom: 30px; font-family: var(--font-sans); font-size: 26px; font-weight: 800; color: var(--text-primary); } | |
| .tab-group { display: flex; background: var(--bg-secondary); padding: 6px; border-radius: var(--radius-lg); margin-bottom: 24px; } | |
| .tab-btn { flex: 1; padding: 12px; text-align: center; border-radius: var(--radius-md); font-size: 14px; font-weight: 700; color: var(--text-secondary); cursor: pointer; transition: var(--trans-fast); } | |
| .tab-btn.active { background: var(--bg-main); color: var(--text-primary); box-shadow: var(--shadow-sm); } | |
| .auth-form-step { display: none; flex-direction: column; animation: slideIn 0.3s ease; } | |
| .auth-form-step.active { display: flex; } | |
| .input-field { width: 100%; padding: 16px; border: 1.5px solid var(--border-light); border-radius: var(--radius-md); font-size: 15px; outline: none; background: var(--bg-secondary); transition: var(--trans-fast); font-family: var(--font-sans); margin-bottom: 16px; } | |
| .input-field:focus { border-color: var(--brand-saffron); background: var(--bg-main); box-shadow: 0 0 0 4px var(--brand-saffron-light); } | |
| .btn-submit-auth { width: 100%; padding: 16px; background: var(--text-primary); color: var(--brand-white); border: none; border-radius: var(--radius-md); font-size: 15px; font-weight: 700; cursor: pointer; transition: var(--trans-fast); margin-top: 8px; } | |
| .btn-submit-auth:hover { background: #000; transform: translateY(-2px); box-shadow: var(--shadow-md); } | |
| .btn-text-back { width: 100%; padding: 12px; background: transparent; color: var(--text-tertiary); border: none; font-size: 14px; font-weight: 600; cursor: pointer; margin-top: 8px; transition: 0.2s; } | |
| .btn-text-back:hover { color: var(--text-primary); } | |
| @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } | |
| @keyframes slideIn { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: none; } } | |
| /* --- 10. RESPONSIVE DESIGN (MOBILE ADAPTATION) --- */ | |
| @media (max-width: 900px) { | |
| .sidebar { position: absolute; left: -300px; z-index: 1000; height: 100%; box-shadow: var(--shadow-float); } | |
| .sidebar.show { left: 0; } | |
| .mobile-toggle { display: block; } | |
| .workspace-panel.active { position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 2000; } | |
| .messages-area { padding: 20px 15px; } | |
| .input-region { padding: 10px 15px 15px; } | |
| .message-row { max-width: 95%; } | |
| .btn-scroll-down { bottom: 120px; } | |
| .hero-title { font-size: 28px; } | |
| .suggestion-grid { grid-template-columns: 1fr; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <!-- 1. NOTIFICATIONS --> | |
| <div id="toast-container"></div> | |
| <!-- 2. INITIAL WELCOME OVERLAY --> | |
| <div class="welcome-overlay" id="welcomeOverlay"> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo"> | |
| <h1>Welcome to Arjun 2.O</h1> | |
| <p>Professional Edition. Featuring Live Voice Mode, Workspace Coding, and high-fidelity Image Generation.</p> | |
| <button class="start-btn" onclick="startArjunApp()">Experience Now</button> | |
| </div> | |
| <!-- 3. LIVE MODE OVERLAY (नया फीचर) --> | |
| <div class="live-overlay" id="liveOverlay"> | |
| <button class="live-close-btn" onclick="stopLiveMode()" title="Exit Live Mode"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| <div class="live-orb-container"> | |
| <div class="live-orb-ring"></div> | |
| <div class="live-orb-ring"></div> | |
| <div class="live-orb-ring"></div> | |
| <div class="live-orb-core" id="liveOrb"></div> | |
| </div> | |
| <div class="live-status-text" id="liveStatusText">Listening...</div> | |
| <div class="live-transcript" id="liveTranscriptText">Speak to Arjun directly.</div> | |
| <div class="live-controls"> | |
| <button class="btn-live-action mic" id="liveMicBtn" onclick="toggleLiveMic()"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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> | |
| <!-- 4. BACKGROUND EFFECTS --> | |
| <div class="ambient-bg"><div class="orb orb-saffron"></div><div class="orb orb-green"></div></div> | |
| <!-- 5. MAIN APP ARCHITECTURE --> | |
| <div class="app-container"> | |
| <!-- A. SIDEBAR --> | |
| <aside class="sidebar" id="sidebar"> | |
| <div class="sidebar-header"> | |
| <a href="#" class="brand-logo"> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo"> | |
| <div class="brand-text"> | |
| <span class="brand-title">ARJUN 2.O</span> | |
| <span class="brand-subtitle">Professional</span> | |
| </div> | |
| </a> | |
| <button class="mobile-toggle" onclick="toggleSidebar()"> | |
| <svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" 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-content"> | |
| <!-- Auth Section --> | |
| <div class="auth-section"> | |
| <button id="loginPromptBtn" class="btn-secure-login" onclick="openAuthModal()"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"><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-card"> | |
| <div class="user-avatar-circle" id="uAvatar">U</div> | |
| <div class="user-info-text"> | |
| <div class="user-email-text" id="uEmail">user@email.com</div> | |
| <div class="user-status-text">Verified Access</div> | |
| </div> | |
| <button class="btn-logout" onclick="logout()" title="Secure Logout"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"><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> | |
| <!-- Brand Info Box --> | |
| <div class="brand-info-box"> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Identity"> | |
| <h3>Arjun Intelligence</h3> | |
| <p>ENGINEERED BY ABHAY KUMAR</p> | |
| </div> | |
| <button class="btn-clear-chat" onclick="clearMemory()"> | |
| <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><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> | |
| Clear Memory Context | |
| </button> | |
| </div> | |
| </aside> | |
| <!-- B. MAIN CHAT INTERFACE --> | |
| <main class="chat-container"> | |
| <!-- Header --> | |
| <header class="chat-header"> | |
| <div class="header-left"> | |
| <button class="mobile-toggle" onclick="toggleSidebar()"> | |
| <svg viewBox="0 0 24 24" width="24" height="24" stroke="currentColor" stroke-width="2" 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="ai-status-badge"> | |
| <div class="status-indicator"></div> | |
| Arjun Core Online | |
| </div> | |
| </div> | |
| <!-- NEW LIVE MODE BUTTON --> | |
| <button class="btn-live-mode" onclick="startLiveMode()"> | |
| <svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><circle cx="12" cy="12" r="10"></circle><circle cx="12" cy="12" r="4" fill="currentColor"></circle></svg> | |
| LIVE MODE | |
| </button> | |
| </header> | |
| <!-- Messages Flow --> | |
| <div class="messages-area" id="msgs" onscroll="checkScroll()"> | |
| <!-- Internal Hero/Welcome --> | |
| <div class="welcome-hero" id="welcome"> | |
| <div class="hero-icon-wrap"> | |
| <div class="hero-icon-glow"></div> | |
| <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo"> | |
| </div> | |
| <h2 class="hero-title">How can I assist you today?</h2> | |
| <p class="hero-subtitle">Experience next-generation reasoning, multi-file processing, and workspace code generation crafted by Abhay Kumar.</p> | |
| <div id="guestBadge" class="file-badge" style="margin-bottom:24px; display:none; background:#fee2e2; color:#ef4444;"> | |
| <span>⚠️ Guest Mode: <span id="guestCountDisplay">0</span>/3 Queries Used. Login Required.</span> | |
| </div> | |
| <div class="suggestion-grid"> | |
| <div class="suggestion-card" onclick="sendEx('Write a python script for a highly secure Flask backend server.')"> | |
| <span class="sugg-icon">💻</span> | |
| <div class="sugg-title">Generate Production Code</div> | |
| <div class="sugg-desc">Create secure, workspace-ready scripts instantly.</div> | |
| </div> | |
| <div class="suggestion-card" onclick="sendEx('Create a hyper-realistic 8k image of a futuristic Indian metropolis.')"> | |
| <span class="sugg-icon">🎨</span> | |
| <div class="sugg-title">Render Stunning Visions</div> | |
| <div class="sugg-desc">Utilize advanced AI to paint your imagination.</div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <button class="btn-scroll-down" id="scrollBtn" onclick="forceScrollDown()"> | |
| <svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><polyline points="6 9 12 15 18 9"></polyline></svg> | |
| </button> | |
| <!-- Input Region --> | |
| <div class="input-region"> | |
| <div class="input-box-wrapper"> | |
| <!-- File Preview Strip --> | |
| <div class="file-preview-strip" id="imgPrev"> | |
| <img class="preview-thumb" id="imgThumb" src="" style="display:none;"> | |
| <div class="preview-thumb" id="fileIcon" style="display:none;">📄</div> | |
| <div class="preview-details"> | |
| <div class="preview-name" id="imgName">document.pdf</div> | |
| </div> | |
| <button class="btn-remove-file" onclick="removeImg()"> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| </div> | |
| <div class="input-controls-row"> | |
| <!-- Attachment Menu System --> | |
| <div class="attach-menu-container"> | |
| <button class="btn-icon" onclick="toggleAttachMenu()" title="Attach File"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 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="attach-popup" id="attachMenu"> | |
| <div class="attach-option" onclick="triggerInput('image')"><span class="ico">🖼️</span> High-Res Image</div> | |
| <div class="attach-option" onclick="triggerInput('video')"><span class="ico">🎥</span> Video Media</div> | |
| <div class="attach-option" onclick="triggerInput('audio')"><span class="ico">🎵</span> Audio Track</div> | |
| <div class="attach-option" onclick="triggerInput('document')"><span class="ico">📄</span> Code / Document</div> | |
| </div> | |
| <!-- Hidden Inputs --> | |
| <input type="file" id="inp-image" class="hidden-file-input" accept="image/*" onchange="handleSpecificFile(this, 'image')"> | |
| <input type="file" id="inp-video" class="hidden-file-input" accept="video/*" onchange="handleSpecificFile(this, 'video')"> | |
| <input type="file" id="inp-audio" class="hidden-file-input" accept="audio/*" onchange="handleSpecificFile(this, 'audio')"> | |
| <input type="file" id="inp-document" class="hidden-file-input" accept=".pdf,.doc,.docx,.txt,.md,.csv,.json,.py,.js,.html,.css,.cpp,.c,.java" onchange="handleSpecificFile(this, 'document')"> | |
| </div> | |
| <!-- NEW: SMART LANGUAGE TOGGLE FOR STT --> | |
| <button class="btn-lang-toggle" id="langToggleBtn" onclick="toggleSpeechLang()" title="Toggle Voice Language (EN/HI)"> | |
| 🌐 <span id="langIndicator">EN</span> | |
| </button> | |
| <!-- Voice Mic --> | |
| <button class="btn-icon btn-mic" id="voiceBtn" onclick="toggleVoice()" title="Dictate Message"> | |
| <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" 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> | |
| <!-- Text Area --> | |
| <textarea class="chat-textarea" id="chatInput" placeholder="Send a message to Arjun 2.O..." rows="1" onkeydown="handleKey(event)" oninput="autoGrow(this)"></textarea> | |
| <!-- Send Button --> | |
| <button class="btn-send" id="sendBtn" onclick="sendMsg()"> | |
| <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="footer-branding">Advanced Artificial Intelligence • Developed by Abhay Kumar</div> | |
| </div> | |
| </main> | |
| <!-- C. WORKSPACE PANEL --> | |
| <aside class="workspace-panel" id="workspace"> | |
| <div class="ws-header"> | |
| <div class="ws-title"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"><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="ws-actions-group"> | |
| <!-- ROBUST COPY BUTTON FOR WORKSPACE --> | |
| <button class="btn-ws-action" onclick="copyWorkspaceCode(this)" id="wsCopyBtn"> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> | |
| Copy Source | |
| </button> | |
| <button class="btn-ws-action btn-ws-close" onclick="closeWorkspace()" title="Close Workspace"> | |
| <svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| </div> | |
| </div> | |
| <div class="ws-editor-area"> | |
| <pre><code id="wsCodeBlock" class="hljs"></code></pre> | |
| </div> | |
| </aside> | |
| </div> | |
| <!-- 6. AUTHENTICATION MODAL --> | |
| <div class="modal-bg" id="authModal"> | |
| <div class="modal-box"> | |
| <button class="modal-close" onclick="closeAuthModal()"> | |
| <svg viewBox="0 0 24 24" width="18" height="18" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg> | |
| </button> | |
| <div class="modal-header-title">Arjun Network</div> | |
| <div class="tab-group"> | |
| <div class="tab-btn active" id="tab-login" onclick="switchAuthTab('login')">Access Account</div> | |
| <div class="tab-btn" id="tab-register" onclick="switchAuthTab('register')">Request Access</div> | |
| </div> | |
| <div id="flow-login"> | |
| <div class="auth-form-step active" id="log-step-1"> | |
| <input type="email" class="input-field" id="logEmail" placeholder="Authorized Email Address"> | |
| <button class="btn-submit-auth" id="btn-log-otp" onclick="processAuth('login_send_otp')">Generate Secure Token (OTP)</button> | |
| </div> | |
| <div class="auth-form-step" id="log-step-2"> | |
| <input type="number" class="input-field" id="logOTP" placeholder="Enter Validation Token"> | |
| <button class="btn-submit-auth" id="btn-log-verify" onclick="processAuth('login_verify')">Authenticate Session</button> | |
| <button class="btn-text-back" onclick="switchAuthStep('log-step-2', 'log-step-1')">← Return</button> | |
| </div> | |
| </div> | |
| <div id="flow-register" style="display:none;"> | |
| <div class="auth-form-step active" id="reg-step-1"> | |
| <input type="text" class="input-field" id="regName" placeholder="Full Legal Name"> | |
| <input type="email" class="input-field" id="regEmail" placeholder="Communication Email"> | |
| <button class="btn-submit-auth" id="btn-reg-otp" onclick="processAuth('register_send_otp')">Verify Identity</button> | |
| </div> | |
| <div class="auth-form-step" id="reg-step-2"> | |
| <input type="number" class="input-field" id="regOTP" placeholder="Enter Identity Token"> | |
| <button class="btn-submit-auth" id="btn-reg-verify" onclick="processAuth('register_verify')">Initialize Account</button> | |
| <button class="btn-text-back" onclick="switchAuthStep('reg-step-2', 'reg-step-1')">← Return</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- ========================================== | |
| ADVANCED JAVASCRIPT LOGIC (पूर्ण लॉजिक) | |
| ========================================== --> | |
| <script> | |
| // --- GLOBAL CONFIGURATION --- | |
| pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js'; | |
| const GAS_URL = "https://script.google.com/macros/s/AKfycbzTsXGoVh97JjtFAAG825GF4WUZqf7FDB3Zbxyf0nrLXt_1HjJSSqA829UcdoamRPvg0A/exec"; | |
| const LOGO_URL = "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png"; | |
| let currentUser = localStorage.getItem('arjun_user'); | |
| let guestCount = parseInt(localStorage.getItem('arjun_guest') || '0'); | |
| window.isStreamingGlobalFlag = false; | |
| let isLiveModeActive = false; | |
| const S = { history: [], pending: null, busy: false, isUserScrolling: false, codeBlocks: [], currentWorkspaceId: null }; | |
| // --- BOOTSTRAP --- | |
| document.addEventListener('DOMContentLoaded', () => { | |
| if(!localStorage.getItem('arjun_welcome_seen')) { document.getElementById('welcomeOverlay').style.display = 'flex'; } | |
| else { document.getElementById('welcomeOverlay').style.display = 'none'; } | |
| updateAuthUI(); | |
| if(currentUser) { fetchHistoryFromGAS(); } | |
| else { document.getElementById('guestBadge').style.display = 'inline-flex'; document.getElementById('guestCountDisplay').innerText = guestCount; } | |
| }); | |
| function startArjunApp() { | |
| document.getElementById('welcomeOverlay').classList.add('hidden'); | |
| localStorage.setItem('arjun_welcome_seen', 'true'); | |
| setTimeout(() => { document.getElementById('welcomeOverlay').style.display = 'none'; }, 400); | |
| } | |
| function toggleSidebar() { document.getElementById('sidebar').classList.toggle('show'); } | |
| function showToast(msg) { | |
| const c = document.getElementById('toast-container'); | |
| const t = document.createElement('div'); t.className = 'toast'; t.innerText = msg; | |
| c.appendChild(t); setTimeout(() => t.remove(), 3000); | |
| } | |
| // ========================================== | |
| // 📋 BULLETPROOF COPY LOGIC (NO COMPROMISE) | |
| // ========================================== | |
| function executeFallbackCopy(text, btnElement) { | |
| var 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 { | |
| var 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"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied Successfully`; | |
| setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500); | |
| } else { | |
| showToast("Clipboard access denied by browser."); | |
| } | |
| } catch (err) { | |
| showToast("Error executing copy command."); | |
| } | |
| document.body.removeChild(textArea); | |
| } | |
| function copyText(btnElement, rawTextToCopy) { | |
| if (!navigator.clipboard) { | |
| executeFallbackCopy(rawTextToCopy, btnElement); | |
| return; | |
| } | |
| navigator.clipboard.writeText(rawTextToCopy).then(function() { | |
| const originalHTML = btnElement.innerHTML; | |
| btnElement.innerHTML = `<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"></polyline></svg> Copied Successfully`; | |
| setTimeout(() => { btnElement.innerHTML = originalHTML; }, 2500); | |
| }).catch(function(err) { | |
| console.warn('Modern Clipboard API failed, attempting fallback...', err); | |
| executeFallbackCopy(rawTextToCopy, btnElement); | |
| }); | |
| } | |
| function copyWorkspaceCode(btnElement) { | |
| if(S.currentWorkspaceId !== null) { | |
| const actualCodeString = S.codeBlocks[S.currentWorkspaceId].code; | |
| copyText(btnElement, actualCodeString); | |
| } | |
| } | |
| // ========================================== | |
| // 🎙️ TEXT TO SPEECH (BACKEND - PRABHAT) | |
| // ========================================== | |
| async function speakText(text) { | |
| let cleanText = text.replace(/[*_~`#]/g, '').replace(/<[^>]*>?/gm, ''); | |
| showToast("🔊 Preparing High-Quality Audio..."); | |
| try { | |
| const res = await fetch('/api/tts', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ text: cleanText }) | |
| }); | |
| if (!res.ok) throw new Error("TTS failed on backend"); | |
| const blob = await res.blob(); | |
| const audioUrl = URL.createObjectURL(blob); | |
| const audio = new Audio(audioUrl); | |
| audio.play(); | |
| } catch (err) { | |
| showToast("Server TTS Error. Make sure app.py backend is updated."); | |
| console.error(err); | |
| } | |
| } | |
| // ========================================== | |
| // 🎤 SMART VOICE TYPING (HINDI/ENGLISH TOGGLE) | |
| // ========================================== | |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| let recognition = null; | |
| let isRecording = false; | |
| // Default STT language | |
| let currentSpeechLang = 'en-IN'; // Default to English/Hinglish | |
| function toggleSpeechLang() { | |
| const btn = document.getElementById('langToggleBtn'); | |
| const indicator = document.getElementById('langIndicator'); | |
| if(currentSpeechLang === 'en-IN') { | |
| currentSpeechLang = 'hi-IN'; | |
| indicator.innerText = 'हि'; | |
| btn.classList.add('active-hi'); | |
| showToast("Voice Mode: Pure Hindi (देवनागरी)"); | |
| } else { | |
| currentSpeechLang = 'en-IN'; | |
| indicator.innerText = 'EN'; | |
| btn.classList.remove('active-hi'); | |
| showToast("Voice Mode: English / Hinglish"); | |
| } | |
| // Update recognition engine language if it exists | |
| if(recognition) { | |
| recognition.lang = currentSpeechLang; | |
| // If it was recording, restart it to apply new language | |
| if(isRecording) { | |
| recognition.stop(); | |
| setTimeout(() => recognition.start(), 300); | |
| } | |
| } | |
| } | |
| if (SpeechRecognition) { | |
| recognition = new SpeechRecognition(); | |
| recognition.continuous = false; | |
| recognition.interimResults = false; | |
| recognition.lang = currentSpeechLang; | |
| recognition.onstart = () => { | |
| isRecording = true; | |
| document.getElementById('voiceBtn').classList.add('is-listening'); | |
| if(isLiveModeActive) { document.getElementById('liveMicBtn').classList.add('recording'); document.getElementById('liveStatusText').innerText = "Listening..."; document.getElementById('liveOrb').style.transform = "scale(1.2)"; } | |
| }; | |
| recognition.onresult = (event) => { | |
| let transcript = ''; | |
| for (let i = event.resultIndex; i < event.results.length; ++i) { transcript += event.results[i][0].transcript; } | |
| transcript = transcript.trim(); | |
| if(transcript.length > 0) { | |
| transcript = transcript.charAt(0).toUpperCase() + transcript.slice(1); | |
| if (!/[.!?।]$/.test(transcript)) transcript += '.'; transcript += ' '; | |
| } | |
| if(isLiveModeActive) { | |
| document.getElementById('liveTranscriptText').innerText = `"${transcript}"`; | |
| document.getElementById('liveStatusText').innerText = "Processing..."; | |
| document.getElementById('chatInput').value = transcript; | |
| sendMsg(); | |
| } else { | |
| const inp = document.getElementById('chatInput'); | |
| inp.value = (inp.value + ' ' + transcript).trim(); | |
| autoGrow(inp); | |
| } | |
| }; | |
| recognition.onend = () => { | |
| isRecording = false; | |
| document.getElementById('voiceBtn').classList.remove('is-listening'); | |
| if(isLiveModeActive && !S.busy) { document.getElementById('liveMicBtn').classList.remove('recording'); document.getElementById('liveStatusText').innerText = "Tap mic to speak"; document.getElementById('liveOrb').style.transform = "scale(1)"; } | |
| }; | |
| recognition.onerror = (e) => { | |
| showToast("Mic Error: " + e.error); | |
| isRecording = false; | |
| document.getElementById('voiceBtn').classList.remove('is-listening'); | |
| if(isLiveModeActive) { document.getElementById('liveMicBtn').classList.remove('recording'); document.getElementById('liveStatusText').innerText = "Error. Tap to retry."; } | |
| }; | |
| } | |
| function toggleVoice() { | |
| if(!recognition) return showToast("Voice input not supported in this browser."); | |
| if(isRecording) recognition.stop(); else recognition.start(); | |
| } | |
| // ========================================== | |
| // 🟢 LIVE MODE CONTROLLER | |
| // ========================================== | |
| function startLiveMode() { | |
| isLiveModeActive = true; | |
| document.getElementById('liveOverlay').classList.add('active'); | |
| if(recognition && !isRecording) { setTimeout(() => { recognition.start(); }, 500); } | |
| } | |
| function stopLiveMode() { | |
| isLiveModeActive = false; | |
| document.getElementById('liveOverlay').classList.remove('active'); | |
| if(recognition && isRecording) { recognition.stop(); } | |
| } | |
| function toggleLiveMic() { | |
| if(!recognition) return showToast("Not supported."); | |
| if(isRecording) recognition.stop(); else recognition.start(); | |
| } | |
| // ========================================== | |
| // 🔐 AUTHENTICATION & SYNC | |
| // ========================================== | |
| function updateAuthUI() { if (currentUser) { document.getElementById('loginPromptBtn').style.display = 'none'; document.getElementById('userProfile').style.display = 'flex'; document.getElementById('uEmail').innerText = currentUser; document.getElementById('uAvatar').innerText = currentUser.charAt(0).toUpperCase(); document.getElementById('guestBadge').style.display = 'none'; } else { document.getElementById('loginPromptBtn').style.display = 'flex'; document.getElementById('userProfile').style.display = 'none'; } } | |
| function openAuthModal() { document.getElementById('authModal').style.display = 'flex'; } | |
| function closeAuthModal() { document.getElementById('authModal').style.display = 'none'; } | |
| function switchAuthTab(tab) { document.querySelectorAll('.tab-btn').forEach(e => e.classList.remove('active')); document.getElementById(`tab-${tab}`).classList.add('active'); document.getElementById('flow-login').style.display = tab === 'login' ? 'block' : 'none'; document.getElementById('flow-register').style.display = tab === 'register' ? 'block' : 'none'; } | |
| function switchAuthStep(curr, next) { document.getElementById(curr).classList.remove('active'); document.getElementById(next).classList.add('active'); } | |
| async function processAuth(action) { | |
| let payload = { action }; let btnId = ''; | |
| if (action === 'register_send_otp') { payload.name = document.getElementById('regName').value.trim(); payload.email = document.getElementById('regEmail').value.trim(); payload.phone = "0000000000"; payload.organization = "Arjun Compound"; if(!payload.email || !payload.name) return showToast("Details required."); btnId = 'btn-reg-otp'; } | |
| else if (action === 'login_send_otp') { payload.email = document.getElementById('logEmail').value.trim(); if(!payload.email) return showToast("Enter email."); btnId = 'btn-log-otp'; } | |
| else if (action === 'register_verify' || action === 'login_verify') { payload.email = document.getElementById(action === 'register_verify' ? 'regEmail' : 'logEmail').value.trim(); payload.otp = document.getElementById(action === 'register_verify' ? 'regOTP' : 'logOTP').value.trim(); if(!payload.otp) return showToast("Enter OTP"); btnId = action === 'register_verify' ? 'btn-reg-verify' : 'btn-log-verify'; } | |
| const btn = document.getElementById(btnId); const ogText = btn.innerHTML; btn.disabled = true; btn.innerText = 'Connecting...'; | |
| try { | |
| const res = await fetch(GAS_URL, { method: 'POST', body: JSON.stringify(payload) }); | |
| const data = await res.json(); | |
| if (data.status === 'success') { | |
| showToast(data.message); | |
| if (action === 'register_send_otp') switchAuthStep('reg-step-1', 'reg-step-2'); | |
| else if (action === 'login_send_otp') switchAuthStep('log-step-1', 'log-step-2'); | |
| else if (action === 'register_verify' || action === 'login_verify') { currentUser = payload.email; localStorage.setItem('arjun_user', currentUser); closeAuthModal(); updateAuthUI(); fetchHistoryFromGAS(); } | |
| } else { showToast(data.message); } | |
| } catch (e) { showToast("Security gateway error."); } | |
| btn.disabled = false; btn.innerHTML = ogText; | |
| } | |
| function logout() { localStorage.removeItem('arjun_user'); location.reload(); } | |
| async function clearMemory() { if(currentUser) { showToast("Erasing memory context..."); await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "delete_history", email: currentUser }) }); } location.reload(); } | |
| async function fetchHistoryFromGAS() { | |
| const m = document.getElementById('msgs'); document.getElementById('welcome').style.display = 'none'; | |
| m.innerHTML = '<div style="text-align:center; padding:60px; font-weight:600; color:var(--brand-saffron);">Decrypting Secure History...</div>'; | |
| try { | |
| const res = await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "get_history", email: currentUser }) }); | |
| const data = await res.json(); m.innerHTML = ''; | |
| if (data.status === 'success' && data.historyJSON !== "[]") { | |
| S.history = JSON.parse(data.historyJSON); | |
| S.history.forEach(msg => { if(msg.role === 'user') appendUserDOM(msg.content, msg.badge); else { window.isStreamingGlobalFlag = false; appendBotDOM(msg.content, true); } }); | |
| setTimeout(forceScrollDown, 100); | |
| } else { document.getElementById('welcome').style.display = 'flex'; } | |
| } catch(e) { m.innerHTML = ''; document.getElementById('welcome').style.display = 'flex'; } | |
| } | |
| function syncHistory() { if(currentUser) { fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "save_history", email: currentUser, historyJSON: JSON.stringify(S.history) }) }); } } | |
| // ========================================== | |
| // 📂 ATTACHMENT & FILE EXTRACTION | |
| // ========================================== | |
| function toggleAttachMenu() { document.getElementById('attachMenu').classList.toggle('show'); } | |
| document.addEventListener('click', function(event) { const wrapper = document.querySelector('.attach-menu-container'); if (!wrapper.contains(event.target)) { document.getElementById('attachMenu').classList.remove('show'); } }); | |
| function triggerInput(type) { document.getElementById('attachMenu').classList.remove('show'); document.getElementById('inp-' + type).click(); } | |
| async function handleSpecificFile(input, expectedType) { | |
| const file = input.files[0]; if(!file) return; | |
| const fileName = file.name.toLowerCase(); | |
| const fileMime = file.type; | |
| if (expectedType === 'image' && !fileMime.startsWith('image/')) { showToast("Invalid Format! Requires Image."); input.value=''; return; } | |
| if (expectedType === 'video' && !fileMime.startsWith('video/')) { showToast("Invalid Format! Requires Video."); input.value=''; return; } | |
| if (expectedType === 'audio' && !fileMime.startsWith('audio/')) { showToast("Invalid Format! Requires Audio."); input.value=''; return; } | |
| document.getElementById('imgThumb').style.display = 'none'; document.getElementById('fileIcon').style.display = 'none'; | |
| document.getElementById('imgName').textContent = file.name; document.getElementById('imgPrev').classList.add('active'); | |
| showToast(`Analyzing ${file.name}...`); | |
| const reader = new FileReader(); | |
| if (expectedType === 'image') { | |
| reader.onload = e => { S.pending = { type: 'image', data: e.target.result.split(',')[1], name: file.name }; document.getElementById('imgThumb').src = e.target.result; document.getElementById('imgThumb').style.display = 'flex'; }; | |
| reader.readAsDataURL(file); | |
| } else if (expectedType === 'video' || expectedType === 'audio') { | |
| reader.onload = e => { S.pending = { type: expectedType, data: e.target.result.split(',')[1], name: file.name }; document.getElementById('fileIcon').textContent = expectedType === 'video' ? '🎥' : '🎵'; document.getElementById('fileIcon').style.display = 'flex'; }; | |
| reader.readAsDataURL(file); | |
| } else if (expectedType === 'document') { | |
| document.getElementById('fileIcon').textContent = '📄'; document.getElementById('fileIcon').style.display = 'flex'; | |
| if (fileName.endsWith('.pdf')) { | |
| reader.onload = async function() { | |
| try { | |
| const typedarray = new Uint8Array(this.result); | |
| const pdf = await pdfjsLib.getDocument(typedarray).promise; let fullText = ""; | |
| for (let i = 1; i <= pdf.numPages; i++) { const page = await pdf.getPage(i); const textContent = await page.getTextContent(); fullText += textContent.items.map(s => s.str).join(' ') + "\n"; } | |
| S.pending = { type: 'text', data: fullText.trim() || "Empty PDF.", name: file.name }; showToast("PDF Text Extracted!"); | |
| } catch (e) { showToast("Failed to parse PDF."); removeImg(); } | |
| }; reader.readAsArrayBuffer(file); | |
| } else if (fileName.endsWith('.docx') || fileName.endsWith('.doc')) { | |
| reader.onload = async function() { | |
| try { | |
| const arrayBuffer = this.result; const result = await mammoth.extractRawText({arrayBuffer: arrayBuffer}); | |
| S.pending = { type: 'text', data: result.value.trim() || "Empty DOCX.", name: file.name }; showToast("Word Text Extracted!"); | |
| } catch(e) { showToast("Failed to parse Word Document."); removeImg(); } | |
| }; reader.readAsArrayBuffer(file); | |
| } else { | |
| reader.onload = e => { S.pending = { type: 'text', data: e.target.result, name: file.name }; showToast("Code/Text loaded!"); }; | |
| reader.readAsText(file); | |
| } | |
| } | |
| input.value = ''; | |
| } | |
| function removeImg(){ S.pending=null; document.getElementById('imgPrev').classList.remove('active'); } | |
| // ========================================== | |
| // 🛠️ UI UTILITIES | |
| // ========================================== | |
| function handleKey(e){ if(e.key==='Enter'&&!e.shiftKey){ e.preventDefault(); sendMsg(); } } | |
| function autoGrow(el){ el.style.height='auto'; el.style.height=Math.min(el.scrollHeight,200)+'px'; } | |
| function checkScroll() { const m = document.getElementById('msgs'); const isAtBottom = Math.abs((m.scrollHeight - m.scrollTop) - m.clientHeight) < 80; S.isUserScrolling = !isAtBottom; const btn = document.getElementById('scrollBtn'); if(S.isUserScrolling) btn.classList.add('show'); else btn.classList.remove('show'); } | |
| function forceScrollDown(){ const m = document.getElementById('msgs'); m.scrollTo({top: m.scrollHeight, behavior: 'smooth'}); S.isUserScrolling = false; document.getElementById('scrollBtn').classList.remove('show'); } | |
| function autoScroll() { if(!S.isUserScrolling) { const m = document.getElementById('msgs'); m.scrollTop = m.scrollHeight; } } | |
| // ========================================== | |
| // 💻 WORKSPACE & MARKDOWN RENDERER | |
| // ========================================== | |
| function openWorkspace(id) { | |
| S.currentWorkspaceId = id; const block = S.codeBlocks[id]; | |
| document.getElementById('wsTitleText').textContent = block.lang ? `Generated ${block.lang.toUpperCase()}` : "Generated Code"; | |
| const codeBlock = document.getElementById('wsCodeBlock'); | |
| codeBlock.className = `hljs language-${block.lang || 'plaintext'}`; | |
| codeBlock.textContent = block.code; | |
| hljs.highlightElement(codeBlock); | |
| document.getElementById('workspace').classList.add('active'); | |
| if(window.innerWidth <= 900) { document.getElementById('sidebar').classList.remove('show'); } | |
| } | |
| function closeWorkspace() { document.getElementById('workspace').classList.remove('active'); } | |
| const renderer = new marked.Renderer(); | |
| renderer.code = function(token) { | |
| const codeText = typeof token === 'string' ? token : (token.text || ''); | |
| const language = typeof token === 'string' ? arguments[1] : (token.lang || ''); | |
| if (window.isStreamingGlobalFlag) { | |
| return ` | |
| <div class="gen-code-box"> | |
| <div class="gen-code-hdr"><div class="spinner-mini"></div> Synthesizing Code...</div> | |
| <pre><code class="language-${language}">${codeText.replace(/</g, '<').replace(/>/g, '>')}</code></pre> | |
| </div>`; | |
| } | |
| const id = S.codeBlocks.length; | |
| S.codeBlocks.push({ code: codeText, lang: language }); | |
| return ` | |
| <div class="workspace-card"> | |
| <div class="ws-card-info"> | |
| <div class="ws-card-icon"><svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg></div> | |
| <div class="ws-card-text"><h4>Code Module Ready</h4><p>${language || 'Syntax'}</p></div> | |
| </div> | |
| <button class="btn-view-ws" onclick="openWorkspace(${id})">Execute in Workspace</button> | |
| </div>`; | |
| }; | |
| marked.use({ renderer }); | |
| // ========================================== | |
| // 🧠 MAIN CHAT CORE ENGINE | |
| // ========================================== | |
| async function sendMsg(){ | |
| if(S.busy) return; | |
| const inp = document.getElementById('chatInput'); | |
| const msg = inp.value.trim(); | |
| if(!msg && !S.pending) return; | |
| if (!currentUser) { | |
| if (guestCount >= 3) { openAuthModal(); return showToast("Guest allowance exceeded. Authenticate to continue."); } | |
| guestCount++; localStorage.setItem('arjun_guest', guestCount.toString()); | |
| document.getElementById('guestCountDisplay').innerText = guestCount; | |
| } | |
| inp.value=''; inp.style.height='auto'; | |
| document.getElementById('sendBtn').disabled=true; | |
| S.busy=true; | |
| const w = document.getElementById('welcome'); if(w) w.style.display='none'; | |
| let backendPayloadMsg = msg; | |
| let attachments = []; | |
| let badge = null; | |
| if(S.pending) { | |
| if(S.pending.type === 'text') { | |
| backendPayloadMsg = `[Attached File Data: ${S.pending.name}]\n\n---FILE CONTENT START---\n${S.pending.data}\n---FILE CONTENT END---\n\nUser Question: ${msg}`; | |
| badge = '📄 '+S.pending.name; | |
| } else { | |
| attachments.push({ type: S.pending.type, data: S.pending.data, name: S.pending.name }); | |
| const icon = S.pending.type === 'video' ? '🎥 ' : (S.pending.type === 'audio' ? '🎵 ' : '🖼️ '); | |
| badge = icon + S.pending.name; | |
| } | |
| appendUserDOM(msg || '[Media Payload Attached]', badge); | |
| S.history.push({role: 'user', content: backendPayloadMsg, badge: badge}); | |
| } else { | |
| appendUserDOM(msg); | |
| S.history.push({role: 'user', content: msg}); | |
| } | |
| removeImg(); syncHistory(); | |
| const botDiv = document.createElement('div'); botDiv.className = 'message-row bot-msg'; | |
| botDiv.innerHTML = `<div class="avatar-box"><img src="${LOGO_URL}"></div><div class="msg-content-wrapper"><div class="bubble"><div class="typing"><span></span><span></span><span></span></div></div></div>`; | |
| document.getElementById('msgs').appendChild(botDiv); | |
| forceScrollDown(); | |
| // 🎲 POLLINATIONS RANDOM SEED GENERATOR | |
| const randomSeed = Math.floor(Math.random() * 999999999); | |
| // 🧠 DYNAMIC SYSTEM PROMPT | |
| let sysPrompt = `You are Arjun 2.O, a highly advanced AI system developed by Abhay Kumar. | |
| LANGUAGE: You are fully bilingual. Write and respond fluently in BOTH English and Hindi dynamically based on user input. Do not force Hindi if user speaks English. | |
| CRITICAL IMAGE GENERATION: | |
| If asked to generate an image, you MUST respond with a markdown image tag using Pollinations AI. | |
| Format:  | |
| Models: flux, flux-realism, flux-anime, flux-3d. Choose best fit. Always include the exact seed provided above in the URL to prevent caching.`; | |
| if(isLiveModeActive) { | |
| sysPrompt += `\n\nCRITICAL STATE: You are currently in LIVE VOICE CONVERSATIONAL MODE. | |
| Your responses MUST be extremely concise, short, and conversational. Do not use Markdown, do not use bolding, and do not provide long lists or code unless absolutely necessary. Speak as if you are on a live phone call. Keep it brief.`; | |
| } | |
| const cleanHistory = S.history.slice(0, -1).map(item => ({ role: item.role, content: item.content })); | |
| const payload = { | |
| message: backendPayloadMsg, | |
| attachments: attachments, | |
| system_prompt: sysPrompt, | |
| history: cleanHistory, | |
| max_tokens: isLiveModeActive ? 150 : 4096, | |
| temperature: 0.7 | |
| }; | |
| try { | |
| const res = await fetch('/api/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) }); | |
| if (!res.ok) throw new Error("Server error"); | |
| const reader = res.body.getReader(); const decoder = new TextDecoder(); | |
| let accumulatedText = ""; | |
| window.isStreamingGlobalFlag = true; | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| const chunk = decoder.decode(value, {stream: true}); | |
| const lines = chunk.split('\n'); | |
| for (let line of lines) { | |
| if (line.startsWith('data: ')) { | |
| const dataStr = line.substring(6); | |
| if (dataStr.trim() === '[DONE]') continue; | |
| try { | |
| const dataObj = JSON.parse(dataStr); | |
| if (dataObj.choices && dataObj.choices[0].delta.content) { | |
| accumulatedText += dataObj.choices[0].delta.content; | |
| renderBotText(botDiv, accumulatedText, true); autoScroll(); | |
| } | |
| } catch (e) {} | |
| } | |
| } | |
| } | |
| window.isStreamingGlobalFlag = false; | |
| renderBotText(botDiv, accumulatedText, false); | |
| S.history.push({role: 'bot', content: accumulatedText}); | |
| syncHistory(); | |
| if(isLiveModeActive) { | |
| document.getElementById('liveStatusText').innerText = "Arjun is speaking..."; | |
| await speakText(accumulatedText); | |
| document.getElementById('liveStatusText').innerText = "Tap mic to reply"; | |
| } | |
| } catch(err) { | |
| window.isStreamingGlobalFlag = false; | |
| renderBotText(botDiv, `**Error:** Server communication failed.`, false); | |
| if(isLiveModeActive) document.getElementById('liveStatusText').innerText = "Network Error"; | |
| } | |
| S.busy = false; document.getElementById('sendBtn').disabled = false; autoScroll(); | |
| } | |
| function sendEx(t){ document.getElementById('chatInput').value=t; sendMsg(); } | |
| function appendUserDOM(text, badgeText){ | |
| let bHtml = badgeText ? `<div class="file-badge">${esc(badgeText)}</div>` : ''; | |
| const el = document.createElement('div'); el.className = 'message-row user-msg'; | |
| el.innerHTML = `<div class="avatar-box">U</div><div class="msg-content-wrapper">${bHtml}<div class="bubble">${esc(text)}</div></div>`; | |
| document.getElementById('msgs').appendChild(el); | |
| } | |
| function appendBotDOM(content, isHistoryLoading=false) { | |
| const el = document.createElement('div'); el.className = 'message-row bot-msg'; | |
| el.innerHTML = `<div class="avatar-box"><img src="${LOGO_URL}"></div><div class="msg-content-wrapper"><div class="bubble"></div></div>`; | |
| document.getElementById('msgs').appendChild(el); | |
| renderBotText(el, content, !isHistoryLoading); | |
| } | |
| function renderBotText(el, raw, isStreaming) { | |
| const b = el.querySelector('.bubble'); | |
| const thinkRegex = /<think>([\s\S]*?)(<\/think>|$)/gi; | |
| let mainText = raw.replace(thinkRegex, ''); | |
| let html = marked.parse(mainText.trim()); | |
| if(isStreaming) { html += '<span class="blinking-cursor"></span>'; } | |
| b.innerHTML = html; | |
| let actionsDiv = el.querySelector('.msg-actions-bar'); | |
| if(!isStreaming && !actionsDiv && mainText.trim().length > 0) { | |
| const cleanRaw = mainText.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, '\\n').replace(/\r/g, ''); | |
| const rawForTTS = mainText.replace(/"/g, '"').replace(/'/g, "\\'").replace(/\n/g, ' '); | |
| const actionsHTML = ` | |
| <div class="msg-actions-bar"> | |
| <button class="action-chip" onclick="copyText(this, \`${cleanRaw}\`)"> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> | |
| Copy Source | |
| </button> | |
| <button class="action-chip btn-tts" onclick="speakText(\`${rawForTTS}\`)"> | |
| <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" 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> | |
| Listen (Neural) | |
| </button> | |
| </div>`; | |
| el.querySelector('.msg-content-wrapper').insertAdjacentHTML('beforeend', actionsHTML); | |
| } | |
| } | |
| function esc(t){ return t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); } | |
| </script> | |
| </body> | |
| </html> | |