Vedika commited on
Commit
dce595c
ยท
verified ยท
1 Parent(s): 50e4c37

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +186 -373
index.html CHANGED
@@ -7,6 +7,11 @@
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
 
 
 
 
 
10
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
11
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
12
  <style>
@@ -23,7 +28,6 @@
23
  --font-mono: 'Geist Mono', monospace;
24
  --radius-lg: 24px; --radius-md: 16px; --radius-sm: 8px;
25
  }
26
- /* Fixed UI layout hiding issue using 100dvh */
27
  html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--text-main);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;}
28
 
29
  /* --- BACKGROUND --- */
@@ -74,9 +78,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
74
  .login-prompt-btn:hover { background: #333; transform: translateY(-1px); box-shadow: var(--shadow-sm); }
75
 
76
  .settings{padding:20px;flex:1;overflow-y:auto;display:flex;flex-direction:column;gap: 20px;}
77
- .settings::-webkit-scrollbar{width:4px;}
78
- .settings::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.1);border-radius:10px;}
79
-
80
  .brand-box {background: #ffffff;border: 1px solid var(--border-light);border-radius: var(--radius-md);padding: 24px 16px;text-align: center;box-shadow: var(--shadow-sm);margin-top:auto;}
81
  .brand-box img {width: 70px; height: 70px; border-radius: 16px; object-fit: cover; margin-bottom: 12px; box-shadow: var(--shadow-sm); border: 1px solid var(--border-light);}
82
  .brand-box h3 {font-family: var(--font-sans); font-size: 15px; font-weight: 700; color: var(--text-main); margin-bottom: 6px;}
@@ -91,12 +92,11 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
91
  .model-badge{font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px;}
92
  .status-dot{width:8px;height:8px;border-radius:50%;background:#34c759;box-shadow:0 0 0 2px rgba(52,199,89,0.2);}
93
 
94
- /* Added min-height: 0 to ensure flexbox scrolling works properly */
95
  .messages{flex:1;overflow-y:auto;padding:30px 40px;display:flex;flex-direction:column;gap:24px;scroll-behavior:smooth;position:relative; min-height:0;}
96
  .messages::-webkit-scrollbar{width:6px;}
97
  .messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.1);border-radius:10px;}
98
 
99
- /* Welcome Screen & Prompts */
100
  .welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:40px 20px;margin:auto;animation:fadeInUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;width:100%;max-width:650px;}
101
  @keyframes fadeInUp{from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:none;}}
102
  .welcome-logo{width:80px;height:80px;border-radius:24px;object-fit:cover;box-shadow:var(--shadow-md);margin-bottom:24px;border:1px solid var(--border-light);}
