Saturday-AI / gui /app.js
Tejas123we's picture
Deploy Saturday-1.1B AI with Iridescent Glassmorphic GUI
8bf1a8c
Raw History Blame Contribute Delete
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
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);
});
};
});