// Sign-in console — futuristic authentication boot screen. // Identity verification, biometric scan, security clearance check. function SignInConsole({ onAuthenticated, theme = "amber" }) { const loginGreeting = "Jarvis login terminal online. Secure authentication is ready. Please provide your operator credentials."; const [stage, setStage] = React.useState("idle"); // idle | scanning | verifying | granted | denied const [username, setUsername] = React.useState(""); const [passphrase, setPassphrase] = React.useState(""); const [biometric, setBiometric] = React.useState(0); // 0..1 const [voiceEnabled, setVoiceEnabled] = React.useState(true); const [voiceStatus, setVoiceStatus] = React.useState("VOICE READY"); const [logLines, setLogLines] = React.useState([ { t: "00:00.001", k: "boot", m: "JARVIS Core v4.2.1 — initializing…" }, { t: "00:00.034", k: "boot", m: "Secure channel established · AES-256-GCM" }, { t: "00:00.082", k: "boot", m: "Awaiting operator credentials." }, ]); const [pulseT, setPulseT] = React.useState(0); const canvasRef = React.useRef(null); const greetedRef = React.useRef(false); React.useEffect(() => { if (window.__JARVIS_LOGIN_ERROR) { pushLog("auth", "Access denied by server vault. Check operator handle/passphrase."); } }, []); React.useEffect(() => { if (!voiceEnabled || greetedRef.current) return; let cancelled = false; let playing = false; const speakOnce = async () => { if (cancelled || greetedRef.current) return; if (playing) return; playing = true; const ok = await speakJarvisLogin(loginGreeting, "login.mp3"); playing = false; if (cancelled || greetedRef.current) return; if (ok) { greetedRef.current = true; setVoiceStatus("GREETING SENT"); pushLog("boot", "Voice greeting transmitted."); } else { setVoiceStatus("CLICK OR TYPE TO ARM VOICE"); } }; const timer = setTimeout(speakOnce, 700); const unlock = () => { primeJarvisLoginAudio(); speakOnce(); }; window.addEventListener("pointerdown", unlock, { once: true }); window.addEventListener("keydown", unlock, { once: true }); return () => { cancelled = true; clearTimeout(timer); window.removeEventListener("pointerdown", unlock); window.removeEventListener("keydown", unlock); }; }, [voiceEnabled, loginGreeting]); // ambient pulse counter React.useEffect(() => { const id = setInterval(() => setPulseT(t => t + 1), 50); return () => clearInterval(id); }, []); // biometric scanner viz React.useEffect(() => { const c = canvasRef.current; if (!c) return; const ctx = c.getContext("2d"); const dpr = Math.min(window.devicePixelRatio || 1, 2); let raf = 0; let t0 = performance.now(); function resize() { const r = c.getBoundingClientRect(); c.width = r.width * dpr; c.height = r.height * dpr; } resize(); const ro = new ResizeObserver(resize); ro.observe(c); function tick() { const now = performance.now(); const t = now - t0; const w = c.width, h = c.height; ctx.clearRect(0, 0, w, h); const cx = w / 2, cy = h / 2; const R = Math.min(w, h) * 0.4; // outer rings for (let i = 0; i < 4; i++) { ctx.beginPath(); ctx.arc(cx, cy, R - i * 8 * dpr, 0, Math.PI * 2); ctx.strokeStyle = `rgba(255,179,71,${0.2 - i * 0.04})`; ctx.lineWidth = 1 * dpr; ctx.stroke(); } // tick marks for (let i = 0; i < 60; i++) { const a = (i / 60) * Math.PI * 2; const isMaj = i % 5 === 0; ctx.beginPath(); ctx.moveTo(cx + Math.cos(a) * R, cy + Math.sin(a) * R); ctx.lineTo(cx + Math.cos(a) * (R + (isMaj ? 8 : 4) * dpr), cy + Math.sin(a) * (R + (isMaj ? 8 : 4) * dpr)); ctx.strokeStyle = isMaj ? "rgba(255,179,71,0.7)" : "rgba(255,179,71,0.3)"; ctx.stroke(); } // sweep arc const sweep = (t * 0.001) % (Math.PI * 2); ctx.beginPath(); ctx.arc(cx, cy, R - 18 * dpr, sweep, sweep + 0.7); ctx.strokeStyle = "var(--primary)"; ctx.strokeStyle = stage === "scanning" ? getCSS("--primary") : `rgba(255,179,71,0.5)`; ctx.lineWidth = 2 * dpr; ctx.stroke(); // fingerprint-ish concentric arcs ctx.lineWidth = 1.4 * dpr; for (let i = 0; i < 8; i++) { const r = (R - 60 * dpr) * (0.3 + i * 0.09); const phase = i * 0.4 + Math.sin(t * 0.001 + i) * 0.3; ctx.beginPath(); ctx.arc(cx, cy, r, phase, phase + Math.PI * 1.4); const op = stage === "scanning" ? 0.8 : 0.5; ctx.strokeStyle = `${getCSS("--primary")}${Math.floor(op * 255).toString(16).padStart(2, "0")}`; ctx.stroke(); } // scanning bar if (stage === "scanning" || stage === "verifying") { const y = cy - R + ((t * 0.4) % (R * 2)); ctx.beginPath(); ctx.moveTo(cx - R, y); ctx.lineTo(cx + R, y); ctx.strokeStyle = getCSS("--hot"); ctx.lineWidth = 1.5 * dpr; ctx.stroke(); // glow band const grad = ctx.createLinearGradient(0, y - 30 * dpr, 0, y + 30 * dpr); grad.addColorStop(0, "rgba(255,94,58,0)"); grad.addColorStop(0.5, "rgba(255,94,58,0.18)"); grad.addColorStop(1, "rgba(255,94,58,0)"); ctx.fillStyle = grad; ctx.fillRect(cx - R, y - 30 * dpr, R * 2, 60 * dpr); } raf = requestAnimationFrame(tick); } tick(); return () => { cancelAnimationFrame(raf); ro.disconnect(); }; }, [stage]); function getCSS(v) { return getComputedStyle(document.documentElement).getPropertyValue(v).trim() || "#ffb347"; } function pushLog(k, m) { const d = new Date(); const t = `${String(d.getMinutes()).padStart(2, "0")}:${String(d.getSeconds()).padStart(2, "0")}.${String(d.getMilliseconds()).padStart(3, "0")}`; setLogLines(ll => [...ll, { t, k, m }].slice(-12)); } async function toggleLoginVoice() { if (voiceEnabled) { primeJarvisLoginAudio(); const ok = await speakJarvisLogin(loginGreeting, "login.mp3"); setVoiceStatus(ok ? "VOICE ONLINE" : "CLICK OR TYPE TO ARM VOICE"); pushLog("boot", ok ? "Login voice link online." : "Voice link awaiting browser gesture."); return; } const next = !voiceEnabled; setVoiceEnabled(next); if (next) { greetedRef.current = false; const ok = await speakJarvisLogin("Login voice link online."); setVoiceStatus(ok ? "VOICE ONLINE" : "CLICK OR TYPE TO ARM VOICE"); pushLog("boot", ok ? "Login voice link online." : "Voice link awaiting browser gesture."); } else { if ("speechSynthesis" in window) window.speechSynthesis.cancel(); setVoiceStatus("VOICE MUTED"); pushLog("boot", "Login voice muted."); } } async function serverLogin() { const res = await fetch(`${window.__JARVIS_BASE_PATH || ""}/api/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "same-origin", body: JSON.stringify({ username: username.trim(), password: passphrase, }), }); let data = {}; try { data = await res.json(); } catch (e) { data = {}; } if (!res.ok || !data.ok) { throw new Error(data.message || "Server rejected the operator credentials."); } return data; } async function authenticate(e) { if (e) e.preventDefault(); if (!username.trim() || !passphrase.trim() || stage !== "idle") return; setStage("scanning"); pushLog("auth", `Operator handle received: ${username.toUpperCase()}`); pushLog("auth", "Engaging biometric subsystem…"); // animate biometric progress await animateProgress(setBiometric, 0, 1, 1600); setStage("verifying"); pushLog("scan", "Biometric signature ✓ — match confidence 99.84%"); await sleep(400); pushLog("crypto", "Cross-referencing identity vault…"); await sleep(450); pushLog("crypto", "Submitting credentials to server vault."); try { const data = await serverLogin(); pushLog("crypto", "Server vault accepted credential hash."); pushLog("auth", `Welcome back, ${(data.username || username).split(/\s+/)[0].toUpperCase()}.`); setStage("granted"); await sleep(650); onAuthenticated && onAuthenticated({ username: data.username || username.trim() }); window.location.assign(`${window.__JARVIS_BASE_PATH || ""}/`); } catch (err) { setStage("denied"); pushLog("auth", err.message || "Access denied by server vault."); await sleep(1100); setBiometric(0); setPassphrase(""); setStage("idle"); } } function animateProgress(setter, from, to, ms) { return new Promise(resolve => { const start = performance.now(); function step() { const t = Math.min(1, (performance.now() - start) / ms); setter(from + (to - from) * t); if (t < 1) requestAnimationFrame(step); else resolve(); } step(); }); } function sleep(ms) { return new Promise(r => setTimeout(r, ms)); } const tagColor = (k) => ({ boot: "var(--ink-2)", auth: "var(--primary)", scan: "var(--hot)", crypto: "var(--good)", }[k] || "var(--ink-2)"); return (
Provide your operator handle and passphrase. Biometric signature will be captured automatically.