Vedika commited on
Commit
6f56f64
ยท
verified ยท
1 Parent(s): 1be59e6

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +117 -27
index.html CHANGED
@@ -19,7 +19,7 @@
19
  --glass-bg: rgba(255, 255, 255, 0.85); --glass-border: rgba(255, 255, 255, 0.4);
20
  --shadow-sm: 0 2px 8px rgba(0,0,0,0.04); --shadow-md: 0 8px 32px rgba(0,0,0,0.08);
21
  --shadow-lg: 0 16px 48px rgba(0,0,0,0.12);
22
- --font-serif: 'Instrument Serif', serif; --font-sans: 'Inter', -apple-system, sans-serif;
23
  --font-mono: 'Geist Mono', monospace;
24
  --radius-lg: 24px; --radius-md: 16px; --radius-sm: 8px;
25
  }
@@ -150,7 +150,8 @@ body:not(.show-thinking) .think-box {display: none;}
150
  .bubble code{font-family:var(--font-mono);background:rgba(0,0,0,0.05);padding:3px 6px;border-radius:6px;font-size:0.85em;color:#e83e8c;}
151
  .msg.user .bubble code{background:rgba(255,255,255,0.2);color:#fff;}
152
  .bubble img{max-width:100%;border-radius:12px;margin:10px 0;box-shadow:var(--shadow-sm);border:1px solid var(--border-light);}
153
- .bubble pre {display: none;}
 
154
 
155
  /* Workspace Trigger Card in Chat */
156
  .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);}
@@ -181,7 +182,7 @@ body:not(.show-thinking) .think-box {display: none;}
181
 
182
  .img-prev-inline{display:none;padding:12px 20px 0;align-items:center;gap:12px;}
183
  .img-prev-inline.show{display:flex;animation:fadeIn 0.3s;}
184
- .inline-th{width:40px;height:40px;object-fit:cover;border-radius:8px;border:1px solid var(--border-light);}
185
  .inline-info{flex:1;}
186
  .inline-nm{font-size:12px;font-weight:600;color:var(--text-main);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px;}
187
  .inline-rm{background:rgba(0,0,0,0.05);border:none;width:24px;height:24px;border-radius:50%;cursor:pointer;font-size:10px;display:flex;align-items:center;justify-content:center;transition:background 0.2s;}
@@ -195,6 +196,8 @@ body:not(.show-thinking) .think-box {display: none;}
195
  .attach-btn:hover{background:rgba(0,0,0,0.04);color:var(--text-main);}
196
  .attach-btn input{position:absolute;inset:0;opacity:0;cursor:pointer;}
197
  .attach-btn svg{width:20px;height:20px;}
 
 
198
 