@@ -126,13 +126,11 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
126
  .msg.user .bubble{background:var(--text-main);color:#ffffff;border-radius:20px 20px 4px 20px;box-shadow:var(--shadow-sm);}
127
  .msg.bot .bubble{background:#ffffff;border:1px solid var(--border-light);border-radius:4px 20px 20px 20px;box-shadow:var(--shadow-sm);width:100%;}
128
 
129
- /* BOT ACTIONS */
130
  .msg-actions{display:flex;gap:8px;margin-top:4px;padding-left:12px;opacity:0.7;transition:opacity 0.2s;}
131
  .msg.bot:hover .msg-actions{opacity:1;}
132
  .action-btn{background:var(--bg-secondary);border:1px solid var(--border-light);color:var(--text-main);font-size:12px;font-weight:500;cursor:pointer;display:flex;align-items:center;gap:6px;padding:6px 12px;border-radius:8px;transition:all 0.2s;}
133
  .action-btn:hover{background:#e5e5ea;transform:translateY(-1px);}
134
 
135
- /* Reasoning / Thinking Box - Hidden completely as per request */
136
  .think-box {display: none;}
137
 
138
  /* Markdown inside bubble */
@@ -144,7 +142,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
144
  .bubble pre {background: #1d1d1f; color:#fff; padding:16px; border-radius:12px; overflow-x:auto;}
145
  .bubble pre code {background: transparent; color:#fff;}
146
 
147
- /* Workspace Trigger Card in Chat */
148
  .ws-trigger-card {background: var(--bg-secondary);border: 1px solid var(--border-light);border-radius: 12px;padding: 16px;margin: 12px 0;display: flex;align-items: center;justify-content: space-between;box-shadow: inset 0 1px 2px rgba(255,255,255,0.5);}
149
  .ws-trigger-info {display: flex;align-items: center;gap: 12px;}
150
  .ws-trigger-icon {width: 44px;height: 44px;border-radius: 10px;background: #fff;display: flex;align-items: center;justify-content: center;font-size: 22px;box-shadow: var(--shadow-sm);border: 1px solid var(--border-light);}
@@ -161,12 +158,11 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
161
  .typing span:nth-child(1){animation-delay:-0.32s;}.typing span:nth-child(2){animation-delay:-0.16s;}
162
  @keyframes bounce{0%,80%,100%{transform:scale(0);}40%{transform:scale(1);}}
163
 
164
- /* --- SCROLL TO BOTTOM BUTTON --- */
165
  .scroll-bottom-btn {position:absolute;bottom:120px;left:50%;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;font-size:18px;color:var(--text-main);opacity:0;pointer-events:none;transform:translateX(-50%) translateY(20px);transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);z-index:20;}
166
  .scroll-bottom-btn.show {opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);}
167
  .scroll-bottom-btn:hover {background:#fff;transform:translateX(-50%) translateY(-3px);box-shadow:var(--shadow-lg);}
168
 
169
- /* --- INPUT AREA (Fixed Position to Avoid Overlap) --- */
170
  .input-container{flex-shrink:0; padding:10px 40px 24px; background:var(--bg-color); border-top:1px solid var(--border-light); position:relative; z-index:20;}
171
  .input-wrap{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:24px;box-shadow:var(--shadow-md);transition:box-shadow 0.3s, border-color 0.3s;}
172
  .input-wrap:focus-within{border-color:var(--border-focus);box-shadow:0 12px 40px rgba(0,0,0,0.1), 0 0 0 3px rgba(255,153,51,0.1);}
@@ -183,10 +179,20 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
183
  .chat-ta{flex:1;border:none;outline:none;background:transparent;font-family:var(--font-sans);font-size:15px;color:var(--text-main);line-height:1.5;resize:none;max-height:150px;min-height:24px;padding:8px 0;}
184
  .chat-ta::placeholder{color:var(--text-muted);}
185
 
 
 
186
  .attach-btn{width:38px;height:38px;border-radius:12px;background:transparent;border:1px solid transparent;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-muted);transition:all 0.2s;position:relative;overflow:hidden;padding-bottom:4px;}
187
  .attach-btn:hover{background:rgba(0,0,0,0.04);color:var(--text-main);}
188
- .attach-btn input{position:absolute;inset:0;opacity:0;cursor:pointer;}
189
  .attach-btn svg{width:20px;height:20px;}
 
 
 
 
 
 
 
 
 
190
  .voice-btn.recording { color: #ff3b30; animation: pulseRecord 1.5s infinite; }
191
  @keyframes pulseRecord { 0% { background: rgba(255,59,48,0.1); } 50% { background: rgba(255,59,48,0.3); } 100% { background: rgba(255,59,48,0.1); } }
192
 
@@ -227,7 +233,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
227
  .secondary-btn { width: 100%; padding: 12px; background: transparent; color: var(--text-muted); border: none; font-size: 14px; font-weight: 600; cursor: pointer; margin-top: 4px; }
228
  .secondary-btn:hover { color: var(--text-main); }
229
 
230
- /* --- RESPONSIVE MOBILE ADAPTIVE (Perfect fit fix) --- */
231
  @media(max-width:850px){
232
  .sidebar{position:absolute;left:-280px;z-index:100;height:100%;transition:left 0.3s ease;}
233
  .sidebar.show{left:0;box-shadow:var(--shadow-lg);}
@@ -243,10 +248,8 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
243
  </head>
244
  <body>
245
 
246
- <!-- TOAST NOTIFICATION -->
247
  <div id="toast-container"></div>
248
 
249
- <!-- GET STARTED WELCOME OVERLAY -->
250
  <div class="welcome-overlay" id="welcomeOverlay">
251
  <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo">
252
  <h1>Welcome to Arjun 2.O</h1>
@@ -254,13 +257,9 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
254
  <button class="start-btn" onclick="startArjunApp()">Get Started</button>
255
  </div>
256
 
257
- <div class="bg">
258
- <div class="orb orb1"></div>
259
- <div class="orb orb2"></div>
260
- </div>
261
 
262
  <div class="shell">
263
- <!-- Sidebar -->
264
  <aside class="sidebar" id="sidebar">
265
  <div class="logo-area">
266
  <div class="logo-row">
@@ -273,7 +272,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
273
  <button class="close-sidebar-btn" onclick="toggleSidebar()">โœ•</button>
274
  </div>
275
 
276
- <!-- AUTH BLOCK -->
277
  <div class="auth-block">
278
  <button id="loginPromptBtn" class="login-prompt-btn" onclick="openAuthModal()">๐Ÿ”’ Secure Login</button>
279
  <div id="userProfile" class="user-profile">
@@ -284,8 +282,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
284
  </div>
285
 
286
  <div class="settings">
287
- <!-- Reasoning Toggle Button has been completely removed as requested -->
288
-
289
  <div class="brand-box">
290
  <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Identity">
291
  <h3>Arjun 2.O</h3>
@@ -295,7 +291,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
295
  </div>
296
  </aside>
297
 
298
- <!-- Main Chat Area -->
299
  <div class="main-wrapper">
300
  <main class="chat-main" id="chatMain">
301
  <header class="chat-hdr">
@@ -304,18 +299,15 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
304
  <div class="status-dot"></div>
305
  Arjun Intelligence
306
  </div>
307
- <div style="width:24px;"></div> <!-- Spacer -->
308
  </header>
309
 
310
  <div class="messages" id="msgs" onscroll="checkScroll()">
311
- <!-- Inner Chat Welcome Screen -->
312
  <div class="welcome" id="welcome">
313
  <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo" class="welcome-logo">
314
  <div class="welcome-title">Welcome, User!</div>
315
  <div class="welcome-sub">I am Arjun 2.O, developed by Abhay Kumar. I am equipped with advanced intelligence, workspace coding, and image generation.</div>
316
-
317
  <div id="guestBadge" class="guest-badge">Guest Mode: <span id="guestCountDisplay">0</span>/3 Queries Used.</div>
318
-
319
  <div class="ex-grid">
320
  <div class="ex-card" onclick="sendEx('Write a python script for a simple HTTP server. Provide the code.')">
321
  <div class="ex-title">๐Ÿ’ป Code Generation</div>
@@ -333,7 +325,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
333
  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>
334
  </button>
335
 
336
- <!-- Input Area -->
337
  <div class="input-container">
338
  <div class="input-wrap">
339
  <div class="img-prev-inline" id="imgPrev">
@@ -346,13 +337,27 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
346
  </div>
347
 
348
  <div class="input-row">
349
- <!-- Universal File Attach -->
350
- <button class="attach-btn" title="Upload Image or Document">
351
- <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>
352
- <input type="file" accept="*/*" onchange="handleFile(this)" id="fileInput">
353
- </button>
354
 
355
- <!-- Voice Input Mic (Fixed Punctuation) -->
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
356
  <button class="attach-btn voice-btn" id="voiceBtn" onclick="toggleVoice()" title="Voice Input">
357
  <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>
358
  </button>
@@ -368,7 +373,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
368
  </div>
369
  </main>
370
 
371
- <!-- Workspace Panel -->
372
  <aside class="workspace" id="workspace">
373
  <div class="ws-hdr">
374
  <div class="ws-title"><span>๐Ÿ’ป</span><span id="wsTitleText">Generated Code</span></div>
@@ -384,18 +388,14 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
384
  </div>
385
  </div>
386
 
387
- <!-- AUTH MODAL -->
388
  <div class="modal-overlay" id="authModal">
389
  <div class="modal-card">
390
  <button class="close-modal" onclick="closeAuthModal()">โœ•</button>
391
  <div class="auth-header"><h2>Arjun Network</h2></div>
392
-
393
  <div class="auth-tabs">
394
  <div class="auth-tab active" id="tab-login" onclick="switchAuthTab('login')">Login</div>
395
  <div class="auth-tab" id="tab-register" onclick="switchAuthTab('register')">Register</div>
396
  </div>
397
-
398
- <!-- Login Form -->
399
  <div id="flow-login">
400
  <div class="auth-step active" id="log-step-1">
401
  <div class="input-group"><input type="email" id="logEmail" placeholder="Registered Email"></div>
@@ -407,8 +407,6 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
407
  <button class="secondary-btn" onclick="switchAuthStep('log-step-2', 'log-step-1')">Back</button>
408
  </div>
409
  </div>
410
-
411
- <!-- Register Form -->
412
  <div id="flow-register" style="display:none;">
413
  <div class="auth-step active" id="reg-step-1">
414
  <div class="input-group"><input type="text" id="regName" placeholder="Full Name"></div>
@@ -425,38 +423,24 @@ html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--t
425
  </div>
426
 
427
  <script>
428
- // --- GLOBAL VARIABLES & CONFIG ---
 
 
429
  const GAS_URL = "https://script.google.com/macros/s/AKfycbzTsXGoVh97JjtFAAG825GF4WUZqf7FDB3Zbxyf0nrLXt_1HjJSSqA829UcdoamRPvg0A/exec";
430
  const LOGO_URL = "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png";
431
 
432
  let currentUser = localStorage.getItem('arjun_user');
433
  let guestCount = parseInt(localStorage.getItem('arjun_guest') || '0');
434
- window.isStreamingGlobalFlag = false; // FLAG TO PREVENT PREMATURE WORKSPACE BUTTONS
435
-
436
- const S = {
437
- history: [],
438
- pending: null,
439
- busy: false,
440
- isUserScrolling: false,
441
- codeBlocks: [],
442
- currentWorkspaceId: null
443
- };
444
 
445
- // --- INITIALIZATION ---
446
- document.addEventListener('DOMContentLoaded', () => {
447
- if(!localStorage.getItem('arjun_welcome_seen')) {
448
- document.getElementById('welcomeOverlay').style.display = 'flex';
449
- } else {
450
- document.getElementById('welcomeOverlay').style.display = 'none';
451
- }
452
 
 
 
 
453
  updateAuthUI();
454
- if(currentUser) {
455
- fetchHistoryFromGAS();
456
- } else {
457
- document.getElementById('guestBadge').style.display = 'inline-block';
458
- document.getElementById('guestCountDisplay').innerText = guestCount;
459
- }
460
  });
461
 
462
  function startArjunApp() {
@@ -467,44 +451,33 @@ function startArjunApp() {
467
  }
468
 
469
  function toggleSidebar() { document.getElementById('sidebar').classList.toggle('show'); }
470
-
471
  function showToast(msg) {
472
  const container = document.getElementById('toast-container');
473
- const t = document.createElement('div'); t.className = 'toast';
474
- t.innerText = msg;
475
- container.appendChild(t);
476
- setTimeout(() => t.remove(), 3000);
477
  }
478
 
479
- // --- VOICE RECOGNITION (SMART PUNCTUATION LOGIC) ---
480
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
481
- let recognition = null;
482
- let isRecording = false;
483
 
484
  if (SpeechRecognition) {
485
  recognition = new SpeechRecognition();
486
- recognition.continuous = false; // Auto stops when paused
487
  recognition.interimResults = false;
488
 
489
- // UPDATED LANGUAGE LOGIC: English India supports Hinglish and English effectively
490
- // It will prevent strict Hindi character conversion when you speak English.
491
- recognition.lang = 'en-IN';
492
 
493
  recognition.onstart = () => { isRecording = true; document.getElementById('voiceBtn').classList.add('recording'); };
494
  recognition.onresult = (event) => {
495
  let transcript = '';
496
- for (let i = event.resultIndex; i < event.results.length; ++i) {
497
- transcript += event.results[i][0].transcript;
498
- }
499
-
500
- // Smart Punctuation & Capitalization Fix
501
  transcript = transcript.trim();
502
  if(transcript.length > 0) {
503
  transcript = transcript.charAt(0).toUpperCase() + transcript.slice(1);
504
- if (!/[.!?เฅค]$/.test(transcript)) transcript += '.';
505
- transcript += ' ';
506
  }
507
-
508
  const inp = document.getElementById('chatInput');
509
  inp.value = (inp.value + ' ' + transcript).trim();
510
  autoGrow(inp);
@@ -517,239 +490,138 @@ function toggleVoice() {
517
  if(isRecording) recognition.stop(); else recognition.start();
518
  }
519
 
520
-
521
- // --- AUTHENTICATION (GAS INTEGRATION) ---
522
- function updateAuthUI() {
523
- if (currentUser) {
524
- document.getElementById('loginPromptBtn').style.display = 'none';
525
- document.getElementById('userProfile').style.display = 'flex';
526
- document.getElementById('uEmail').innerText = currentUser;
527
- document.getElementById('uAvatar').innerText = currentUser.charAt(0).toUpperCase();
528
- document.getElementById('guestBadge').style.display = 'none';
529
- } else {
530
- document.getElementById('loginPromptBtn').style.display = 'flex';
531
- document.getElementById('userProfile').style.display = 'none';
532
- }
533
- }
534
-
535
  function openAuthModal() { document.getElementById('authModal').style.display = 'flex'; }
536
  function closeAuthModal() { document.getElementById('authModal').style.display = 'none'; }
537
-
538
- function switchAuthTab(tab) {
539
- document.querySelectorAll('.auth-tab').forEach(e => e.classList.remove('active'));
540
- document.getElementById(`tab-${tab}`).classList.add('active');
541
- document.getElementById('flow-login').style.display = tab === 'login' ? 'block' : 'none';
542
- document.getElementById('flow-register').style.display = tab === 'register' ? 'block' : 'none';
 
 
 
 
 
 
543
  }
544
- function switchAuthStep(curr, next) {
545
- document.getElementById(curr).classList.remove('active');
546
- document.getElementById(next).classList.add('active');
 
 
 
 
 
 
547
  }
548
 
549
- async function processAuth(action) {
550
- let payload = { action }; let btnId = '';
 
 
 
 
 
 
 
 
 
 
 
 
551
 
552
- if (action === 'register_send_otp') {
553
- payload.name = document.getElementById('regName').value.trim();
554
- payload.email = document.getElementById('regEmail').value.trim();
555
- payload.phone = "0000000000"; payload.organization = "Arjun Compound";
556
- if(!payload.email || !payload.name) return showToast("Name and Email required.");
557
- btnId = 'btn-reg-otp';
558
- } else if (action === 'login_send_otp') {
559
- payload.email = document.getElementById('logEmail').value.trim();
560
- if(!payload.email) return showToast("Enter email first.");
561
- btnId = 'btn-log-otp';
562
- } else if (action === 'register_verify' || action === 'login_verify') {
563
- payload.email = document.getElementById(action === 'register_verify' ? 'regEmail' : 'logEmail').value.trim();
564
- payload.otp = document.getElementById(action === 'register_verify' ? 'regOTP' : 'logOTP').value.trim();
565
- if(!payload.otp) return showToast("Enter OTP!");
566
- btnId = action === 'register_verify' ? 'btn-reg-verify' : 'btn-log-verify';
 
 
 
 
 
567
  }
568
-
569
- const btn = document.getElementById(btnId); const ogText = btn.innerHTML;
570
- btn.disabled = true; btn.innerText = 'Processing...';
571
-
572
- try {
573
- const res = await fetch(GAS_URL, { method: 'POST', body: JSON.stringify(payload) });
574
- const data = await res.json();
575
 
576
- if (data.status === 'success') {
577
- showToast(data.message);
578
- if (action === 'register_send_otp') switchAuthStep('reg-step-1', 'reg-step-2');
579
- else if (action === 'login_send_otp') switchAuthStep('log-step-1', 'log-step-2');
580
- else if (action === 'register_verify' || action === 'login_verify') {
581
- currentUser = payload.email;
582
- localStorage.setItem('arjun_user', currentUser);
583
- closeAuthModal(); updateAuthUI(); fetchHistoryFromGAS();
584
- }
585
- } else showToast(data.message);
586
- } catch (e) { showToast("Network Error."); }
587
- btn.disabled = false; btn.innerHTML = ogText;
588
- }
589
-
590
- function logout() { localStorage.removeItem('arjun_user'); location.reload(); }
591
-
592
- async function clearMemory() {
593
- if(currentUser) {
594
- showToast("Clearing Cloud History...");
595
- await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "delete_history", email: currentUser }) });
596
- }
597
- location.reload();
598
- }
599
-
600
- // --- GAS HISTORY SYNC ---
601
- async function fetchHistoryFromGAS() {
602
- const m = document.getElementById('msgs');
603
- document.getElementById('welcome').style.display = 'none';
604
- m.innerHTML = '<div style="text-align:center; padding:40px; color:var(--text-muted);">Syncing Secure Data...</div>';
605
-
606
- try {
607
- const res = await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "get_history", email: currentUser }) });
608
- const data = await res.json();
609
- m.innerHTML = '';
610
- if (data.status === 'success' && data.historyJSON !== "[]") {
611
- S.history = JSON.parse(data.historyJSON);
612
- S.history.forEach(msg => {
613
- if(msg.role === 'user') appendUserDOM(msg.content, msg.badge);
614
- else {
615
- window.isStreamingGlobalFlag = false;
616
- appendBotDOM(msg.content, true);
617
- }
618
- });
619
- setTimeout(forceScrollDown, 100);
620
- } else {
621
- document.getElementById('welcome').style.display = 'flex';
622
  }
623
- } catch(e) { m.innerHTML = ''; document.getElementById('welcome').style.display = 'flex'; }
624
- }
625
-
626
- function syncHistory() {
627
- if(currentUser) {
628
- fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "save_history", email: currentUser, historyJSON: JSON.stringify(S.history) }) });
629
  }
 
