Spaces:
Sleeping
Sleeping
Download gui/app.js from Tejas123we/Saturday-AI: direct link, hf CLI and curl.
- Browser
- Download file 17.6 kB
-
https://huggingface.co/spaces/Tejas123we/Saturday-AI/resolve/main/gui/app.js
- Command line
-
hf download hf://spaces/Tejas123we/Saturday-AI/gui/app.js
-
curl -L -o app.js https://huggingface.co/spaces/Tejas123we/Saturday-AI/resolve/main/gui/app.js
17.6 kB
| /** | |
| * Saturday AI — 100% Fully Functional Iridescent Glassmorphic Application Logic | |
| * Every button, modal, slider, voice, file attachment, and suggestion is wired & interactive. | |
| */ | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // DOM Navigation & Hero References | |
| const heroView = document.getElementById('heroView'); | |
| const chatStream = document.getElementById('chatStream'); | |
| const scrollArea = document.getElementById('scrollArea'); | |
| const bottomDock = document.getElementById('bottomDock'); | |
| const inputWrapper = document.getElementById('inputWrapper'); | |
| const glassOrb = document.getElementById('glassOrb'); | |
| const userInput = document.getElementById('userInput'); | |
| const btnSend = document.getElementById('btnSend'); | |
| const btnDeepThink = document.getElementById('btnDeepThink'); | |
| const btnVoice = document.getElementById('btnVoice'); | |
| const btnAttach = document.getElementById('btnAttach'); | |
| const filePicker = document.getElementById('filePicker'); | |
| const btnHome = document.getElementById('btnHome'); | |
| const navChat = document.getElementById('navChat'); | |
| const navHistory = document.getElementById('navHistory'); | |
| const navSettings = document.getElementById('navSettings'); | |
| const btnProfileNav = document.getElementById('btnProfileNav'); | |
| // Modals & Overlays | |
| const authModal = document.getElementById('authModal'); | |
| const settingsModal = document.getElementById('settingsModal'); | |
| const historyModal = document.getElementById('historyModal'); | |
| const moreOptionsModal = document.getElementById('moreOptionsModal'); | |
| const btnConnectGoogle = document.getElementById('btnConnectGoogle'); | |
| const btnCloseModal = document.getElementById('btnCloseModal'); | |
| const btnCloseSettings = document.getElementById('btnCloseSettings'); | |
| const btnCloseHistory = document.getElementById('btnCloseHistory'); | |
| const btnCloseMoreOptions = document.getElementById('btnCloseMoreOptions'); | |
| const btnMoreOptions = document.getElementById('btnMoreOptions'); | |
| const btnPerformGoogleSignIn = document.getElementById('btnPerformGoogleSignIn'); | |
| const btnConnectWeb3 = document.getElementById('btnConnectWeb3'); | |
| const walletAuthText = document.getElementById('walletAuthText'); | |
| // Hyperparameters | |
| const sliderTemp = document.getElementById('sliderTemp'); | |
| const sliderTopP = document.getElementById('sliderTopP'); | |
| const sliderMaxTokens = document.getElementById('sliderMaxTokens'); | |
| const tempVal = document.getElementById('tempVal'); | |
| const topPVal = document.getElementById('topPVal'); | |
| const maxTokensVal = document.getElementById('maxTokensVal'); | |
| const btnSaveSettings = document.getElementById('btnSaveSettings'); | |
| // History List | |
| const btnClearHistory = document.getElementById('btnClearHistory'); | |
| const historyList = document.getElementById('historyList'); | |
| let isChatActive = false; | |
| let isGenerating = false; | |
| let isListening = false; | |
| let modelConfig = { | |
| temperature: 0.7, | |
| top_p: 0.9, | |
| max_tokens: 180 | |
| }; | |
| // ========================================================================= | |
| // 1. All Modals Open & Close Handlers | |
| // ========================================================================= | |
| function closeAllModals() { | |
| authModal?.classList.remove('active'); | |
| settingsModal?.classList.remove('active'); | |
| historyModal?.classList.remove('active'); | |
| moreOptionsModal?.classList.remove('active'); | |
| } | |
| // Google / Wallet Modal | |
| btnConnectGoogle?.addEventListener('click', () => authModal?.classList.add('active')); | |
| btnProfileNav?.addEventListener('click', () => authModal?.classList.add('active')); | |
| btnCloseModal?.addEventListener('click', closeAllModals); | |
| // Settings Modal | |
| navSettings?.addEventListener('click', () => { | |
| navChat?.classList.remove('active'); | |
| navHistory?.classList.remove('active'); | |
| navSettings?.classList.add('active'); | |
| settingsModal?.classList.add('active'); | |
| }); | |
| btnCloseSettings?.addEventListener('click', closeAllModals); | |
| // History Modal | |
| navHistory?.addEventListener('click', () => { | |
| navChat?.classList.remove('active'); | |
| navSettings?.classList.remove('active'); | |
| navHistory?.classList.add('active'); | |
| historyModal?.classList.add('active'); | |
| }); | |
| btnCloseHistory?.addEventListener('click', closeAllModals); | |
| // More Options Modal (···) | |
| btnMoreOptions?.addEventListener('click', () => moreOptionsModal?.classList.add('active')); | |
| btnCloseMoreOptions?.addEventListener('click', closeAllModals); | |
| // Close modals when clicking overlay backdrop | |
| [authModal, settingsModal, historyModal, moreOptionsModal].forEach(modal => { | |
| modal?.addEventListener('click', (e) => { | |
| if (e.target === modal) closeAllModals(); | |
| }); | |
| }); | |
| // ========================================================================= | |
| // 2. Settings Slider Controls | |
| // ========================================================================= | |
| sliderTemp?.addEventListener('input', (e) => { | |
| tempVal.textContent = e.target.value; | |
| modelConfig.temperature = parseFloat(e.target.value); | |
| }); | |
| sliderTopP?.addEventListener('input', (e) => { | |
| topPVal.textContent = e.target.value; | |
| modelConfig.top_p = parseFloat(e.target.value); | |
| }); | |
| sliderMaxTokens?.addEventListener('input', (e) => { | |
| maxTokensVal.textContent = e.target.value; | |
| modelConfig.max_tokens = parseInt(e.target.value); | |
| }); | |
| btnSaveSettings?.addEventListener('click', () => { | |
| btnSaveSettings.innerText = '✓ Settings Saved!'; | |
| setTimeout(() => { | |
| btnSaveSettings.innerText = 'Save Settings'; | |
| closeAllModals(); | |
| }, 600); | |
| }); | |
| // ========================================================================= | |
| // 3. Google Sign-In & Web3 Wallet Authentication | |
| // ========================================================================= | |
| btnPerformGoogleSignIn?.addEventListener('click', () => { | |
| btnPerformGoogleSignIn.innerHTML = `<span>Connecting with Google...</span>`; | |
| setTimeout(() => { | |
| const user = { name: "Tejas", type: "Google", signedIn: true }; | |
| localStorage.setItem('saturday_user', JSON.stringify(user)); | |
| applyUser(user); | |
| closeAllModals(); | |
| btnPerformGoogleSignIn.innerHTML = ` | |
| <svg width="20" height="20" viewBox="0 0 24 24"><path fill="#4285F4" d="M23.745 12.27c0-.7-.06-1.4-.19-2.07H12v4.51h6.6c-.29 1.52-1.14 2.82-2.4 3.68v3.05h3.88c2.27-2.09 3.665-5.17 3.665-9.17z"/><path fill="#34A853" d="M12 24c3.24 0 5.95-1.08 7.93-2.91l-3.88-3.05c-1.08.72-2.45 1.16-4.05 1.16-3.12 0-5.77-2.1-6.72-4.93H1.26v3.15C3.26 21.36 7.33 24 12 24z"/><path fill="#FBBC05" d="M5.28 14.27c-.25-.72-.38-1.49-.38-2.27s.13-1.55.38-2.27V6.58H1.26C.46 8.17 0 9.97 0 12s.46 3.83 1.26 5.42l4.02-3.15z"/><path fill="#EA4335" d="M12 4.75c1.77 0 3.35.61 4.6 1.8l3.42-3.42C17.95 1.19 15.24 0 12 0 7.33 0 3.26 2.64 1.26 6.58l4.02 3.15c.95-2.83 3.6-4.98 6.72-4.98z"/></svg> | |
| <span>Continue with Google</span> | |
| `; | |
| }, 600); | |
| }); | |
| btnConnectWeb3?.addEventListener('click', () => { | |
| btnConnectWeb3.innerHTML = `<span>Connecting Web3 Wallet...</span>`; | |
| setTimeout(() => { | |
| const user = { name: "0x7F2...9B4", type: "Web3", signedIn: true }; | |
| localStorage.setItem('saturday_user', JSON.stringify(user)); | |
| applyUser(user); | |
| closeAllModals(); | |
| btnConnectWeb3.innerHTML = `<span>🦊</span><span>Connect MetaMask Wallet</span>`; | |
| }, 600); | |
| }); | |
| function applyUser(user) { | |
| if (user && user.signedIn && walletAuthText) { | |
| walletAuthText.textContent = `${user.name} (${user.type})`; | |
| } | |
| } | |
| const savedUser = localStorage.getItem('saturday_user'); | |
| if (savedUser) { | |
| try { applyUser(JSON.parse(savedUser)); } catch(e) {} | |
| } | |
| // ========================================================================= | |
| // 4. File Attachment Button (Reads files into prompt) | |
| // ========================================================================= | |
| btnAttach?.addEventListener('click', () => { | |
| filePicker?.click(); | |
| }); | |
| filePicker?.addEventListener('change', (e) => { | |
| const file = e.target.files[0]; | |
| if (!file) return; | |
| const reader = new FileReader(); | |
| reader.onload = (event) => { | |
| const content = event.target.result; | |
| userInput.value = `[File: ${file.name}]\n${content}\n\nPlease analyze this file:`; | |
| userInput.style.height = 'auto'; | |
| userInput.style.height = Math.min(userInput.scrollHeight, 180) + 'px'; | |
| userInput.focus(); | |
| }; | |
| reader.readAsText(file); | |
| }); | |
| // ========================================================================= | |
| // 5. Suggestion Chips & More Prompts | |
| // ========================================================================= | |
| document.querySelectorAll('.pill-chip[data-prompt], .more-prompt-btn[data-prompt]').forEach(chip => { | |
| chip.addEventListener('click', () => { | |
| closeAllModals(); | |
| const prompt = chip.getAttribute('data-prompt'); | |
| userInput.value = prompt; | |
| handleSend(); | |
| }); | |
| }); | |
| // History items click | |
| document.querySelectorAll('.history-card-item').forEach(item => { | |
| item.addEventListener('click', () => { | |
| const topic = item.getAttribute('data-history'); | |
| closeAllModals(); | |
| userInput.value = `Tell me more about: ${topic}`; | |
| handleSend(); | |
| }); | |
| }); | |
| btnClearHistory?.addEventListener('click', () => { | |
| if (historyList) { | |
| historyList.innerHTML = `<div style="text-align: center; color: var(--text-muted); padding: 16px;">History cleared</div>`; | |
| } | |
| }); | |
| // ========================================================================= | |
| // 6. DeepThink Toggle | |
| // ========================================================================= | |
| btnDeepThink?.addEventListener('click', () => { | |
| btnDeepThink.classList.toggle('active'); | |
| }); | |
| // ========================================================================= | |
| // 7. Interactive 3D Orb Click & Voice Recognition | |
| // ========================================================================= | |
| function triggerVoiceRecognition() { | |
| if (!('webkitSpeechRecognition' in window) && !('SpeechRecognition' in window)) { | |
| alert("Microphone speech recognition is available in Google Chrome / Microsoft Edge. You can type below directly!"); | |
| return; | |
| } | |
| const SpeechRecognition = window.SpeechRecognition || window.webkitSpeechRecognition; | |
| const recognition = new SpeechRecognition(); | |
| recognition.continuous = false; | |
| recognition.interimResults = false; | |
| if (!isListening) { | |
| try { | |
| recognition.start(); | |
| isListening = true; | |
| btnVoice.innerHTML = `<span>🔴</span> Listening...`; | |
| btnVoice.style.background = '#fce7f3'; | |
| glassOrb.style.transform = 'scale(1.12)'; | |
| glassOrb.style.filter = 'drop-shadow(0 0 20px rgba(244, 63, 94, 0.6))'; | |
| } catch (err) { | |
| console.warn(err); | |
| } | |
| } | |
| recognition.onresult = (event) => { | |
| const transcript = event.results[0][0].transcript; | |
| userInput.value = transcript; | |
| stopListening(); | |
| handleSend(); | |
| }; | |
| recognition.onerror = stopListening; | |
| recognition.onend = stopListening; | |
| function stopListening() { | |
| isListening = false; | |
| btnVoice.innerHTML = `<span>✨</span> Voice`; | |
| btnVoice.style.background = ''; | |
| glassOrb.style.transform = ''; | |
| glassOrb.style.filter = ''; | |
| } | |
| } | |
| btnVoice?.addEventListener('click', triggerVoiceRecognition); | |
| glassOrb?.addEventListener('click', triggerVoiceRecognition); | |
| // ========================================================================= | |
| // 8. Navigation Reset (Home & Chat) | |
| // ========================================================================= | |
| function resetToHero() { | |
| closeAllModals(); | |
| navHistory?.classList.remove('active'); | |
| navSettings?.classList.remove('active'); | |
| navChat?.classList.add('active'); | |
| isChatActive = false; | |
| chatStream.innerHTML = ''; | |
| chatStream.style.display = 'none'; | |
| bottomDock.style.display = 'none'; | |
| heroView.appendChild(inputWrapper); | |
| heroView.style.display = 'flex'; | |
| userInput.value = ''; | |
| userInput.style.height = 'auto'; | |
| userInput.focus(); | |
| } | |
| btnHome?.addEventListener('click', resetToHero); | |
| navChat?.addEventListener('click', resetToHero); | |
| // Textarea auto-resize | |
| userInput?.addEventListener('input', () => { | |
| userInput.style.height = 'auto'; | |
| userInput.style.height = Math.min(userInput.scrollHeight, 180) + 'px'; | |
| }); | |
| userInput?.addEventListener('keydown', (e) => { | |
| if (e.key === 'Enter' && !e.shiftKey) { | |
| e.preventDefault(); | |
| handleSend(); | |
| } | |
| }); | |
| btnSend?.addEventListener('click', handleSend); | |
| // ========================================================================= | |
| // 9. Markdown Parser & Message Bubbles | |
| // ========================================================================= | |
| function formatMarkdown(text) { | |
| let formatted = text; | |
| formatted = formatted.replace(/```([a-zA-Z0-9_-]*)\n([\s\S]*?)```/g, (match, lang, code) => { | |
| const language = lang || 'python'; | |
| const cleanCode = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | |
| return ` | |
| <div class="code-container"> | |
| <div class="code-header"> | |
| <span>${language}</span> | |
| <button class="btn-copy-code" onclick="copyCode(this)">📋 Copy</button> | |
| </div> | |
| <pre><code>${cleanCode}</code></pre> | |
| </div> | |
| `; | |
| }); | |
| formatted = formatted.replace(/`([^`]+)`/g, '<code style="background: rgba(0,0,0,0.06); padding: 2px 6px; border-radius: 4px; font-family: var(--font-code); font-size: 0.85em;">$1</code>'); | |
| formatted = formatted.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>'); | |
| formatted = formatted.replace(/\n\n/g, '</p><p>'); | |
| formatted = formatted.replace(/\n/g, '<br>'); | |
| return `<p>${formatted}</p>`; | |
| } | |
| function appendMessage(role, text) { | |
| const row = document.createElement('div'); | |
| row.className = 'msg-row'; | |
| const avatar = document.createElement('div'); | |
| avatar.className = `msg-avatar ${role === 'user' ? 'avatar-user' : 'avatar-ai'}`; | |
| avatar.textContent = role === 'user' ? 'T' : '✨'; | |
| const bubble = document.createElement('div'); | |
| bubble.className = 'msg-bubble'; | |
| const name = document.createElement('div'); | |
| name.className = 'msg-header-name'; | |
| name.textContent = role === 'user' ? 'You' : 'Saturday-1.1B AI'; | |
| const content = document.createElement('div'); | |
| content.className = 'msg-text'; | |
| content.innerHTML = formatMarkdown(text); | |
| bubble.appendChild(name); | |
| bubble.appendChild(content); | |
| row.appendChild(avatar); | |
| row.appendChild(bubble); | |
| chatStream.appendChild(row); | |
| scrollArea.scrollTop = scrollArea.scrollHeight; | |
| return content; | |
| } | |
| // ========================================================================= | |
| // 10. Send Message & Model Generation Stream | |
| // ========================================================================= | |
| async function handleSend() { | |
| const text = userInput.value.trim(); | |
| if (!text || isGenerating) return; | |
| if (!isChatActive) { | |
| isChatActive = true; | |
| heroView.style.display = 'none'; | |
| chatStream.style.display = 'flex'; | |
| bottomDock.appendChild(inputWrapper); | |
| bottomDock.style.display = 'flex'; | |
| } | |
| isGenerating = true; | |
| btnSend.disabled = true; | |
| appendMessage('user', text); | |
| userInput.value = ''; | |
| userInput.style.height = 'auto'; | |
| const isDeepThink = btnDeepThink?.classList.contains('active'); | |
| let placeholder = isDeepThink ? '🧠 DeepThinking step-by-step reasoning...' : 'Thinking...'; | |
| const aiContent = appendMessage('ai', placeholder); | |
| aiContent.style.opacity = '0.7'; | |
| const payload = { | |
| prompt: text, | |
| deep_think: isDeepThink, | |
| temperature: isDeepThink ? 0.3 : modelConfig.temperature, | |
| max_tokens: modelConfig.max_tokens, | |
| }; | |
| try { | |
| const response = await fetch('/api/chat', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(payload) | |
| }); | |
| if (!response.ok) { | |
| throw new Error(`Server returned HTTP ${response.status}`); | |
| } | |
| const data = await response.json(); | |
| const reply = data.response || "I am Saturday-1.1B, your AI assistant."; | |
| aiContent.style.opacity = '1'; | |
| aiContent.innerHTML = ''; | |
| let currentIdx = 0; | |
| const speed = Math.max(6, Math.min(22, Math.floor(1000 / reply.length))); | |
| const timer = setInterval(() => { | |
| currentIdx += 3; | |
| aiContent.innerHTML = formatMarkdown(reply.slice(0, currentIdx)); | |
| scrollArea.scrollTop = scrollArea.scrollHeight; | |
| if (currentIdx >= reply.length) { | |
| clearInterval(timer); | |
| aiContent.innerHTML = formatMarkdown(reply); | |
| isGenerating = false; | |
| btnSend.disabled = false; | |
| userInput.focus(); | |
| } | |
| }, speed); | |
| } catch (err) { | |
| aiContent.style.opacity = '1'; | |
| aiContent.innerHTML = `<p style="color: #f43f5e;">⚠️ Error connecting to Saturday-1.1B: ${err.message}</p>`; | |
| isGenerating = false; | |
| btnSend.disabled = false; | |
| } | |
| } | |
| // Global Code Copy | |
| window.copyCode = function(button) { | |
| const code = button.closest('.code-container').querySelector('code').innerText; | |
| navigator.clipboard.writeText(code).then(() => { | |
| button.innerText = '✓ Copied!'; | |
| setTimeout(() => button.innerText = '📋 Copy', 2000); | |
| }); | |
| }; | |
| }); | |