commit 902c7655fa330eec68147ee0ef555a420e3ee73a
parent 42ff9a4a333781482c941977fef5d0d0554d3cc0
Author: Amit Dutta <amitdutta4255@gmail.com>
Date: Thu, 22 Jan 2026 12:09:14 +0530
[2026-01-22]: added new login methods
Diffstat:
2 files changed, 230 insertions(+), 72 deletions(-)
diff --git a/docs/index.html b/docs/index.html
@@ -4,8 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Ada AI</title>
-<link rel="icon" type="image/png" href="logo.png">
-
+ <link rel="icon" type="image/png" href="logo.png">
<!-- Tailwind CSS -->
<script src="https://cdn.tailwindcss.com"></script>
@@ -445,38 +444,64 @@
</div>
<!-- Toast Notification (Custom Popups) -->
- <div id="toast" class="fixed bottom-24 md:bottom-10 left-1/2 -translate-x-1/2 bg-gemini-surface border border-gemini-surfaceHover text-white px-6 py-3 rounded-full shadow-2xl z-[220] transition-all duration-300 transform translate-y-20 opacity-0 pointer-events-none flex items-center gap-2">
- <i data-lucide="info" class="w-4 h-4 text-gemini-blue"></i>
- <span id="toast-message" class="text-sm font-medium">Notification</span>
+ <div id="toast" class="fixed bottom-24 md:bottom-10 left-1/2 -translate-x-1/2 bg-gemini-surface border border-gemini-surfaceHover text-white px-6 py-3 rounded-full shadow-2xl z-[220] transition-all duration-300 transform translate-y-20 opacity-0 pointer-events-none flex items-center gap-2 max-w-[90vw] w-max">
+ <i data-lucide="info" class="w-4 h-4 text-gemini-blue flex-shrink-0"></i>
+ <span id="toast-message" class="text-sm font-medium whitespace-normal text-center">Notification</span>
</div>
- <!-- Auth Modal (Updated for Multi-Auth) -->
+ <!-- Auth Modal (Updated for Email/Pass + Verification) -->
<div id="auth-modal" class="fixed inset-0 z-[100] flex items-center justify-center bg-[#131314] transition-opacity duration-300 hidden opacity-0 pointer-events-none">
<div class="w-full max-w-md p-8 text-center bg-[#131314] rounded-2xl md:bg-[#131314] md:border-none relative max-h-[90vh] overflow-y-auto">
<div class="mx-auto w-20 h-20 mb-6 rounded-full bg-gemini-surface flex items-center justify-center relative overflow-hidden group">
<div class="absolute inset-0 bg-gradient-to-tr from-blue-500/20 to-purple-500/20 opacity-0 group-hover:opacity-100 transition-opacity duration-500"></div>
<svg class="w-10 h-10 animate-spin-slow-entry drop-shadow-lg"><use href="#icon-ada"></use></svg>
</div>
- <h1 class="text-3xl font-medium text-white mb-3">Welcome to Ada</h1>
- <p class="text-gemini-textSecondary mb-8">Sign in to access your coding workspace.</p>
+ <h1 id="auth-title" class="text-3xl font-medium text-white mb-2">Welcome to Ada</h1>
+ <p id="auth-subtitle" class="text-gemini-textSecondary mb-8 text-sm">Sign in to access your workspace.</p>
- <!-- Google -->
- <button onclick="signInWithGoogle()" class="w-full bg-white text-[#131314] hover:bg-gray-100 font-medium py-3 px-6 rounded-full flex items-center justify-center gap-3 transition-colors mb-3">
- <img src="https://www.google.com/favicon.ico" class="w-5 h-5" alt="G">
- <span>Sign In with Google</span>
- </button>
- <br>
-<!-- Divider -->
- <div class="relative mb-6">
+ <form id="auth-form" onsubmit="handleAuthSubmit(event)" class="space-y-4 text-left">
+ <input type="email" id="auth-email" placeholder="Email" autocomplete="email" required class="w-full bg-gemini-surface border border-gemini-surfaceHover rounded-xl p-3 text-white focus:outline-none focus:border-gemini-blue placeholder-gemini-textSecondary">
+
+ <div class="relative">
+ <input type="password" id="auth-password" placeholder="Password" autocomplete="current-password" required class="w-full bg-gemini-surface border border-gemini-surfaceHover rounded-xl p-3 pr-10 text-white focus:outline-none focus:border-gemini-blue placeholder-gemini-textSecondary">
+ <button type="button" onclick="togglePasswordVisibility()" class="absolute right-3 top-1/2 -translate-y-1/2 text-gemini-textSecondary hover:text-white focus:outline-none">
+ <i id="password-toggle-icon" data-lucide="eye" class="w-5 h-5"></i>
+ </button>
+ </div>
+
+ <div id="auth-feedback" class="hidden text-center text-sm mt-2"></div>
+
+ <div id="signin-extras" class="flex justify-end">
+ <button type="button" onclick="handleForgotPassword()" class="text-xs text-gemini-textSecondary hover:text-gemini-blue hover:underline">Forgot Password?</button>
+ </div>
+
+ <button type="submit" id="auth-submit-btn" class="w-full bg-gemini-blue text-black font-semibold py-3 rounded-xl hover:bg-white transition-colors">
+ Sign In
+ </button>
+ </form>
+
+ <div class="mt-6">
+ <button type="button" id="toggle-auth-mode" onclick="toggleAuthMode()" class="text-sm text-gemini-blue hover:underline">
+ Don't have an account? Create one
+ </button>
+ </div>
+
+ <div class="relative my-8">
<div class="absolute inset-0 flex items-center"><div class="w-full border-t border-gemini-surfaceHover"></div></div>
- <div class="relative flex justify-center text-sm"><span class="px-2 bg-[#131314] text-gemini-textSecondary">Or continue with</span></div>
+ <span class="relative px-3 bg-[#131314] text-xs text-gemini-textSecondary uppercase tracking-widest">Or</span>
+ </div>
+
+ <div class="space-y-3">
+ <button onclick="signInWithGoogle()" class="w-full bg-white text-[#131314] hover:bg-gray-100 font-medium py-3 px-6 rounded-full flex items-center justify-center gap-3 transition-colors">
+ <img src="https://www.google.com/favicon.ico" class="w-5 h-5" alt="G">
+ <span>Sign In with Google</span>
+ </button>
+ <button onclick="signInAsGuest()" class="w-full bg-[#3C4043] text-white hover:bg-[#494c50] font-medium py-3 px-6 rounded-full flex items-center justify-center gap-3 transition-colors">
+ <i data-lucide="user" class="w-5 h-5"></i>
+ <span>Continue as Guest</span>
+ </button>
</div>
- <!-- Guest -->
- <button onclick="signInAsGuest()" class="w-full bg-[#3C4043] text-white hover:bg-[#494c50] font-medium py-3 px-6 rounded-full flex items-center justify-center gap-3 transition-colors">
- <i data-lucide="user" class="w-5 h-5"></i>
- <span>Continue as Guest</span>
- </button>
</div>
</div>
@@ -830,6 +855,7 @@
// --- STATE & INIT ---
let auth, db;
+ let isSignUpMode = false;
let state = {
messages: [],
isTyping: false,
@@ -1102,29 +1128,86 @@
}
// --- UI TOGGLES & HELPERS ---
- function showToast(message, type = 'info') {
- const toast = document.getElementById('toast');
- const icon = toast.querySelector('i');
- document.getElementById('toast-message').innerText = message;
+ let toastTimeout;
+
+ function showToast(msg, type = 'info') {
+ // Check for Mobile + Auth Modal Open case
+ const authModal = document.getElementById('auth-modal');
+ const isMobile = window.innerWidth < 768; // Tailwind md breakpoint
+ const isAuthOpen = !authModal.classList.contains('hidden') && !authModal.classList.contains('opacity-0');
+
+ if (isMobile && isAuthOpen) {
+ const feedback = document.getElementById('auth-feedback');
+ feedback.innerText = msg;
+
+ // Style based on type
+ if (type === 'error') {
+ feedback.className = 'text-center text-sm mt-3 p-3 rounded-lg bg-red-500/10 text-red-400 border border-red-500/20';
+ } else if (type === 'success') {
+ feedback.className = 'text-center text-sm mt-3 p-3 rounded-lg bg-green-500/10 text-green-400 border border-green-500/20';
+ } else {
+ feedback.className = 'text-center text-sm mt-3 p-3 rounded-lg bg-blue-500/10 text-blue-400 border border-blue-500/20';
+ }
+
+ feedback.classList.remove('hidden');
+
+ // Auto-hide inline feedback after 6s
+ if (toastTimeout) clearTimeout(toastTimeout);
+ toastTimeout = setTimeout(() => {
+ feedback.classList.add('hidden');
+ }, 6000);
+ return;
+ }
+
+ // Standard Toast Logic (PC or Auth closed)
+ const t = document.getElementById('toast');
+ if (toastTimeout) clearTimeout(toastTimeout);
+
+ // 1. Determine icon and style based on type
+ let iconName = 'info';
+ let iconColor = 'text-gemini-blue';
+ let containerClass = 'bg-gemini-surface border-gemini-accent text-white';
+
if (type === 'error') {
- icon.setAttribute('data-lucide', 'alert-circle');
- icon.className = 'w-4 h-4 text-red-400';
+ iconName = 'alert-circle';
+ iconColor = 'text-red-400';
+ containerClass = 'bg-red-500/10 border-red-500/20 text-red-400';
} else if (type === 'success') {
- icon.setAttribute('data-lucide', 'check-circle');
- icon.className = 'w-4 h-4 text-green-400';
- } else {
- icon.setAttribute('data-lucide', 'info');
- icon.className = 'w-4 h-4 text-gemini-blue';
+ iconName = 'check-circle';
+ iconColor = 'text-green-400';
}
- lucide.createIcons();
- toast.classList.remove('opacity-0', 'pointer-events-none', 'translate-y-20');
- setTimeout(() => {
- toast.classList.add('opacity-0', 'pointer-events-none', 'translate-y-20');
+ // 2. Completely rebuild innerHTML to ensure we have a fresh <i> tag for Lucide
+ t.innerHTML = `<i data-lucide="${iconName}" class="w-4 h-4 ${iconColor} flex-shrink-0"></i><span id="toast-message" class="text-sm font-medium whitespace-normal text-center">${msg}</span>`;
+
+ // 3. Set container classes and visibility
+ t.className = `fixed bottom-24 md:bottom-10 left-1/2 -translate-x-1/2 border px-6 py-3 rounded-full shadow-2xl z-[220] transition-all duration-300 transform translate-y-0 opacity-100 flex items-center gap-2 max-w-[90vw] w-max ${containerClass}`;
+
+ // 4. Render icons
+ lucide.createIcons();
+
+ // 5. Auto-hide
+ toastTimeout = setTimeout(() => {
+ t.classList.remove('opacity-100', 'translate-y-0');
+ t.classList.add('opacity-0', 'translate-y-20', 'pointer-events-none');
}, 3000);
}
+ function togglePasswordVisibility() {
+ const input = document.getElementById('auth-password');
+ const icon = document.getElementById('password-toggle-icon');
+
+ if (input.type === 'password') {
+ input.type = 'text';
+ icon.setAttribute('data-lucide', 'eye-off');
+ } else {
+ input.type = 'password';
+ icon.setAttribute('data-lucide', 'eye');
+ }
+ lucide.createIcons();
+ }
+
function toggleShareSetting(checkbox) {
settings.allowSharing = checkbox.checked;
localStorage.setItem('ada_allow_sharing', settings.allowSharing);
@@ -1477,24 +1560,46 @@
}
// --- AUTH & FIREBASE ---
- function checkAuth() {
- auth.onAuthStateChanged(user => {
+ async function checkAuth() {
+ auth.onAuthStateChanged(async (user) => {
const loadingOverlay = document.getElementById('loading-overlay');
const authModal = document.getElementById('auth-modal');
if (user) {
- state.user = user;
+ try {
+ // Force refresh user to get latest emailVerified status
+ await user.reload();
+ } catch (e) {
+ console.log("Session reload failed, signing out.");
+ auth.signOut();
+ return;
+ }
+
+ const updatedUser = auth.currentUser;
+ if (!updatedUser) return;
+
+ // Check if email is verified for email/password users
+ const isEmailProvider = updatedUser.providerData.some(p => p.providerId === 'password');
+ if (isEmailProvider && !updatedUser.emailVerified) {
+ showToast("Please verify your email. Check your inbox!", "error");
+ auth.signOut();
+ loadingOverlay.classList.add('opacity-0', 'pointer-events-none');
+ authModal.classList.remove('hidden', 'opacity-0', 'pointer-events-none');
+ return;
+ }
+
+ state.user = updatedUser;
localStorage.setItem('ada_user_cached', 'true');
loadingOverlay.classList.add('opacity-0', 'pointer-events-none');
- authModal.classList.add('hidden');
+ authModal.classList.add('hidden', 'opacity-0', 'pointer-events-none');
// --- SIDEBAR VISIBILITY LOGIC (Guest vs User) ---
const historyLabel = document.getElementById('history-label');
const historySearch = document.getElementById('history-search-container');
const historyList = document.getElementById('chat-history-list');
- if (user.isAnonymous) {
+ if (updatedUser.isAnonymous) {
// GUEST: Hide History
if(historyLabel) historyLabel.classList.add('hidden');
if(historySearch) historySearch.classList.add('hidden');
@@ -1510,12 +1615,12 @@
if(historySearch) historySearch.classList.remove('hidden');
if(historyList) historyList.classList.remove('hidden');
- document.getElementById('user-email').innerText = user.email || '';
- document.getElementById('user-initial').innerText = (user.displayName || 'U')[0];
+ document.getElementById('user-email').innerText = updatedUser.email || '';
+ document.getElementById('user-initial').innerText = (updatedUser.displayName || 'U')[0];
- if (user.photoURL) {
+ if (updatedUser.photoURL) {
const img = document.getElementById('user-avatar');
- img.src = user.photoURL;
+ img.src = updatedUser.photoURL;
img.classList.remove('hidden');
document.getElementById('user-initial').classList.add('hidden');
}
@@ -1523,7 +1628,7 @@
const welcomeName = document.getElementById('welcome-name');
if (welcomeName) {
- const name = user.isAnonymous ? 'Guest' : (user.email || user.displayName || 'Student');
+ const name = updatedUser.isAnonymous ? 'Guest' : (updatedUser.email || updatedUser.displayName || 'Student');
welcomeName.innerText = name;
welcomeName.title = name;
}
@@ -1531,7 +1636,7 @@
if(!state.currentChatId) state.currentChatId = 'session_' + Date.now();
// Only fetch history if not guest
- if (!user.isAnonymous) {
+ if (!updatedUser.isAnonymous) {
fetchChats();
}
} else {
@@ -1544,6 +1649,83 @@
}
// --- AUTH HANDLERS ---
+ function toggleAuthMode() {
+ isSignUpMode = !isSignUpMode;
+ const title = document.getElementById('auth-title');
+ const subtitle = document.getElementById('auth-subtitle');
+ const btn = document.getElementById('auth-submit-btn');
+ const toggle = document.getElementById('toggle-auth-mode');
+ const extras = document.getElementById('signin-extras');
+
+ // Hide feedback when switching modes
+ document.getElementById('auth-feedback').classList.add('hidden');
+
+ if (isSignUpMode) {
+ title.innerText = "Create Account";
+ subtitle.innerText = "Join Ada to save your code history.";
+ btn.innerText = "Register";
+ toggle.innerText = "Already have an account? Sign In";
+ extras.classList.add('hidden');
+ } else {
+ title.innerText = "Welcome to Ada";
+ subtitle.innerText = "Sign in to access your workspace.";
+ btn.innerText = "Sign In";
+ toggle.innerText = "Don't have an account? Create one";
+ extras.classList.remove('hidden');
+ }
+ }
+
+ async function handleAuthSubmit(e) {
+ e.preventDefault();
+ const email = document.getElementById('auth-email').value;
+ const pass = document.getElementById('auth-password').value;
+ const btn = document.getElementById('auth-submit-btn');
+
+ // Hide previous feedback
+ document.getElementById('auth-feedback').classList.add('hidden');
+
+ if (!email || !pass) return;
+
+ try {
+ btn.disabled = true;
+ btn.innerText = isSignUpMode ? "Registering..." : "Signing in...";
+
+ if (isSignUpMode) {
+ // Create account
+ const cred = await auth.createUserWithEmailAndPassword(email, pass);
+ // Send verification email
+ await cred.user.sendEmailVerification();
+ // Sign out immediately so they can't log in without verification
+ await auth.signOut();
+ // Switch to Sign In mode FIRST so the toast can populate the correct feedback container
+ toggleAuthMode();
+ showToast("Verification email sent! Please check your inbox and spam folder.", "success");
+ } else {
+ // Sign in
+ const cred = await auth.signInWithEmailAndPassword(email, pass);
+ // Reload to check verification status handled in checkAuth
+ }
+ } catch (err) {
+ showToast(err.message, "error");
+ } finally {
+ btn.disabled = false;
+ btn.innerText = isSignUpMode ? "Register" : "Sign In";
+ }
+ }
+
+ async function handleForgotPassword() {
+ const email = document.getElementById('auth-email').value;
+ if (!email) {
+ showToast("Enter your email first.", "error");
+ return;
+ }
+ try {
+ await auth.sendPasswordResetEmail(email);
+ showToast("Password reset link sent!", "success");
+ } catch (err) {
+ showToast(err.message, "error");
+ }
+ }
function signInWithGoogle() {
const provider = new firebase.auth.GoogleAuthProvider();
@@ -1560,30 +1742,6 @@
});
}
- function handleEmailSignIn() {
- const email = document.getElementById('auth-email').value;
- const pass = document.getElementById('auth-password').value;
- if(!email || !pass) {
- showToast("Please enter email and password", "error");
- return;
- }
- auth.signInWithEmailAndPassword(email, pass).catch(e => {
- showToast(e.message, "error");
- });
- }
-
- function handleEmailSignUp() {
- const email = document.getElementById('auth-email').value;
- const pass = document.getElementById('auth-password').value;
- if(!email || !pass) {
- showToast("Please enter email and password", "error");
- return;
- }
- auth.createUserWithEmailAndPassword(email, pass).catch(e => {
- showToast(e.message, "error");
- });
- }
-
function signOutUser() {
auth.signOut().then(() => {
localStorage.removeItem('ada_user_cached');
diff --git a/docs/index.html b/docs/indexbackup_21-01-2026_01.html