630
  }
631
 
632
- // --- MULTI-FILE HANDLING (Image + Text/Code/Data) ---
633
- function handleFile(input){
634
- const file = input.files[0]; if(!file) return;
635
- const reader = new FileReader();
636
-
637
- document.getElementById('imgThumb').style.display = 'none';
638
- document.getElementById('fileIcon').style.display = 'none';
639
- document.getElementById('imgName').textContent = file.name;
640
- document.getElementById('imgPrev').classList.add('show');
641
-
642
- // If Image
643
- if (file.type.startsWith('image/')) {
644
- reader.onload = e => {
645
- const base64Data = e.target.result.split(',')[1];
646
- S.pending = { type: 'image', data: base64Data, name: file.name };
647
- document.getElementById('imgThumb').src = e.target.result;
648
- document.getElementById('imgThumb').style.display = 'flex';
649
- };
650
- reader.readAsDataURL(file);
651
- }
652
- // If Text or Code File
653
- else if (file.type.match(/(text|json|csv|xml)/) || file.name.match(/\.(txt|md|csv|json|py|js|html|css|cpp|c)$/i)) {
654
- reader.onload = e => {
655
- S.pending = { type: 'text', data: e.target.result, name: file.name };
656
- document.getElementById('fileIcon').style.display = 'flex';
657
- };
658
- reader.readAsText(file);
659
- }
660
- // Other Fallback (PDF, etc)
661
- else {
662
- reader.onload = e => {
663
- const base64Data = e.target.result.split(',')[1];
664
- S.pending = { type: 'document', data: base64Data, name: file.name };
665
- document.getElementById('fileIcon').style.display = 'flex';
666
- };
667
- reader.readAsDataURL(file);
668
- }
669
- }
670
- function removeImg(){S.pending=null;document.getElementById('imgPrev').classList.remove('show');document.getElementById('fileInput').value='';}
671
 
672
  // --- UI INTERACTIONS ---
673
  function handleKey(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendMsg();}}
674
  function autoGrow(el){el.style.height='auto';el.style.height=Math.min(el.scrollHeight,200)+'px';}
675
-
676
- function checkScroll() {
677
- const m = document.getElementById('msgs');
678
- const isAtBottom = Math.abs((m.scrollHeight - m.scrollTop) - m.clientHeight) < 80;
679
- S.isUserScrolling = !isAtBottom;
680
- const btn = document.getElementById('scrollBtn');
681
- if(S.isUserScrolling) btn.classList.add('show');
682
- else btn.classList.remove('show');
683
- }
684
- function forceScrollDown(){
685
- const m=document.getElementById('msgs');
686
- m.scrollTo({top: m.scrollHeight, behavior: 'smooth'});
687
- S.isUserScrolling = false;
688
- document.getElementById('scrollBtn').classList.remove('show');
689
- }
690
- function autoScroll() {
691
- if(!S.isUserScrolling) {
692
- const m=document.getElementById('msgs');
693
- m.scrollTop = m.scrollHeight;
694
- }
695
- }
696
-
697
- // --- COPY UTILS ---
698
- function copyText(btn, text) {
699
- navigator.clipboard.writeText(text);
700
- const orig = btn.innerHTML;
701
- btn.innerHTML = "โœ“ Copied";
702
- setTimeout(() => btn.innerHTML = orig, 2000);
703
- }
704
- function copyWorkspaceCode() {
705
- if(S.currentWorkspaceId !== null) {
706
- navigator.clipboard.writeText(S.codeBlocks[S.currentWorkspaceId].code);
707
- const btn = document.getElementById('wsCopyBtn');
708
- const orig = btn.innerHTML;
709
- btn.innerHTML = "โœ“ Copied";
710
- setTimeout(() => btn.innerHTML = orig, 2000);
711
- }
712
- }
713
-
714
- // --- WORKSPACE LOGIC (MARKDOWN RENDERER FIXED FOR STREAMING) ---
715
- function openWorkspace(id) {
716
- S.currentWorkspaceId = id;
717
- const block = S.codeBlocks[id];
718
- document.getElementById('wsTitleText').textContent = block.lang ? `Generated ${block.lang.toUpperCase()}` : "Generated Code";
719
- const codeBlock = document.getElementById('wsCodeBlock');
720
- codeBlock.className = `hljs language-${block.lang || 'plaintext'}`;
721
- codeBlock.textContent = block.code;
722
- hljs.highlightElement(codeBlock);
723
- document.getElementById('workspace').classList.add('open');
724
- if(window.innerWidth <= 850) { document.getElementById('sidebar').classList.remove('show'); }
725
- }
726
  function closeWorkspace() { document.getElementById('workspace').classList.remove('open'); }
