Avoca / index.html
luguog's picture
Update index.html
c923a19 verified
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width,initial-scale=1" /> <title>๐ŸŒฟ Green Miracle 2085 โ€” Hyper Functional BackUIX</title> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;600;800&display=swap" rel="stylesheet"> <script src="https://cdn.tailwindcss.com"></script> <style> /* === GLOBAL THEME VARIABLES ================================================ */ :root { --bg1:#e8f7ee; --bg2:#bbf7d0; --fg:#052e16; --accent:#10b981; --shadow:#cce3d7; --light:#ffffff; --radius:22px; --pad:1rem; } /* === BASE RESET =========================================================== */ *{box-sizing:border-box;} html,body{margin:0;padding:0;font-family:'Poppins',sans-serif;height:100%;} body{ background:radial-gradient(circle at 30% 30%,var(--bg2),var(--bg1) 80%); color:var(--fg);display:flex;flex-direction:column;align-items:center; justify-content:flex-start;min-height:100vh;overflow:hidden;position:relative; } body::before{ content:"";position:absolute;inset:-50%; background: radial-gradient(circle,rgba(16,185,129,.15)0%,transparent70%), radial-gradient(circle at 70%40%,rgba(5,150,105,.1)0%,transparent60%); animation:glow 18s infinite alternate ease-in-out; z-index:-1; } @keyframes glow{0%{transform:scale(1)}100%{transform:scale(1.35)rotate(180deg)}} /* === HEADER =============================================================== */ header{ text-align:center;padding-top:2rem;z-index:2; } h1{ font-weight:800;font-size:2.4rem; background:linear-gradient(90deg,#10b981,#6ee7b7); -webkit-background-clip:text;color:transparent; text-shadow:0 0 12px rgba(110,231,183,.6); } p.subtitle{margin-top:.25rem;font-size:.9rem;color:#064e3b;} /* === CHAT CONTAINER SHELL ================================================= */ main#app{ flex-grow:1;width:90%;max-width:960px;margin:1rem auto; display:flex;flex-direction:column;align-items:stretch; justify-content:flex-start; background:rgba(255,255,255,.6); backdrop-filter:blur(12px); border-radius:var(--radius); box-shadow:10px 10px 25px var(--shadow),-10px -10px 25px var(--light); position:relative;overflow:hidden; } /* === SCROLLBAR ============================================================ */ ::-webkit-scrollbar{width:6px;height:6px;} ::-webkit-scrollbar-thumb{background:#a7f3d0;border-radius:3px;} /* === FOOTER INPUT STRIP =================================================== */ footer{ width:90%;max-width:960px;margin:1rem auto 1.5rem; display:flex;align-items:center; background:rgba(255,255,255,.7);backdrop-filter:blur(8px); border-radius:var(--radius); box-shadow:inset 6px 6px 12px rgba(0,0,0,.1); padding:.6rem .8rem; } footer input[type=text]{ flex-grow:1;border:none;outline:none;background:transparent; padding:.8rem 1rem;color:#064e3b;font-size:.95rem; } footer button,label.file-btn{ background:linear-gradient(135deg,#34d399,#10b981);color:#fff;border:none; border-radius:15px;padding:.8rem 1.2rem;margin-left:.6rem;cursor:pointer; font-size:.85rem;box-shadow:2px 2px 6px rgba(0,0,0,.2); transition:transform .2s ease; } footer button:hover,label.file-btn:hover{transform:scale(1.05);} label.file-btn input{display:none;} </style> </head> <body> <header> <h1>๐ŸŒฟ Green Miracle 2085 โ€” Hyper Functional BackUIX</h1> <p class="subtitle">Bioluminescent adaptive chat interface with local memory</p> </header> <!-- === MAIN CONTAINER ==================================================== --> <main id="app"> <section id="chat" class="flex flex-col overflow-y-auto p-4"></section> </main> <!-- === FOOTER BAR ======================================================== --> <footer> <input id="prompt" type="text" placeholder="Whisper to the bio-coreโ€ฆ" /> <label class="file-btn"> Upload<input id="file" type="file" accept=".json,.txt,.zip" /> </label> <button id="send">Send</button> </footer> <script> /* === BASIC STATE ========================================================== */ const chat = document.getElementById("chat"); const input = document.getElementById("prompt"); const send = document.getElementById("send"); const fileIn = document.getElementById("file"); let fileData = null; let usefulness = 0; let isTyping = false; /* === HELPER: CREATE A CHAT BUBBLE ======================================== */ function bubble(text, role){ const b = document.createElement("div"); b.className = `bubble ${role}`; b.style.alignSelf = role==="user" ? "flex-end" : "flex-start"; b.style.background = role==="user" ? "linear-gradient(145deg,#a7f3d0,#6ee7b7)" : "linear-gradient(145deg,#f0fdf4,#dcfce7)"; b.style.borderRadius = "18px"; b.style.padding = "0.9rem 1.2rem"; b.style.margin = "0.4rem 0"; b.style.maxWidth = "75%"; b.style.wordBreak = "break-word"; b.style.boxShadow = "inset 4px 4px 8px rgba(0,0,0,.05)"; b.textContent = text; chat.appendChild(b); chat.scrollTop = chat.scrollHeight; return b; } /* === HELPER: SCROLL TO BOTTOM ============================================ */ function scrollChat(){ chat.scrollTop = chat.scrollHeight; } /* === FILE UPLOAD HANDLER ================================================= */ fileIn.addEventListener("change", e=>{ const f = e.target.files[0]; if(!f) return; const r = new FileReader(); r.onload = ()=>{ try{ fileData = f.name.endsWith(".json") ? JSON.parse(r.result) : r.result.slice(0,400); bubble(`๐Ÿ“ฆ ${f.name} absorbed into eco-memory.`, "bot"); }catch{ fileData = r.result.slice(0,400); bubble(`โš ๏ธ ${f.name} loaded as raw text.`, "bot"); } saveMemory(); }; r.readAsText(f); }); /* === INPUT SEND HANDLER ================================================== */ async function sendMessage(){ const text = input.value.trim(); if(!text || isTyping) return; bubble(text, "user"); input.value = ""; scrollChat(); isTyping = true; const thinking = bubble("๐ŸŒฑ Synthesizing biothoughtโ€ฆ", "bot"); await simulateReply(thinking, text); isTyping = false; } /* === SIMULATED AI REPLY ================================================== */ async function simulateReply(target, userText){ const delay = ms => new Promise(r => setTimeout(r, ms)); await delay(800 + Math.random()*600); const responses = [ "๐ŸŒฟ The forest listens and replies softly.", "๐Ÿงฌ Organic data flows through the roots of memory.", "๐ŸŒž Testing mode active โ€” biosphere stable.", "๐Ÿ’ง Photosynthetic thoughts resonate in green tones.", "๐Ÿƒ The winds echo your words to every leaf." ]; const reply = responses[Math.floor(Math.random()*responses.length)]; await typeOut(target, reply); saveMemory(); } /* === TYPE-OUT EFFECT ===================================================== */ async function typeOut(element, text){ element.textContent = ""; for(let i=0;i<text.length;i++){ element.textContent += text[i]; if(i%5===0) await new Promise(r=>setTimeout(r,20)); } } /* === EVENT BINDINGS ====================================================== */ input.addEventListener("keypress",e=>{ if(e.key==="Enter") sendMessage(); }); send.addEventListener("click",sendMessage); /* === MEMORY PLACEHOLDERS (implemented in Part 3) ========================= */ function saveMemory(){} // stub function loadMemory(){} // stub </script> <script> /* === MEMORY MANAGEMENT ==================================================== */ const STORAGE_KEY = "greenMiracle2085HF_chatMemory"; /* Save entire chat log to localStorage */ function saveMemory(){ const bubbles = Array.from(chat.querySelectorAll(".bubble")); const messages = bubbles.map(b => ({ text: b.textContent, role: b.style.alignSelf === "flex-end" ? "user" : "bot" })); localStorage.setItem(STORAGE_KEY, JSON.stringify(messages)); } /* Load chat log from localStorage at startup */ function loadMemory(){ const saved = localStorage.getItem(STORAGE_KEY); if(!saved) return; try{ const messages = JSON.parse(saved); messages.forEach(m => bubble(m.text, m.role)); }catch(e){ console.warn("Memory load error:", e); } } /* Clear memory manually (used later in settings panel) */ function clearMemory(){ localStorage.removeItem(STORAGE_KEY); chat.innerHTML = ""; } /* Load previous conversation on page load */ window.addEventListener("DOMContentLoaded", loadMemory); /* === IMPROVED SIMULATED AI REPLIES ======================================= */ let conversationMemory = []; async function simulateReply(target, userText){ // Keep a rolling history of last 5 user inputs for pseudo-context conversationMemory.push(userText); if(conversationMemory.length>5) conversationMemory.shift(); const context = conversationMemory.join(" / "); const delay = ms => new Promise(r => setTimeout(r, ms)); await delay(800 + Math.random()*600); // Generate contextual pseudo-reply const seeds = [ "๐ŸŒฟ The canopy absorbs your thoughts about", "๐Ÿงฌ Biocircuit analysis indicates resonance with", "๐Ÿ’ซ The neural flora reflects on", "๐ŸŒธ A fragrant conclusion emerges from", "๐ŸŒž Light filters through the leaves regarding" ]; const endPhrases = [ "and restores balance to the ecosystem.", "โ€” harmony achieved within organic code.", "while chlorophyll algorithms sing softly.", "as the soil whispers memories of data.", "bringing renewal to digital roots." ]; const prefix = seeds[Math.floor(Math.random()*seeds.length)]; const suffix = endPhrases[Math.floor(Math.random()*endPhrases.length)]; const reply = `${prefix} "${userText}" ${suffix}`; await typeOut(target, reply); saveMemory(); } /* === AUTO-SCROLL ENHANCEMENT ============================================= */ const observer = new MutationObserver(()=>{ chat.scrollTop = chat.scrollHeight; }); observer.observe(chat,{childList:true}); /* === SMALL RATE-LIMIT GUARD ============================================== */ let lastSend = 0; async function sendMessage(){ const now = Date.now(); if(now - lastSend < 800 || isTyping) return; lastSend = now; const text = input.value.trim(); if(!text) return; bubble(text,"user"); input.value=""; isTyping=true; const waiting = bubble("๐ŸŒฑ Synthesizing biothoughtโ€ฆ","bot"); await simulateReply(waiting,text); isTyping=false; } /* === BINDINGS UPDATED ==================================================== */ input.removeEventListener("keypress",()=>{}); input.addEventListener("keypress",e=>{ if(e.key==="Enter") sendMessage(); }); send.onclick = sendMessage; </script> <script> /* === THEME SYSTEM ========================================================= */ const THEMES = { light:{ "--bg1":"#e8f7ee","--bg2":"#bbf7d0","--fg":"#052e16","--accent":"#10b981" }, dark:{ "--bg1":"#0f172a","--bg2":"#1e293b","--fg":"#f8fafc","--accent":"#14b8a6" }, aurora:{ "--bg1":"#1a2238","--bg2":"#3a6073","--fg":"#e0f7fa","--accent":"#80deea" } }; let currentTheme = localStorage.getItem("greenMiracleTheme") || "light"; applyTheme(currentTheme); function applyTheme(name){ const vars = THEMES[name]; Object.keys(vars).forEach(k=>document.documentElement.style.setProperty(k,vars[k])); currentTheme = name; localStorage.setItem("greenMiracleTheme",name); } /* === THEME TOGGLER BUTTON ================================================ */ const themeBtn = document.createElement("button"); themeBtn.textContent = "๐ŸŒ—"; themeBtn.title = "Toggle theme"; Object.assign(themeBtn.style,{ position:"fixed",top:"1rem",right:"1rem",zIndex:999, background:"var(--accent)",color:"#fff",border:"none", borderRadius:"50%",width:"2.5rem",height:"2.5rem", fontSize:"1.2rem",cursor:"pointer",boxShadow:"2px 2px 8px rgba(0,0,0,.3)" }); themeBtn.onclick = ()=>{ const next = currentTheme==="light"?"dark":currentTheme==="dark"?"aurora":"light"; applyTheme(next); }; document.body.appendChild(themeBtn); /* === AMBIENT SOUND CONTROL =============================================== */ let audioCtx,ambientSource,ambientGain; const audioBtn=document.createElement("button"); audioBtn.textContent="๐Ÿ”Š"; audioBtn.title="Toggle ambient sound"; Object.assign(audioBtn.style,{ position:"fixed",top:"1rem",right:"4.2rem",zIndex:999, background:"var(--accent)",color:"#fff",border:"none", borderRadius:"50%",width:"2.5rem",height:"2.5rem", fontSize:"1.1rem",cursor:"pointer",boxShadow:"2px 2px 8px rgba(0,0,0,.3)" }); document.body.appendChild(audioBtn); let ambientOn=false; audioBtn.onclick=()=>{ if(!ambientOn){ startAmbient(); }else{ stopAmbient(); } }; function startAmbient(){ ambientOn=true;audioBtn.textContent="๐Ÿ”ˆ"; if(!audioCtx) audioCtx=new(window.AudioContext||window.webkitAudioContext)(); ambientGain=audioCtx.createGain();ambientGain.gain.value=0.05; ambientGain.connect(audioCtx.destination); // simple procedural noise const buffer=audioCtx.createBuffer(1,audioCtx.sampleRate*2,audioCtx.sampleRate); const data=buffer.getChannelData(0); for(let i=0;i<data.length;i++){data[i]=Math.random()*2-1;} ambientSource=audioCtx.createBufferSource(); ambientSource.buffer=buffer;ambientSource.loop=true; ambientSource.connect(ambientGain);ambientSource.start(0); } function stopAmbient(){ ambientOn=false;audioBtn.textContent="๐Ÿ”Š"; if(ambientSource){ambientSource.stop();ambientSource.disconnect();} } /* === FEEDBACK TOAST ====================================================== */ function toast(msg){ const t=document.createElement("div"); t.textContent=msg; Object.assign(t.style,{ position:"fixed",bottom:"2rem",left:"50%",transform:"translateX(-50%)", background:"var(--accent)",color:"#fff",padding:"0.6rem 1rem", borderRadius:"12px",boxShadow:"2px 2px 8px rgba(0,0,0,.2)", opacity:"0",transition:"opacity .3s ease",zIndex:1000 }); document.body.appendChild(t); setTimeout(()=>t.style.opacity="1",10); setTimeout(()=>{t.style.opacity="0";setTimeout(()=>t.remove(),500)},2500); } /* Example: notify on theme switch */ themeBtn.addEventListener("click",()=>toast(`Theme: ${currentTheme}`)); /* === KEYBOARD SHORTCUTS ================================================== */ window.addEventListener("keydown",e=>{ if(e.ctrlKey&&e.key.toLowerCase()==="l"){ // Ctrl+L clears memory clearMemory(); toast("๐Ÿง  Memory cleared"); } }); /* === ACCESSIBILITY HELP ================================================== */ document.body.setAttribute("aria-live","polite"); document.body.setAttribute("aria-label","Chat interface for Green Miracle 2085"); </script> <script> /* === SETTINGS PANEL ====================================================== */ const gearBtn = document.createElement("button"); gearBtn.textContent="โš™๏ธ"; gearBtn.title="Settings"; Object.assign(gearBtn.style,{ position:"fixed",top:"1rem",right:"7.4rem",zIndex:999, background:"var(--accent)",color:"#fff",border:"none", borderRadius:"50%",width:"2.5rem",height:"2.5rem", fontSize:"1.1rem",cursor:"pointer",boxShadow:"2px 2px 8px rgba(0,0,0,.3)" }); document.body.appendChild(gearBtn); const panel=document.createElement("div"); panel.id="settingsPanel"; Object.assign(panel.style,{ position:"fixed",top:"0",right:"-320px",width:"300px",height:"100%", background:"rgba(255,255,255,.95)",backdropFilter:"blur(8px)", boxShadow:"-4px 0 12px rgba(0,0,0,.2)",padding:"1rem", transition:"right .4s ease",zIndex:"998",overflowY:"auto" }); panel.innerHTML=` <h2 style="font-weight:700;font-size:1.3rem;margin-bottom:.5rem;color:var(--fg);">โš™๏ธ Settings</h2> <label class="block mt-2 font-semibold text-sm">Display Name</label> <input id="userNameInput" type="text" class="w-full border rounded p-2 mb-3" placeholder="Your name..."> <label class="block mt-2 font-semibold text-sm">Accent Brightness</label> <input id="accentSlider" type="range" min="50" max="150" value="100" class="w-full mb-3"> <button id="clearMemoryBtn" style="background:var(--accent);color:#fff;border:none;border-radius:8px;padding:.5rem 1rem;cursor:pointer;">๐Ÿง  Clear Chat Memory</button> <p class="text-xs mt-2 text-gray-600">Shortcut: Ctrl + L</p> <hr class="my-3"> <p style="font-size:.8rem;color:#475569;">Green Miracle 2085 HF v1.0 โ€” Local build. All data stored only in your browser.</p> `; document.body.appendChild(panel); let panelOpen=false; gearBtn.onclick=()=>{ panelOpen=!panelOpen; panel.style.right=panelOpen?"0":"-320px"; }; /* === SETTINGS FUNCTIONALITY ============================================= */ const userNameInput=document.getElementById("userNameInput"); const accentSlider=document.getElementById("accentSlider"); const clearMemoryBtn=document.getElementById("clearMemoryBtn"); userNameInput.value=localStorage.getItem("gmUserName")||""; accentSlider.value=localStorage.getItem("gmAccentScale")||"100"; /* Save user name + accent brightness */ userNameInput.addEventListener("input",()=>{ localStorage.setItem("gmUserName",userNameInput.value); toast("Name saved"); }); accentSlider.addEventListener("input",()=>{ const val=accentSlider.value; document.documentElement.style.setProperty("--accent", `hsl(160,100%,${val/1.5}%)`); localStorage.setItem("gmAccentScale",val); }); /* Clear chat memory button */ clearMemoryBtn.onclick=()=>{ clearMemory(); toast("Chat memory cleared ๐Ÿง "); }; /* Insert display name before user messages */ const oldBubble=bubble; bubble=function(text,role){ const b=oldBubble(text,role); if(role==="user"){ const name=localStorage.getItem("gmUserName")||"You"; const label=document.createElement("div"); label.textContent=name; label.style.fontSize=".7rem"; label.style.fontWeight="600"; label.style.color="var(--accent)"; label.style.marginBottom="-.4rem"; b.prepend(label); } return b; }; /* === FINAL UX POLISH ===================================================== */ window.addEventListener("focus",()=>document.title="๐ŸŒฟ Green Miracle 2085 โ€” Active"); window.addEventListener("blur",()=>document.title="๐ŸŒฟ Green Miracle 2085 โ€” Idle"); /* Smooth scroll for new messages */ const style=document.createElement("style"); style.textContent=`.bubble{transition:all .2s ease;}`; document.head.appendChild(style); /* Initial accent load */ const val=localStorage.getItem("gmAccentScale"); if(val)document.documentElement.style.setProperty("--accent",`hsl(160,100%,${val/1.5}%)`); </script> <!-- === END OF APP ======================================================== --> </body> </html>