199
  .send-btn{width:42px;height:42px;border-radius:14px;border:none;background:linear-gradient(135deg, #FF9933 0%, #ffffff 50%, #138808 100%);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.3s;box-shadow:0 4px 12px rgba(0,0,0,0.1);}
200
  .send-btn svg{width:18px;height:18px;fill:#1d1d1f;transition:transform 0.2s;}
@@ -317,7 +320,7 @@ body:not(.show-thinking) .think-box {display: none;}
317
  <div class="status-dot"></div>
318
  Arjun Intelligence
319
  </div>
320
- <div style="width:24px;"></div> <!-- Spacer for flex balance -->
321
  </header>
322
 
323
  <div class="messages" id="msgs" onscroll="checkScroll()">
@@ -350,19 +353,28 @@ body:not(.show-thinking) .think-box {display: none;}
350
  <div class="input-container">
351
  <div class="input-wrap">
352
  <div class="img-prev-inline" id="imgPrev">
353
- <img class="inline-th" id="imgThumb" src="">
 
354
  <div class="inline-info">
355
- <div class="inline-nm" id="imgName">image.png</div>
356
  </div>
357
  <button class="inline-rm" onclick="removeImg()">โœ•</button>
358
  </div>
359
 
360
  <div class="input-row">
361
- <button class="attach-btn" title="Upload Image">
 
362
  <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>
363
- <input type="file" accept="image/*" onchange="handleFile(this)" id="fileInput">
364
  </button>
 
 
 
 
 
 
365
  <textarea class="chat-ta" id="chatInput" placeholder="Message Arjun Compound..." rows="1" onkeydown="handleKey(event)" oninput="autoGrow(this)"></textarea>
 
366
  <button class="send-btn" id="sendBtn" onclick="sendMsg()">
367
  <svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
368
  </button>
@@ -435,6 +447,7 @@ const LOGO_URL = "https://i.ibb.co/0y2sdbq6/PT-20260523-133251-0000.png";
435
 
436
  let currentUser = localStorage.getItem('arjun_user');
437
  let guestCount = parseInt(localStorage.getItem('arjun_guest') || '0');
 
438
 
439
  const S = {
440
  history: [],
@@ -447,7 +460,6 @@ const S = {
447
 
448
  // --- INITIALIZATION ---
449
  document.addEventListener('DOMContentLoaded', () => {
450
- // Welcome Overlay Cache
451
  if(!localStorage.getItem('arjun_welcome_seen')) {
452
  document.getElementById('welcomeOverlay').style.display = 'flex';
453
  } else {
@@ -487,6 +499,30 @@ function toggleThinking(el) {
487
  else document.body.classList.remove('show-thinking');
488
  }
489
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  // --- AUTHENTICATION (GAS INTEGRATION) ---
491
  function updateAuthUI() {
492
  if (currentUser) {
@@ -580,7 +616,10 @@ async function fetchHistoryFromGAS() {
580
  S.history = JSON.parse(data.historyJSON);
581
  S.history.forEach(msg => {
582
  if(msg.role === 'user') appendUserDOM(msg.content, msg.badge);
583
- else appendBotDOM(msg.content, true);
 
 
 
584
  });
585
  setTimeout(forceScrollDown, 100);
586
  } else {
@@ -595,18 +634,43 @@ function syncHistory() {
595
  }
596
  }
597
 
598
- // --- FILE HANDLING ---
599
  function handleFile(input){
600
- const f=input.files[0];if(!f)return;
601
- const r=new FileReader();
602
- r.onload=e=>{
603
- const base64Data = e.target.result.split(',')[1];
604
- S.pending={type: 'image', data: base64Data, name: f.name};
605
- document.getElementById('imgThumb').src = e.target.result;
606
- document.getElementById('imgName').textContent=f.name;
607
- document.getElementById('imgPrev').classList.add('show');
608
- };
609
- r.readAsDataURL(f);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
610
  }
611
  function removeImg(){S.pending=null;document.getElementById('imgPrev').classList.remove('show');document.getElementById('fileInput').value='';}
612
 
@@ -652,7 +716,7 @@ function copyWorkspaceCode() {
652
  }
653
  }
654
 
655
- // --- WORKSPACE LOGIC (MARKDOWN RENDERER) ---
656
  function openWorkspace(id) {
657
  S.currentWorkspaceId = id;
658
  const block = S.codeBlocks[id];
@@ -670,6 +734,12 @@ const renderer = new marked.Renderer();
670
  renderer.code = function(token) {
671
  const codeText = typeof token === 'string' ? token : (token.text || '');
672
  const language = typeof token === 'string' ? arguments[1] : (token.lang || '');
 
 
 
 
 
 
673
  const id = S.codeBlocks.length;
674
  S.codeBlocks.push({ code: codeText, lang: language });
675
 
@@ -715,17 +785,29 @@ async function sendMsg(){
715
  const w=document.getElementById('welcome');
716
  if(w)w.style.display='none';
717
 
 
718
  let attachments = [];
719
  let badge = null;
 
 
720
  if(S.pending) {
721
- attachments.push(S.pending);
722
- badge = '๐Ÿ“Ž '+S.pending.name;
723
- appendUserDOM(msg || '[Image Attached]', badge);
724
- S.history.push({role: 'user', content: msg || '[Image Attached]', badge: badge});
 
 
 
 
 
 
 
 
725
  } else {
726
  appendUserDOM(msg);
727
  S.history.push({role: 'user', content: msg});
728
  }
 
729
  removeImg();
730
  syncHistory();
731
 
@@ -744,10 +826,12 @@ async function sendMsg(){
744
  Choose the best model based on the prompt. Example: if user says anime, use flux-anime.
745
  Always include this markdown tag when image generation is requested.`;
746
 
 
747
  const payload = {
748
- message: msg,
749
  attachments: attachments,
750
  system_prompt: sysPrompt,
 
751
  max_tokens: 4096,
752
  temperature: 0.7
753
  };
@@ -762,6 +846,8 @@ async function sendMsg(){
762
  const reader = res.body.getReader();
763
  const decoder = new TextDecoder();
764
  let accumulatedText = "";
 
 
765
 
766
  while (true) {
767
  const { done, value } = await reader.read();
@@ -784,12 +870,16 @@ async function sendMsg(){
784
  }
785
  }
786
  }
 
 
 
787
  renderBotText(botDiv, accumulatedText, false);
788
 
789
  S.history.push({role: 'bot', content: accumulatedText});
790
  syncHistory();
791
 
792
  }catch(err){
 
793
  renderBotText(botDiv,`**Error:** Server communication failed.`, false);
794
  }
795
 
 
19
  --glass-bg: rgba(255, 255, 255, 0.85); --glass-border: rgba(255, 255, 255, 0.4);
20
  --shadow-sm: 0 2px 8px rgba(0,0,0,0.04); --shadow-md: 0 8px 32px rgba(0,0,0,0.08);
21
  --shadow-lg: 0 16px 48px rgba(0,0,0,0.12);
22
+ --font-sans: 'Inter', -apple-system, sans-serif;
23
  --font-mono: 'Geist Mono', monospace;
24
  --radius-lg: 24px; --radius-md: 16px; --radius-sm: 8px;
25
  }
 
150
  .bubble code{font-family:var(--font-mono);background:rgba(0,0,0,0.05);padding:3px 6px;border-radius:6px;font-size:0.85em;color:#e83e8c;}
151
  .msg.user .bubble code{background:rgba(255,255,255,0.2);color:#fff;}
152
  .bubble img{max-width:100%;border-radius:12px;margin:10px 0;box-shadow:var(--shadow-sm);border:1px solid var(--border-light);}
153
+ .bubble pre {background: #1d1d1f; color:#fff; padding:16px; border-radius:12px; overflow-x:auto;}
154
+ .bubble pre code {background: transparent; color:#fff;}
155
 
156
  /* Workspace Trigger Card in Chat */
157
  .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);}
 
182
 
183
  .img-prev-inline{display:none;padding:12px 20px 0;align-items:center;gap:12px;}
184
  .img-prev-inline.show{display:flex;animation:fadeIn 0.3s;}
185
+ .inline-th{width:40px;height:40px;object-fit:cover;border-radius:8px;border:1px solid var(--border-light); display:flex; justify-content:center; align-items:center; background:#f0f0f0; font-size:20px;}
186
  .inline-info{flex:1;}
187
  .inline-nm{font-size:12px;font-weight:600;color:var(--text-main);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:150px;}
188
  .inline-rm{background:rgba(0,0,0,0.05);border:none;width:24px;height:24px;border-radius:50%;cursor:pointer;font-size:10px;display:flex;align-items:center;justify-content:center;transition:background 0.2s;}
 
196
  .attach-btn:hover{background:rgba(0,0,0,0.04);color:var(--text-main);}
197
  .attach-btn input{position:absolute;inset:0;opacity:0;cursor:pointer;}
198
  .attach-btn svg{width:20px;height:20px;}
199
+ .voice-btn.recording { color: #ff3b30; animation: pulseRecord 1.5s infinite; }
200
+ @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); } }
201
 
202
  .send-btn{width:42px;height:42px;border-radius:14px;border:none;background:linear-gradient(135deg, #FF9933 0%, #ffffff 50%, #138808 100%);display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all 0.3s;box-shadow:0 4px 12px rgba(0,0,0,0.1);}
203
  .send-btn svg{width:18px;height:18px;fill:#1d1d1f;transition:transform 0.2s;}
 
320
  <div class="status-dot"></div>
321
  Arjun Intelligence
322
  </div>
323
+ <div style="width:24px;"></div> <!-- Spacer -->
324
  </header>
325
 
326
  <div class="messages" id="msgs" onscroll="checkScroll()">
 
353
  <div class="input-container">
354
  <div class="input-wrap">
355
  <div class="img-prev-inline" id="imgPrev">
356
+ <img class="inline-th" id="imgThumb" src="" style="display:none;">
357
+ <div class="inline-th" id="fileIcon" style="display:none;">๐Ÿ“„</div>
358
  <div class="inline-info">
359
+ <div class="inline-nm" id="imgName">file.txt</div>
360
  </div>
361
  <button class="inline-rm" onclick="removeImg()">โœ•</button>
362
  </div>
363
 
364
  <div class="input-row">
365
+ <!-- Universal File Attach -->
366
+ <button class="attach-btn" title="Upload Image or Document">
367
  <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>
368
+ <input type="file" accept="*/*" onchange="handleFile(this)" id="fileInput">
369
  </button>
370
+
371
+ <!-- Voice Input Mic -->
372
+ <button class="attach-btn voice-btn" id="voiceBtn" onclick="toggleVoice()" title="Voice Input">
373
+ <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>
374
+ </button>
375
+
376
  <textarea class="chat-ta" id="chatInput" placeholder="Message Arjun Compound..." rows="1" onkeydown="handleKey(event)" oninput="autoGrow(this)"></textarea>
377
+
378
  <button class="send-btn" id="sendBtn" onclick="sendMsg()">
379
  <svg viewBox="0 0 24 24"><path d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"/></svg>
380
  </button>
 
447
 
448
  let currentUser = localStorage.getItem('arjun_user');
449
  let guestCount = parseInt(localStorage.getItem('arjun_guest') || '0');
450
+ window.isStreamingGlobalFlag = false; // FLAG TO PREVENT PREMATURE WORKSPACE BUTTONS
451
 
452
  const S = {
453
  history: [],
 
460
 
461
  // --- INITIALIZATION ---
462
  document.addEventListener('DOMContentLoaded', () => {
 
463
  if(!localStorage.getItem('arjun_welcome_seen')) {
464
  document.getElementById('welcomeOverlay').style.display = 'flex';
465
  } else {
 
499
  else document.body.classList.remove('show-thinking');
500
  }
501
 
502
+ // --- VOICE RECOGNITION (MIC FEATURE) ---
503
+ const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition;
504
+ let recognition = null;
505
+ let isRecording = false;
506
+
507
+ if (SpeechRecognition) {
508
+ recognition = new SpeechRecognition();
509
+ recognition.continuous = false; recognition.interimResults = true; recognition.lang = 'en-US';
510
+
511
+ recognition.onstart = () => { isRecording = true; document.getElementById('voiceBtn').classList.add('recording'); };
512
+ recognition.onresult = (event) => {
513
+ let transcript = '';
514
+ for (let i = event.resultIndex; i < event.results.length; ++i) { transcript += event.results[i][0].transcript; }
515
+ document.getElementById('chatInput').value = transcript; autoGrow(document.getElementById('chatInput'));
516
+ };
517
+ recognition.onend = () => { isRecording = false; document.getElementById('voiceBtn').classList.remove('recording'); };
518
+ recognition.onerror = () => { showToast("Voice recognition failed."); };
519
+ }
520
+ function toggleVoice() {
521
+ if(!recognition) return showToast("Voice input not supported in this browser.");
522
+ if(isRecording) recognition.stop(); else recognition.start();
523
+ }
524
+
525
+
526
  // --- AUTHENTICATION (GAS INTEGRATION) ---
527
  function updateAuthUI() {
528
  if (currentUser) {
 
616
  S.history = JSON.parse(data.historyJSON);
617
  S.history.forEach(msg => {
618
  if(msg.role === 'user') appendUserDOM(msg.content, msg.badge);
619
+ else {
620
+ window.isStreamingGlobalFlag = false; // Ensure final render
621
+ appendBotDOM(msg.content, true);
622
+ }
623
  });
624
  setTimeout(forceScrollDown, 100);
625
  } else {
 
634
  }
635
  }
636
 
637
+ // --- MULTI-FILE HANDLING (Image + Text/Code/Data) ---
638
  function handleFile(input){
639
+ const file = input.files[0]; if(!file) return;
640
+ const reader = new FileReader();
641
+
642
+ document.getElementById('imgThumb').style.display = 'none';
643
+ document.getElementById('fileIcon').style.display = 'none';
644
+ document.getElementById('imgName').textContent = file.name;
645
+ document.getElementById('imgPrev').classList.add('show');
646
+
647
+ // If Image
648
+ if (file.type.startsWith('image/')) {
649
+ reader.onload = e => {
650
+ const base64Data = e.target.result.split(',')[1];
651
+ S.pending = { type: 'image', data: base64Data, name: file.name };
652
+ document.getElementById('imgThumb').src = e.target.result;
653
+ document.getElementById('imgThumb').style.display = 'flex';
654
+ };
655
+ reader.readAsDataURL(file);
656
+ }
657
+ // If Text or Code File
658
+ else if (file.type.match(/(text|json|csv|xml)/) || file.name.match(/\.(txt|md|csv|json|py|js|html|css|cpp|c)$/i)) {
659
+ reader.onload = e => {
660
+ S.pending = { type: 'text', data: e.target.result, name: file.name };
661
+ document.getElementById('fileIcon').style.display = 'flex';
662
+ };
663
+ reader.readAsText(file);
664
+ }
665
+ // Other Fallback (PDF, etc)
666
+ else {
667
+ reader.onload = e => {
668
+ const base64Data = e.target.result.split(',')[1];
669
+ S.pending = { type: 'document', data: base64Data, name: file.name };
670
+ document.getElementById('fileIcon').style.display = 'flex';
671
+ };
672
+ reader.readAsDataURL(file);
673
+ }
674
  }
675
  function removeImg(){S.pending=null;document.getElementById('imgPrev').classList.remove('show');document.getElementById('fileInput').value='';}
676
 
 
716
  }
717
  }
718
 
719
+ // --- WORKSPACE LOGIC (MARKDOWN RENDERER FIXED FOR STREAMING) ---
720
  function openWorkspace(id) {
721
  S.currentWorkspaceId = id;
722
  const block = S.codeBlocks[id];
 
734
  renderer.code = function(token) {
735
  const codeText = typeof token === 'string' ? token : (token.text || '');
736
  const language = typeof token === 'string' ? arguments[1] : (token.lang || '');
737
+
738
+ // FIXED: DO NOT SHOW WORKSPACE BUTTON WHILE STREAMING
739
+ if (window.isStreamingGlobalFlag) {
740
+ return `<pre><code class="language-${language}">${codeText.replace(/</g, '&lt;').replace(/>/g, '&gt;')}</code></pre>`;
741
+ }
742
+
743
  const id = S.codeBlocks.length;
744
  S.codeBlocks.push({ code: codeText, lang: language });
745
 
 
785
  const w=document.getElementById('welcome');
786
  if(w)w.style.display='none';
787
 
788
+ let backendPayloadMsg = msg;
789
  let attachments = [];
790
  let badge = null;
791
+
792
+ // Multi-File Extraction Logic
793
  if(S.pending) {
794
+ if(S.pending.type === 'text') {
795
+ backendPayloadMsg = `[User attached file: ${S.pending.name}]\nFile Content:\n${S.pending.data}\n\nUser Message: ${msg}`;
796
+ badge = '๐Ÿ“„ '+S.pending.name;
797
+ } else if(S.pending.type === 'image') {
798
+ attachments.push(S.pending);
799
+ badge = '๐Ÿ–ผ๏ธ '+S.pending.name;
800
+ } else {
801
+ badge = '๐Ÿ“Ž '+S.pending.name;
802
+ }
803
+
804
+ appendUserDOM(msg || '[File Attached]', badge);
805
+ S.history.push({role: 'user', content: backendPayloadMsg, badge: badge});
806
  } else {
807
  appendUserDOM(msg);
808
  S.history.push({role: 'user', content: msg});
809
  }
810
+
811
  removeImg();
812
  syncHistory();
813
 
 
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
+ // FIXED: Memory (History) mapped inside the payload
830
  const payload = {
831
+ message: backendPayloadMsg,
832
  attachments: attachments,
833
  system_prompt: sysPrompt,
834
+ history: S.history.slice(0, -1), // Sends previous memory!
835
  max_tokens: 4096,
836
  temperature: 0.7
837
  };
 
846
  const reader = res.body.getReader();
847
  const decoder = new TextDecoder();
848
  let accumulatedText = "";
849
+
850
+ window.isStreamingGlobalFlag = true; // PREVENTS WORKSPACE BUTTON FROM SPAWNING EARLY
851
 
852
  while (true) {
853
  const { done, value } = await reader.read();
 
870
  }
871
  }
872
  }
873
+
874
+ // STREAMING DONE - ALLOW WORKSPACE BUTTON CREATION
875
+ window.isStreamingGlobalFlag = false;
876
  renderBotText(botDiv, accumulatedText, false);
877
 
878
  S.history.push({role: 'bot', content: accumulatedText});
879
  syncHistory();
880
 
881
  }catch(err){
882
+ window.isStreamingGlobalFlag = false;
883
  renderBotText(botDiv,`**Error:** Server communication failed.`, false);
884
  }
885