Wea / index.html
luguog's picture
Update index.html
64b334e 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>