727
-
728
  const renderer = new marked.Renderer();
729
  renderer.code = function(token) {
730
  const codeText = typeof token === 'string' ? token : (token.text || '');
731
  const language = typeof token === 'string' ? arguments[1] : (token.lang || '');
732
-
733
- // FIXED: DO NOT SHOW WORKSPACE BUTTON WHILE STREAMING
734
- if (window.isStreamingGlobalFlag) {
735
- return `<pre><code class="language-${language}">${codeText.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre>`;
736
- }
737
-
738
- const id = S.codeBlocks.length;
739
- S.codeBlocks.push({ code: codeText, lang: language });
740
-
741
- return `
742
- <div class="ws-trigger-card">
743
- <div class="ws-trigger-info">
744
- <div class="ws-trigger-icon">๐Ÿ’ป</div>
745
- <div class="ws-trigger-text">
746
- <h4>Code Generated</h4>
747
- <p>${language || 'Snippet'}</p>
748
- </div>
749
- </div>
750
- <button class="ws-btn" onclick="openWorkspace(${id})">View in Workspace</button>
751
- </div>
752
- `;
753
  };
754
  marked.use({ renderer });
755
 
@@ -766,7 +638,6 @@ async function sendMsg(){
766
  const inp=document.getElementById('chatInput');
767
  const msg=inp.value.trim();if(!msg && !S.pending)return;
768
 
769
- // Guest Logic
770
  if (!currentUser) {
771
  if (guestCount >= 3) { openAuthModal(); return showToast("Guest limit reached. Please Login."); }
772
  guestCount++; localStorage.setItem('arjun_guest', guestCount.toString());
@@ -784,18 +655,17 @@ async function sendMsg(){
784
  let attachments = [];
785
  let badge = null;
786
 
787
- // Multi-File Extraction Logic
788
  if(S.pending) {
789
  if(S.pending.type === 'text') {
790
- backendPayloadMsg = `[User attached file: ${S.pending.name}]\nFile Content:\n${S.pending.data}\n\nUser Message: ${msg}`;
 
791
  badge = '๐Ÿ“„ '+S.pending.name;
792
- } else if(S.pending.type === 'image') {
793
- attachments.push(S.pending);
794
- badge = '๐Ÿ–ผ๏ธ '+S.pending.name;
795
  } else {
796
- badge = '๐Ÿ“Ž '+S.pending.name;
 
 
 
797
  }
798
-
799
  appendUserDOM(msg || '[File Attached]', badge);
800
  S.history.push({role: 'user', content: backendPayloadMsg, badge: badge});
801
  } else {
@@ -803,15 +673,13 @@ async function sendMsg(){
803
  S.history.push({role: 'user', content: msg});
804
  }
805
 
806
- removeImg();
807
- syncHistory();
808
 
809
  const botDiv = document.createElement('div');botDiv.className='msg bot';
810
  botDiv.innerHTML=`<div class="avatar"><img src="${LOGO_URL}"></div><div class="body"><div class="bubble"><div class="typing"><span></span><span></span><span></span></div></div></div>`;
811
  document.getElementById('msgs').appendChild(botDiv);
812
  forceScrollDown();
813
 
814
- // STRICT BILINGUAL PROMPT ADDED
815
  const sysPrompt = `You are Arjun 2.O, a state-of-the-art AI assistant developed by Abhay Kumar.
816
  LANGUAGE PROTOCOL: You are fully bilingual. You MUST write and respond fluently in BOTH English and Hindi.
817
  1. If the user asks in English, reply entirely in English.
@@ -826,11 +694,7 @@ async function sendMsg(){
826
  Choose the best model based on the prompt. Example: if user says anime, use flux-anime.
827
  Always include this markdown tag when image generation is requested.`;
828
 
829
- // Clean history for backend API compatibility
830
- const cleanHistory = S.history.slice(0, -1).map(item => ({
831
- role: item.role,
832
- content: item.content
833
- }));
834
 
835
  const payload = {
836
  message: backendPayloadMsg,
@@ -842,26 +706,16 @@ async function sendMsg(){
842
  };
843
 
844
  try{
845
- const res = await fetch('/api/chat', {
846
- method: 'POST',
847
- headers: {'Content-Type': 'application/json'},
848
- body: JSON.stringify(payload)
849
- });
850
-
851
  if (!res.ok) throw new Error("Server error");
852
-
853
- const reader = res.body.getReader();
854
- const decoder = new TextDecoder();
855
  let accumulatedText = "";
856
-
857
- window.isStreamingGlobalFlag = true; // PREVENTS WORKSPACE BUTTON FROM SPAWNING EARLY
858
-
859
  while (true) {
860
  const { done, value } = await reader.read();
861
  if (done) break;
862
  const chunk = decoder.decode(value, {stream: true});
863
  const lines = chunk.split('\n');
864
-
865
  for (let line of lines) {
866
  if (line.startsWith('data: ')) {
867
  const dataStr = line.substring(6);
@@ -870,65 +724,24 @@ async function sendMsg(){
870
  const dataObj = JSON.parse(dataStr);
871
  if (dataObj.choices && dataObj.choices[0].delta.content) {
872
  accumulatedText += dataObj.choices[0].delta.content;
873
- renderBotText(botDiv, accumulatedText, true);
874
- autoScroll();
875
  }
876
  } catch (e) { }
877
  }
878
  }
879
  }
880
-
881
- // STREAMING DONE - ALLOW WORKSPACE BUTTON CREATION
882
  window.isStreamingGlobalFlag = false;
883
  renderBotText(botDiv, accumulatedText, false);
884
-
885
  S.history.push({role: 'bot', content: accumulatedText});
886
  syncHistory();
887
-
888
- }catch(err){
889
- window.isStreamingGlobalFlag = false;
890
- renderBotText(botDiv,`**Error:** Server communication failed.`, false);
891
- }
892
-
893
  S.busy=false; document.getElementById('sendBtn').disabled=false; autoScroll();
894
  }
895
 
896
  function sendEx(t){document.getElementById('chatInput').value=t;sendMsg();}
897
-
898
- function appendUserDOM(text, badgeText){
899
- let bHtml = badgeText ? `<div style="font-size:11px;color:var(--accent);margin-bottom:4px;font-weight:600;">${esc(badgeText)}</div>` : '';
900
- const el=document.createElement('div');el.className='msg user';
901
- el.innerHTML=`<div class="avatar">U</div><div class="body">${bHtml}<div class="bubble">${esc(text)}</div></div>`;
902
- document.getElementById('msgs').appendChild(el);
903
- }
904
-
905
- function appendBotDOM(content, isHistoryLoading=false) {
906
- const el=document.createElement('div');el.className='msg bot';
907
- el.innerHTML=`<div class="avatar"><img src="${LOGO_URL}"></div><div class="body"><div class="bubble"></div></div>`;
908
- document.getElementById('msgs').appendChild(el);
909
- renderBotText(el, content, !isHistoryLoading);
910
- }
911
-
912
- function renderBotText(el, raw, isStreaming) {
913
- const b = el.querySelector('.bubble');
914
- const { mainText, thinkText } = parseThinking(raw);
915
- let html = '';
916
-
917
- // Note: Reasoning Box display is completely disabled by CSS (.think-box {display:none})
918
- if (thinkText) { html += `<div class="think-box"><div class="think-title">๐Ÿง  Reasoning Process</div>${esc(thinkText)}</div>`; }
919
-
920
- html += marked.parse(mainText);
921
- if(isStreaming) { html += '<span class="blinking-cursor"></span>'; }
922
- b.innerHTML = html;
923
-
924
- let actionsDiv = el.querySelector('.msg-actions');
925
- if(!isStreaming && !actionsDiv && mainText.trim().length > 0) {
926
- const cleanRaw = mainText.replace(/"/g, '&quot;').replace(/'/g, "\\'").replace(/\n/g, '\\n');
927
- const actionsHTML = `<div class="msg-actions"><button class="action-btn" onclick="copyText(this, \`${cleanRaw}\`)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> Copy Text</button></div>`;
928
- el.querySelector('.body').insertAdjacentHTML('beforeend', actionsHTML);
929
- }
930
- }
931
-
932
  function esc(t){return t.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
933
  </script>
934
  </body>
 
7
  <link rel="preconnect" href="https://fonts.googleapis.com">
8
  <link href="https://fonts.googleapis.com/css2?family=Instrument+Serif:ital@0;1&family=Inter:wght@300;400;500;600;700&family=Geist+Mono:wght@400;500&display=swap" rel="stylesheet">
9
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github.min.css">
10
+ <!-- PDF.js for PDF to Text Extraction -->
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.min.js"></script>
12
+ <!-- Mammoth.js for Word (.docx) to Text Extraction -->
13
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.6.0/mammoth.browser.min.js"></script>
14
+
15
  <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
16
  <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/12.0.1/marked.min.js"></script>
17
  <style>
 
28
  --font-mono: 'Geist Mono', monospace;
29
  --radius-lg: 24px; --radius-md: 16px; --radius-sm: 8px;
30
  }
 
31
  html,body{height:100dvh;overflow:hidden;background:var(--bg-color);color:var(--text-main);font-family:var(--font-sans);-webkit-font-smoothing:antialiased;}
32
 
33
  /* --- BACKGROUND --- */
 
78
  .login-prompt-btn:hover { background: #333; transform: translateY(-1px); box-shadow: var(--shadow-sm); }
79
 
80
  .settings{padding:20px;flex:1;overflow-y:auto;display:flex;flex-direction:column;gap: 20px;}
 
 
 
81
  .brand-box {background: #ffffff;border: 1px solid var(--border-light);border-radius: var(--radius-md);padding: 24px 16px;text-align: center;box-shadow: var(--shadow-sm);margin-top:auto;}
82
  .brand-box img {width: 70px; height: 70px; border-radius: 16px; object-fit: cover; margin-bottom: 12px; box-shadow: var(--shadow-sm); border: 1px solid var(--border-light);}
83
  .brand-box h3 {font-family: var(--font-sans); font-size: 15px; font-weight: 700; color: var(--text-main); margin-bottom: 6px;}
 
92
  .model-badge{font-size:13px;font-weight:600;display:flex;align-items:center;gap:8px;}
93
  .status-dot{width:8px;height:8px;border-radius:50%;background:#34c759;box-shadow:0 0 0 2px rgba(52,199,89,0.2);}
94
 
 
95
  .messages{flex:1;overflow-y:auto;padding:30px 40px;display:flex;flex-direction:column;gap:24px;scroll-behavior:smooth;position:relative; min-height:0;}
96
  .messages::-webkit-scrollbar{width:6px;}
97
  .messages::-webkit-scrollbar-thumb{background:rgba(0,0,0,0.1);border-radius:10px;}
98
 
99
+ /* Welcome Screen */
100
  .welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;padding:40px 20px;margin:auto;animation:fadeInUp 0.6s cubic-bezier(0.16, 1, 0.3, 1) both;width:100%;max-width:650px;}
101
  @keyframes fadeInUp{from{opacity:0;transform:translateY(20px);}to{opacity:1;transform:none;}}
102
  .welcome-logo{width:80px;height:80px;border-radius:24px;object-fit:cover;box-shadow:var(--shadow-md);margin-bottom:24px;border:1px solid var(--border-light);}
 
126
  .msg.user .bubble{background:var(--text-main);color:#ffffff;border-radius:20px 20px 4px 20px;box-shadow:var(--shadow-sm);}
127
  .msg.bot .bubble{background:#ffffff;border:1px solid var(--border-light);border-radius:4px 20px 20px 20px;box-shadow:var(--shadow-sm);width:100%;}
128
 
 
129
  .msg-actions{display:flex;gap:8px;margin-top:4px;padding-left:12px;opacity:0.7;transition:opacity 0.2s;}
130
  .msg.bot:hover .msg-actions{opacity:1;}
131
  .action-btn{background:var(--bg-secondary);border:1px solid var(--border-light);color:var(--text-main);font-size:12px;font-weight:500;cursor:pointer;display:flex;align-items:center;gap:6px;padding:6px 12px;border-radius:8px;transition:all 0.2s;}
132
  .action-btn:hover{background:#e5e5ea;transform:translateY(-1px);}
133
 
 
134
  .think-box {display: none;}
135
 
136
  /* Markdown inside bubble */
 
142
  .bubble pre {background: #1d1d1f; color:#fff; padding:16px; border-radius:12px; overflow-x:auto;}
143
  .bubble pre code {background: transparent; color:#fff;}
144
 
 
145
  .ws-trigger-card {background: var(--bg-secondary);border: 1px solid var(--border-light);border-radius: 12px;padding: 16px;margin: 12px 0;display: flex;align-items: center;justify-content: space-between;box-shadow: inset 0 1px 2px rgba(255,255,255,0.5);}
146
  .ws-trigger-info {display: flex;align-items: center;gap: 12px;}
147
  .ws-trigger-icon {width: 44px;height: 44px;border-radius: 10px;background: #fff;display: flex;align-items: center;justify-content: center;font-size: 22px;box-shadow: var(--shadow-sm);border: 1px solid var(--border-light);}
 
158
  .typing span:nth-child(1){animation-delay:-0.32s;}.typing span:nth-child(2){animation-delay:-0.16s;}
159
  @keyframes bounce{0%,80%,100%{transform:scale(0);}40%{transform:scale(1);}}
160
 
 
161
  .scroll-bottom-btn {position:absolute;bottom:120px;left:50%;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;font-size:18px;color:var(--text-main);opacity:0;pointer-events:none;transform:translateX(-50%) translateY(20px);transition:all 0.3s cubic-bezier(0.4, 0, 0.2, 1);z-index:20;}
162
  .scroll-bottom-btn.show {opacity:1;pointer-events:auto;transform:translateX(-50%) translateY(0);}
163
  .scroll-bottom-btn:hover {background:#fff;transform:translateX(-50%) translateY(-3px);box-shadow:var(--shadow-lg);}
164
 
165
+ /* --- INPUT AREA & ATTACHMENT MENU --- */
166
  .input-container{flex-shrink:0; padding:10px 40px 24px; background:var(--bg-color); border-top:1px solid var(--border-light); position:relative; z-index:20;}
167
  .input-wrap{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:24px;box-shadow:var(--shadow-md);transition:box-shadow 0.3s, border-color 0.3s;}
168
  .input-wrap:focus-within{border-color:var(--border-focus);box-shadow:0 12px 40px rgba(0,0,0,0.1), 0 0 0 3px rgba(255,153,51,0.1);}
 
179
  .chat-ta{flex:1;border:none;outline:none;background:transparent;font-family:var(--font-sans);font-size:15px;color:var(--text-main);line-height:1.5;resize:none;max-height:150px;min-height:24px;padding:8px 0;}
180
  .chat-ta::placeholder{color:var(--text-muted);}
181
 
182
+ /* GEMINI STYLE ATTACHMENT MENU */
183
+ .attach-wrapper { position: relative; }
184
  .attach-btn{width:38px;height:38px;border-radius:12px;background:transparent;border:1px solid transparent;display:flex;align-items:center;justify-content:center;cursor:pointer;color:var(--text-muted);transition:all 0.2s;position:relative;overflow:hidden;padding-bottom:4px;}
185
  .attach-btn:hover{background:rgba(0,0,0,0.04);color:var(--text-main);}
 
186
  .attach-btn svg{width:20px;height:20px;}
187
+ .attach-menu { position: absolute; bottom: 50px; left: 0; background: rgba(255,255,255,0.9); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); border: 1px solid var(--border-light); border-radius: 16px; padding: 8px; box-shadow: var(--shadow-lg); display: none; flex-direction: column; gap: 4px; z-index: 100; min-width: 160px; transform: translateY(10px); opacity: 0; transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
188
+ .attach-menu.show { display: flex; transform: translateY(0); opacity: 1; }
189
+ .attach-menu-item { display: flex; align-items: center; gap: 10px; padding: 10px 12px; border-radius: 10px; cursor: pointer; font-size: 14px; font-weight: 500; color: var(--text-main); transition: background 0.2s; }
190
+ .attach-menu-item:hover { background: rgba(0,0,0,0.05); }
191
+ .attach-menu-item span.icon { font-size: 16px; }
192
+
193
+ /* Hidden Inputs */
194
+ .hidden-file-input { display: none; }
195
+
196
  .voice-btn.recording { color: #ff3b30; animation: pulseRecord 1.5s infinite; }
197
  @keyframes pulseRecord { 0% { background: rgba(255,59,48,0.1); } 50% { background: rgba(255,59,48,0.3); } 100% { background: rgba(255,59,48,0.1); } }
198
 
 
233
  .secondary-btn { width: 100%; padding: 12px; background: transparent; color: var(--text-muted); border: none; font-size: 14px; font-weight: 600; cursor: pointer; margin-top: 4px; }
234
  .secondary-btn:hover { color: var(--text-main); }
235
 
 
236
  @media(max-width:850px){
237
  .sidebar{position:absolute;left:-280px;z-index:100;height:100%;transition:left 0.3s ease;}
238
  .sidebar.show{left:0;box-shadow:var(--shadow-lg);}
 
248
  </head>
249
  <body>
250
 
 
251
  <div id="toast-container"></div>
252
 
 
253
  <div class="welcome-overlay" id="welcomeOverlay">
254
  <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo">
255
  <h1>Welcome to Arjun 2.O</h1>
 
257
  <button class="start-btn" onclick="startArjunApp()">Get Started</button>
258
  </div>
259
 
260
+ <div class="bg"><div class="orb orb1"></div><div class="orb orb2"></div></div>
 
 
 
261
 
262
  <div class="shell">
 
263
  <aside class="sidebar" id="sidebar">
264
  <div class="logo-area">
265
  <div class="logo-row">
 
272
  <button class="close-sidebar-btn" onclick="toggleSidebar()">โœ•</button>
273
  </div>
274
 
 
275
  <div class="auth-block">
276
  <button id="loginPromptBtn" class="login-prompt-btn" onclick="openAuthModal()">๐Ÿ”’ Secure Login</button>
277
  <div id="userProfile" class="user-profile">
 
282
  </div>
283
 
284
  <div class="settings">
 
 
285
  <div class="brand-box">
286
  <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Identity">
287
  <h3>Arjun 2.O</h3>
 
291
  </div>
292
  </aside>
293
 
 
294
  <div class="main-wrapper">
295
  <main class="chat-main" id="chatMain">
296
  <header class="chat-hdr">
 
299
  <div class="status-dot"></div>
300
  Arjun Intelligence
301
  </div>
302
+ <div style="width:24px;"></div>
303
  </header>
304
 
305
  <div class="messages" id="msgs" onscroll="checkScroll()">
 
306
  <div class="welcome" id="welcome">
307
  <img src="https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png" alt="Arjun Logo" class="welcome-logo">
308
  <div class="welcome-title">Welcome, User!</div>
309
  <div class="welcome-sub">I am Arjun 2.O, developed by Abhay Kumar. I am equipped with advanced intelligence, workspace coding, and image generation.</div>
 
310
  <div id="guestBadge" class="guest-badge">Guest Mode: <span id="guestCountDisplay">0</span>/3 Queries Used.</div>
 
311
  <div class="ex-grid">
312
  <div class="ex-card" onclick="sendEx('Write a python script for a simple HTTP server. Provide the code.')">
313
  <div class="ex-title">๐Ÿ’ป Code Generation</div>
 
325
  <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M19 12l-7 7-7-7"/></svg>
326
  </button>
327
 
 
328
  <div class="input-container">
329
  <div class="input-wrap">
330
  <div class="img-prev-inline" id="imgPrev">
 
337
  </div>
338
 
339
  <div class="input-row">
 
 
 
 
 
340
 
341
+ <!-- GEMINI STYLE ATTACHMENT MENU -->
342
+ <div class="attach-wrapper">
343
+ <button class="attach-btn" id="attachToggleBtn" onclick="toggleAttachMenu()" title="Attach File">
344
+ <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>
345
+ </button>
346
+
347
+ <div class="attach-menu" id="attachMenu">
348
+ <div class="attach-menu-item" onclick="triggerInput('image')"><span class="icon">๐Ÿ–ผ๏ธ</span> Image</div>
349
+ <div class="attach-menu-item" onclick="triggerInput('video')"><span class="icon">๐ŸŽฅ</span> Video</div>
350
+ <div class="attach-menu-item" onclick="triggerInput('audio')"><span class="icon">๐ŸŽต</span> Audio</div>
351
+ <div class="attach-menu-item" onclick="triggerInput('document')"><span class="icon">๐Ÿ“„</span> Document / Code</div>
352
+ </div>
353
+
354
+ <!-- Hidden Inputs for strict validation -->
355
+ <input type="file" id="inp-image" class="hidden-file-input" accept="image/*" onchange="handleSpecificFile(this, 'image')">
356
+ <input type="file" id="inp-video" class="hidden-file-input" accept="video/*" onchange="handleSpecificFile(this, 'video')">
357
+ <input type="file" id="inp-audio" class="hidden-file-input" accept="audio/*" onchange="handleSpecificFile(this, 'audio')">
358
+ <input type="file" id="inp-document" class="hidden-file-input" accept=".pdf,.doc,.docx,.txt,.md,.csv,.json,.py,.js,.html,.css,.cpp,.c" onchange="handleSpecificFile(this, 'document')">
359
+ </div>
360
+
361
  <button class="attach-btn voice-btn" id="voiceBtn" onclick="toggleVoice()" title="Voice Input">
362
  <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>
363
  </button>
 
373
  </div>
374
  </main>
375
 
 
376
  <aside class="workspace" id="workspace">
377
  <div class="ws-hdr">
378
  <div class="ws-title"><span>๐Ÿ’ป</span><span id="wsTitleText">Generated Code</span></div>
 
388
  </div>
389
  </div>
390
 
 
391
  <div class="modal-overlay" id="authModal">
392
  <div class="modal-card">
393
  <button class="close-modal" onclick="closeAuthModal()">โœ•</button>
394
  <div class="auth-header"><h2>Arjun Network</h2></div>
 
395
  <div class="auth-tabs">
396
  <div class="auth-tab active" id="tab-login" onclick="switchAuthTab('login')">Login</div>
397
  <div class="auth-tab" id="tab-register" onclick="switchAuthTab('register')">Register</div>
398
  </div>
 
 
399
  <div id="flow-login">
400
  <div class="auth-step active" id="log-step-1">
401
  <div class="input-group"><input type="email" id="logEmail" placeholder="Registered Email"></div>
 
407
  <button class="secondary-btn" onclick="switchAuthStep('log-step-2', 'log-step-1')">Back</button>
408
  </div>
409
  </div>
 
 
410
  <div id="flow-register" style="display:none;">
411
  <div class="auth-step active" id="reg-step-1">
412
  <div class="input-group"><input type="text" id="regName" placeholder="Full Name"></div>
 
423
  </div>
424
 
425
  <script>
426
+ // --- PDF.JS INITIALIZATION ---
427
+ pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.11.174/pdf.worker.min.js';
428
+
429
  const GAS_URL = "https://script.google.com/macros/s/AKfycbzTsXGoVh97JjtFAAG825GF4WUZqf7FDB3Zbxyf0nrLXt_1HjJSSqA829UcdoamRPvg0A/exec";
430
  const LOGO_URL = "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png";
431
 
432
  let currentUser = localStorage.getItem('arjun_user');
433
  let guestCount = parseInt(localStorage.getItem('arjun_guest') || '0');
434
+ window.isStreamingGlobalFlag = false;
 
 
 
 
 
 
 
 
 
435
 
436
+ const S = { history: [], pending: null, busy: false, isUserScrolling: false, codeBlocks: [], currentWorkspaceId: null };
 
 
 
 
 
 
437
 
438
+ document.addEventListener('DOMContentLoaded', () => {
439
+ if(!localStorage.getItem('arjun_welcome_seen')) { document.getElementById('welcomeOverlay').style.display = 'flex'; }
440
+ else { document.getElementById('welcomeOverlay').style.display = 'none'; }
441
  updateAuthUI();
442
+ if(currentUser) { fetchHistoryFromGAS(); }
443
+ else { document.getElementById('guestBadge').style.display = 'inline-block'; document.getElementById('guestCountDisplay').innerText = guestCount; }
 
 
 
 
444
  });
445
 
446
  function startArjunApp() {
 
451
  }
452
 
453
  function toggleSidebar() { document.getElementById('sidebar').classList.toggle('show'); }
 
454
  function showToast(msg) {
455
  const container = document.getElementById('toast-container');
456
+ const t = document.createElement('div'); t.className = 'toast'; t.innerText = msg;
457
+ container.appendChild(t); setTimeout(() => t.remove(), 3000);
 
 
458
  }
459
 
460
+ // --- VOICE RECOGNITION (SMART HINDI/ENGLISH) ---
461
  const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
462
+ let recognition = null; let isRecording = false;
 
463
 
464
  if (SpeechRecognition) {
465
  recognition = new SpeechRecognition();
466
+ recognition.continuous = false;
467
  recognition.interimResults = false;
468
 
469
+ // Set to 'hi-IN'. Google will type pure Hindi in Devanagari and English words in English.
470
+ recognition.lang = 'hi-IN';
 
471
 
472
  recognition.onstart = () => { isRecording = true; document.getElementById('voiceBtn').classList.add('recording'); };
473
  recognition.onresult = (event) => {
474
  let transcript = '';
475
+ for (let i = event.resultIndex; i < event.results.length; ++i) transcript += event.results[i][0].transcript;
 
 
 
 
476
  transcript = transcript.trim();
477
  if(transcript.length > 0) {
478
  transcript = transcript.charAt(0).toUpperCase() + transcript.slice(1);
479
+ if (!/[.!?เฅค]$/.test(transcript)) transcript += '.'; transcript += ' ';
 
480
  }
 
481
  const inp = document.getElementById('chatInput');
482
  inp.value = (inp.value + ' ' + transcript).trim();
483
  autoGrow(inp);
 
490
  if(isRecording) recognition.stop(); else recognition.start();
491
  }
492
 
493
+ // --- AUTH & HISTORY FUNCTIONS (UNCHANGED) ---
494
+ 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'; } }
 
 
 
 
 
 
 
 
 
 
 
 
 
495
  function openAuthModal() { document.getElementById('authModal').style.display = 'flex'; }
496
  function closeAuthModal() { document.getElementById('authModal').style.display = 'none'; }
497
+ function switchAuthTab(tab) { document.querySelectorAll('.auth-tab').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'; }
498
+ function switchAuthStep(curr, next) { document.getElementById(curr).classList.remove('active'); document.getElementById(next).classList.add('active'); }
499
+ 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("Name and Email 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 first."); 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 = 'Processing...'; 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("Network Error."); } btn.disabled = false; btn.innerHTML = ogText; }
500
+ function logout() { localStorage.removeItem('arjun_user'); location.reload(); }
501
+ async function clearMemory() { if(currentUser) { showToast("Clearing Cloud History..."); await fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "delete_history", email: currentUser }) }); } location.reload(); }
502
+ async function fetchHistoryFromGAS() { const m = document.getElementById('msgs'); document.getElementById('welcome').style.display = 'none'; m.innerHTML = '<div style="text-align:center; padding:40px; color:var(--text-muted);">Syncing Secure Data...</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'; } }
503
+ function syncHistory() { if(currentUser) { fetch(GAS_URL, { method: 'POST', body: JSON.stringify({ action: "save_history", email: currentUser, historyJSON: JSON.stringify(S.history) }) }); } }
504
+
505
+ // --- ADVANCED MULTI-FILE & CONVERTER LOGIC ---
506
+ function toggleAttachMenu() {
507
+ const menu = document.getElementById('attachMenu');
508
+ menu.classList.toggle('show');
509
  }
510
+ // Hide menu when clicked outside
511
+ document.addEventListener('click', function(event) {
512
+ const wrapper = document.querySelector('.attach-wrapper');
513
+ if (!wrapper.contains(event.target)) { document.getElementById('attachMenu').classList.remove('show'); }
514
+ });
515
+
516
+ function triggerInput(type) {
517
+ document.getElementById('attachMenu').classList.remove('show');
518
+ document.getElementById('inp-' + type).click();
519
  }
520
 
521
+ async function handleSpecificFile(input, expectedType) {
522
+ const file = input.files[0]; if(!file) return;
523
+ const fileName = file.name.toLowerCase();
524
+ const fileMime = file.type;
525
+
526
+ // 1. STRICT VALIDATION & INVALID FORMAT TOAST
527
+ if (expectedType === 'image' && !fileMime.startsWith('image/')) { showToast("Invalid Format! Please select an Image file."); input.value=''; return; }
528
+ if (expectedType === 'video' && !fileMime.startsWith('video/')) { showToast("Invalid Format! Please select a Video file."); input.value=''; return; }
529
+ if (expectedType === 'audio' && !fileMime.startsWith('audio/')) { showToast("Invalid Format! Please select an Audio file."); input.value=''; return; }
530
+
531
+ document.getElementById('imgThumb').style.display = 'none';
532
+ document.getElementById('fileIcon').style.display = 'none';
533
+ document.getElementById('imgName').textContent = file.name;
534
+ document.getElementById('imgPrev').classList.add('show');
535
 
536
+ showToast(`Processing ${file.name}...`);
537
+
538
+ const reader = new FileReader();
539
+
540
+ // 2. IMAGE, VIDEO, AUDIO (Send as Base64 Media to Model)
541
+ if (expectedType === 'image') {
542
+ reader.onload = e => {
543
+ S.pending = { type: 'image', data: e.target.result.split(',')[1], name: file.name };
544
+ document.getElementById('imgThumb').src = e.target.result;
545
+ document.getElementById('imgThumb').style.display = 'flex';
546
+ };
547
+ reader.readAsDataURL(file);
548
+ }
549
+ else if (expectedType === 'video' || expectedType === 'audio') {
550
+ reader.onload = e => {
551
+ S.pending = { type: expectedType, data: e.target.result.split(',')[1], name: file.name };
552
+ document.getElementById('fileIcon').textContent = expectedType === 'video' ? '๐ŸŽฅ' : '๐ŸŽต';
553
+ document.getElementById('fileIcon').style.display = 'flex';
554
+ };
555
+ reader.readAsDataURL(file);
556
  }
557
+ // 3. DOCUMENT / CODE (Convert everything to TEXT before sending to Model)
558
+ else if (expectedType === 'document') {
559
+ document.getElementById('fileIcon').textContent = '๐Ÿ“„';
560
+ document.getElementById('fileIcon').style.display = 'flex';
 
 
 
561
 
562
+ // A. Handle PDF -> Text via pdf.js
563
+ if (fileName.endsWith('.pdf')) {
564
+ reader.onload = async function() {
565
+ try {
566
+ const typedarray = new Uint8Array(this.result);
567
+ const pdf = await pdfjsLib.getDocument(typedarray).promise;
568
+ let fullText = "";
569
+ for (let i = 1; i <= pdf.numPages; i++) {
570
+ const page = await pdf.getPage(i);
571
+ const textContent = await page.getTextContent();
572
+ fullText += textContent.items.map(s => s.str).join(' ') + "\n";
573
+ }
574
+ S.pending = { type: 'text', data: fullText.trim() || "No text found in PDF.", name: file.name };
575
+ showToast("PDF converted to text successfully!");
576
+ } catch (e) { showToast("Error parsing PDF."); removeImg(); }
577
+ };
578
+ reader.readAsArrayBuffer(file);
579
+ }
580
+ // B. Handle Word (.docx) -> Text via Mammoth.js
581
+ else if (fileName.endsWith('.docx')) {
582
+ reader.onload = async function() {
583
+ try {
584
+ const arrayBuffer = this.result;
585
+ const result = await mammoth.extractRawText({arrayBuffer: arrayBuffer});
586
+ S.pending = { type: 'text', data: result.value.trim() || "No text found in DOCX.", name: file.name };
587
+ showToast("Word document converted to text!");
588
+ } catch(e) { showToast("Error parsing Word doc."); removeImg(); }
589
+ };
590
+ reader.readAsArrayBuffer(file);
591
+ }
592
+ // C. Handle HTML/Code/Text (.py, .js, .html, .csv etc) -> Text naturally
593
+ else {
594
+ reader.onload = e => {
595
+ S.pending = { type: 'text', data: e.target.result, name: file.name };
596
+ showToast("Code/Text file attached!");
597
+ };
598
+ reader.readAsText(file);
 
 
 
 
 
 
 
 
 
599
  }
 
 
 
 
 
 
600
  }
601
+ input.value = ''; // reset
602
  }
603
 
604
+ function removeImg(){ S.pending=null; document.getElementById('imgPrev').classList.remove('show'); }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
605
 
606
  // --- UI INTERACTIONS ---
607
  function handleKey(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sendMsg();}}
608
  function autoGrow(el){el.style.height='auto';el.style.height=Math.min(el.scrollHeight,200)+'px';}
609
+ 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'); }
610
+ function forceScrollDown(){ const m=document.getElementById('msgs'); m.scrollTo({top: m.scrollHeight, behavior: 'smooth'}); S.isUserScrolling = false; document.getElementById('scrollBtn').classList.remove('show'); }
611
+ function autoScroll() { if(!S.isUserScrolling) { const m=document.getElementById('msgs'); m.scrollTop = m.scrollHeight; } }
612
+ function copyText(btn, text) { navigator.clipboard.writeText(text); const orig = btn.innerHTML; btn.innerHTML = "โœ“ Copied"; setTimeout(() => btn.innerHTML = orig, 2000); }
613
+ function copyWorkspaceCode() { if(S.currentWorkspaceId !== null) { navigator.clipboard.writeText(S.codeBlocks[S.currentWorkspaceId].code); const btn = document.getElementById('wsCopyBtn'); const orig = btn.innerHTML; btn.innerHTML = "โœ“ Copied"; setTimeout(() => btn.innerHTML = orig, 2000); } }
614
+
615
+ // --- WORKSPACE LOGIC ---
616
+ 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('open'); if(window.innerWidth <= 850) { document.getElementById('sidebar').classList.remove('show'); } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
617
  function closeWorkspace() { document.getElementById('workspace').classList.remove('open'); }
 
618
  const renderer = new marked.Renderer();
619
  renderer.code = function(token) {
620
  const codeText = typeof token === 'string' ? token : (token.text || '');
621
  const language = typeof token === 'string' ? arguments[1] : (token.lang || '');
622
+ if (window.isStreamingGlobalFlag) { return `<pre><code class="language-${language}">${codeText.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre>`; }
623
+ const id = S.codeBlocks.length; S.codeBlocks.push({ code: codeText, lang: language });
624
+ return `<div class="ws-trigger-card"><div class="ws-trigger-info"><div class="ws-trigger-icon">๐Ÿ’ป</div><div class="ws-trigger-text"><h4>Code Generated</h4><p>${language || 'Snippet'}</p></div></div><button class="ws-btn" onclick="openWorkspace(${id})">View in Workspace</button></div>`;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
625
  };
626
  marked.use({ renderer });
627
 
 
638
  const inp=document.getElementById('chatInput');
639
  const msg=inp.value.trim();if(!msg && !S.pending)return;
640
 
 
641
  if (!currentUser) {
642
  if (guestCount >= 3) { openAuthModal(); return showToast("Guest limit reached. Please Login."); }
643
  guestCount++; localStorage.setItem('arjun_guest', guestCount.toString());
 
655
  let attachments = [];
656
  let badge = null;
657
 
 
658
  if(S.pending) {
659
  if(S.pending.type === 'text') {
660
+ // Send Extracted Code/PDF text
661
+ backendPayloadMsg = `[Attached File Data: ${S.pending.name}]\n${S.pending.data}\n\nUser Question: ${msg}`;
662
  badge = '๐Ÿ“„ '+S.pending.name;
 
 
 
663
  } else {
664
+ // Send raw media data for Image, Audio, Video
665
+ attachments.push({ type: S.pending.type, data: S.pending.data, name: S.pending.name });
666
+ const icon = S.pending.type === 'video' ? '๐ŸŽฅ ' : (S.pending.type === 'audio' ? '๐ŸŽต ' : '๐Ÿ–ผ๏ธ ');
667
+ badge = icon + S.pending.name;
668
  }
 
669
  appendUserDOM(msg || '[File Attached]', badge);
670
  S.history.push({role: 'user', content: backendPayloadMsg, badge: badge});
671
  } else {
 
673
  S.history.push({role: 'user', content: msg});
674
  }
675
 
676
+ removeImg(); syncHistory();
 
677
 
678
  const botDiv = document.createElement('div');botDiv.className='msg bot';
679
  botDiv.innerHTML=`<div class="avatar"><img src="${LOGO_URL}"></div><div class="body"><div class="bubble"><div class="typing"><span></span><span></span><span></span></div></div></div>`;
680
  document.getElementById('msgs').appendChild(botDiv);
681
  forceScrollDown();
682
 
 
683
  const sysPrompt = `You are Arjun 2.O, a state-of-the-art AI assistant developed by Abhay Kumar.
684
  LANGUAGE PROTOCOL: You are fully bilingual. You MUST write and respond fluently in BOTH English and Hindi.
685
  1. If the user asks in English, reply entirely in English.
 
694
  Choose the best model based on the prompt. Example: if user says anime, use flux-anime.
695
  Always include this markdown tag when image generation is requested.`;
696
 
697
+ const cleanHistory = S.history.slice(0, -1).map(item => ({ role: item.role, content: item.content }));
 
 
 
 
698
 
699
  const payload = {
700
  message: backendPayloadMsg,
 
706
  };
707
 
708
  try{
709
+ const res = await fetch('/api/chat', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(payload) });
 
 
 
 
 
710
  if (!res.ok) throw new Error("Server error");
711
+ const reader = res.body.getReader(); const decoder = new TextDecoder();
 
 
712
  let accumulatedText = "";
713
+ window.isStreamingGlobalFlag = true;
 
 
714
  while (true) {
715
  const { done, value } = await reader.read();
716
  if (done) break;
717
  const chunk = decoder.decode(value, {stream: true});
718
  const lines = chunk.split('\n');
 
719
  for (let line of lines) {
720
  if (line.startsWith('data: ')) {
721
  const dataStr = line.substring(6);
 
724
  const dataObj = JSON.parse(dataStr);
725
  if (dataObj.choices && dataObj.choices[0].delta.content) {
726
  accumulatedText += dataObj.choices[0].delta.content;
727
+ renderBotText(botDiv, accumulatedText, true); autoScroll();
 
728
  }
729
  } catch (e) { }
730
  }
731
  }
732
  }
 
 
733
  window.isStreamingGlobalFlag = false;
734
  renderBotText(botDiv, accumulatedText, false);
 
735
  S.history.push({role: 'bot', content: accumulatedText});
736
  syncHistory();
737
+ }catch(err){ window.isStreamingGlobalFlag = false; renderBotText(botDiv,`**Error:** Server communication failed.`, false); }
 
 
 
 
 
738
  S.busy=false; document.getElementById('sendBtn').disabled=false; autoScroll();
739
  }
740
 
741
  function sendEx(t){document.getElementById('chatInput').value=t;sendMsg();}
742
+ function appendUserDOM(text, badgeText){ let bHtml = badgeText ? `<div style="font-size:11px;color:var(--accent);margin-bottom:4px;font-weight:600;">${esc(badgeText)}</div>` : ''; const el=document.createElement('div');el.className='msg user'; el.innerHTML=`<div class="avatar">U</div><div class="body">${bHtml}<div class="bubble">${esc(text)}</div></div>`; document.getElementById('msgs').appendChild(el); }
743
+ function appendBotDOM(content, isHistoryLoading=false) { const el=document.createElement('div');el.className='msg bot'; el.innerHTML=`<div class="avatar"><img src="${LOGO_URL}"></div><div class="body"><div class="bubble"></div></div>`; document.getElementById('msgs').appendChild(el); renderBotText(el, content, !isHistoryLoading); }
744
+ function renderBotText(el, raw, isStreaming) { const b = el.querySelector('.bubble'); const { mainText, thinkText } = parseThinking(raw); let html = ''; if (thinkText) { html += `<div class="think-box"><div class="think-title">๐Ÿง  Reasoning Process</div>${esc(thinkText)}</div>`; } html += marked.parse(mainText); if(isStreaming) { html += '<span class="blinking-cursor"></span>'; } b.innerHTML = html; let actionsDiv = el.querySelector('.msg-actions'); if(!isStreaming && !actionsDiv && mainText.trim().length > 0) { const cleanRaw = mainText.replace(/"/g, '&quot;').replace(/'/g, "\\'").replace(/\n/g, '\\n'); const actionsHTML = `<div class="msg-actions"><button class="action-btn" onclick="copyText(this, \`${cleanRaw}\`)"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg> Copy Text</button></div>`; el.querySelector('.body').insertAdjacentHTML('beforeend', actionsHTML); } }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
745
  function esc(t){return t.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');}
746
  </script>
747
  </body>