This commit is contained in:
Ionel Andrei Cataon
2026-02-18 16:30:27 +02:00
parent 64e4138e16
commit 40a0d44803

View File

@@ -3,181 +3,175 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NutriAI Elite - Professional Coaching</title> <title>NutriAI Elite - High Contrast Pro</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet"> <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
<style> <style>
:root { --primary: #3b82f6; --bg: #0f172a; --card: #1e293b; --text: #f8fafc; } :root {
body.light-theme { --bg: #f8fafc; --card: #ffffff; --text: #0f172a; } --primary: #3b82f6;
body.blue-theme { --bg: #1e3a8a; --card: #1e40af; --text: #ffffff; } --bg: #020617; /* Deeper black for maximum contrast */
--card: #1e293b;
--text-bright: #ffffff; /* pure white for readability */
--text-dim: #94a3b8;
}
body { background-color: var(--bg); color: var(--text); font-family: 'Outfit', sans-serif; transition: 0.4s; overflow-x: hidden; } body.light-theme { --bg: #f8fafc; --card: #ffffff; --text-bright: #0f172a; --text-dim: #475569; }
.sidebar { background: var(--card); border-right: 1px solid rgba(255,255,255,0.1); min-height: 100vh; padding: 2rem; position: relative; }
.glass-input { background: rgba(0,0,0,0.2) !important; border: 1px solid rgba(255,255,255,0.1) !important; color: var(--text) !important; border-radius: 12px; }
.kcal-hub { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); border-radius: 20px; padding: 2rem; text-align: center; box-shadow: 0 10px 25px rgba(37, 99, 235, 0.4); } body {
.chat-area { height: 60vh; overflow-y: auto; padding: 20px; border-radius: 20px; background: rgba(0,0,0,0.2); scroll-behavior: smooth; } background-color: var(--bg);
color: var(--text-bright);
font-family: 'Outfit', sans-serif;
transition: 0.4s;
}
.msg { padding: 1.2rem; border-radius: 1.2rem; margin-bottom: 1.2rem; max-width: 85%; font-size: 1.05rem; line-height: 1.6; animation: fadeIn 0.3s ease; } /* Improved Sidebar Visibility */
.ai { background: var(--card); border-left: 5px solid var(--primary); } .sidebar {
.user { background: #10b981; margin-left: auto; color: white; } background: var(--card);
border-right: 1px solid rgba(255,255,255,0.1);
min-height: 100vh;
padding: 2rem;
}
.top-controls { position: fixed; top: 20px; right: 20px; z-index: 1000; display: flex; gap: 10px; } .glass-input {
.input-bar { background: var(--card); border-radius: 50px; padding: 8px 25px; border: 2px solid rgba(255,255,255,0.1); } background: rgba(15, 23, 42, 0.6) !important;
@keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } border: 2px solid #334155 !important;
color: #ffffff !important; /* Forces input text to be bright white */
font-weight: 500;
}
/* Professional AI Response Cards */
.chat-area {
height: 65vh;
overflow-y: auto;
padding: 25px;
background: rgba(0,0,0,0.3);
border-radius: 24px;
}
.msg {
padding: 1.8rem;
border-radius: 20px;
margin-bottom: 2rem;
max-width: 90%;
font-size: 1.15rem; /* Larger font */
line-height: 1.8;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.3);
}
.ai {
background: #1e293b;
border: 1px solid #334155;
color: #ffffff !important;
}
.ai table {
width: 100%;
margin: 15px 0;
border-collapse: collapse;
background: rgba(0,0,0,0.2);
border-radius: 10px;
overflow: hidden;
}
.ai th, .ai td { padding: 12px; border: 1px solid #475569; text-align: left; }
.ai th { background: var(--primary); color: white; }
.user { background: var(--primary); margin-left: auto; color: white; font-weight: 600; }
.kcal-hub {
background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%);
padding: 2rem; border-radius: 20px;
border: 2px solid rgba(255,255,255,0.1);
}
.input-bar {
background: #1e293b;
border-radius: 15px;
padding: 12px 25px;
border: 2px solid #3b82f6;
}
#userInput { color: white !important; font-size: 1.2rem; }
</style> </style>
</head> </head>
<body> <body>
<div class="top-controls"> <div class="top-controls p-3 d-flex justify-content-end gap-3 position-fixed w-100" style="z-index: 10;">
<div class="btn-group shadow" role="group"> <div class="btn-group shadow-lg">
<input type="radio" class="btn-check" name="units" id="metric" checked onchange="toggleUnits()"> <input type="radio" class="btn-check" name="units" id="metric" checked onchange="toggleUnits()">
<label class="btn btn-outline-primary rounded-start-pill px-3" for="metric">Metric</label> <label class="btn btn-primary px-4" for="metric">Metric</label>
<input type="radio" class="btn-check" name="units" id="imperial" onchange="toggleUnits()"> <input type="radio" class="btn-check" name="units" id="imperial" onchange="toggleUnits()">
<label class="btn btn-outline-primary rounded-end-pill px-3" for="imperial">Imperial</label> <label class="btn btn-primary px-4" for="imperial">Imperial</label>
</div> </div>
<button class="btn btn-outline-light rounded-pill" onclick="setTheme('light')"><i class="fas fa-sun"></i></button>
<button class="btn btn-dark rounded-circle shadow p-3" data-bs-toggle="dropdown" style="width: 50px; height: 50px;"> <button class="btn btn-outline-light rounded-pill" onclick="setTheme('dark')"><i class="fas fa-moon"></i></button>
<i class="fas fa-palette"></i>
</button>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item" href="#" onclick="setTheme('dark')">Dark Mode</a></li>
<li><a class="dropdown-item" href="#" onclick="setTheme('light')">Light Mode</a></li>
<li><a class="dropdown-item" href="#" onclick="setTheme('blue')">Ocean Blue</a></li>
</ul>
</div> </div>
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<div class="col-lg-3 sidebar shadow"> <div class="col-lg-3 sidebar">
<h3 class="fw-bold mb-4 text-primary"><i class="fas fa-bolt me-2"></i>NutriAI Elite</h3> <h2 class="fw-extrabold mb-5"><i class="fas fa-shield-alt text-primary me-2"></i>NutriAI <span class="text-primary">Pro</span></h2>
<div class="row g-3 mb-4"> <div class="row g-3 mb-4">
<div class="col-6"> <div class="col-6">
<label class="small opacity-75">Age</label> <label class="small fw-bold text-dim">Age</label>
<input type="number" id="age" class="form-control glass-input" value="30"> <input type="number" id="age" class="form-control glass-input" value="30">
</div> </div>
<div class="col-6"> <div class="col-6">
<label class="small opacity-75">Sex</label> <label class="small fw-bold text-dim">Sex</label>
<select id="sex" class="form-select glass-input"> <select id="sex" class="form-select glass-input">
<option value="male">Male</option> <option value="male">Male</option>
<option value="female">Female</option> <option value="female">Female</option>
</select> </select>
</div> </div>
<div class="col-6"> <div class="col-6">
<label class="small opacity-75" id="labelHeight">Height (cm)</label> <label class="small fw-bold text-dim" id="labelHeight">Height (cm)</label>
<input type="number" id="height" class="form-control glass-input" value="180"> <input type="number" id="height" class="form-control glass-input" value="180">
</div> </div>
<div class="col-6"> <div class="col-6">
<label class="small opacity-75" id="labelWeight">Weight (kg)</label> <label class="small fw-bold text-dim" id="labelWeight">Weight (kg)</label>
<input type="number" id="weight" class="form-control glass-input" value="85"> <input type="number" id="weight" class="form-control glass-input" value="85">
</div> </div>
<div class="col-12">
<label class="small opacity-75">Activity Level</label>
<select id="activity" class="form-select glass-input">
<option value="1.2">Sedentary (Office)</option>
<option value="1.375">Lightly Active (1-2 days sport)</option>
<option value="1.55">Moderately Active (3-5 days sport)</option>
<option value="1.725">Very Active (Daily/Physical work)</option>
</select>
</div>
</div> </div>
<div class="mb-4"> <div class="kcal-hub mb-4">
<label class="fw-bold d-flex justify-content-between"> <h4 class="small text-uppercase fw-bold opacity-75">Daily Target</h4>
Goal Offset <span><span id="objLabel">0</span> kcal</span> <h1 class="display-4 fw-bold mb-0" id="totalKcal">2500</h1>
</label> <p class="mb-0">Calories</p>
<input type="range" class="form-range" id="kcalAdjust" min="-1000" max="1000" step="50" value="0">
</div> </div>
<div class="kcal-hub shadow-lg"> <button onclick="updateProfile()" class="btn btn-primary w-100 py-3 fw-bold rounded-pill shadow-lg">
<small class="text-uppercase opacity-75">Daily Target</small> <i class="fas fa-sync me-2"></i>UPDATE PROFILE
<h1 class="fw-bold display-5 mb-0" id="totalKcal">0</h1>
<span class="small fw-bold">CALORIES / DAY</span>
</div>
<button onclick="updateProfile()" class="btn btn-primary w-100 mt-4 py-3 fw-bold rounded-pill shadow">
<i class="fas fa-calculator me-2"></i>RECALCULATE
</button> </button>
</div> </div>
<div class="col-lg-9 p-5"> <div class="col-lg-9 p-5 mt-5">
<div class="mb-4"> <div id="chatBox" class="chat-area">
<h2 class="fw-bold mb-1">AI Nutrition Expert</h2>
<p class="text-muted">High-precision meal plans powered by Llama 3.3</p>
</div>
<div id="chatBox" class="chat-area shadow-sm">
<div class="msg ai"> <div class="msg ai">
<h5 class="fw-bold">System Online.</h5> <h4 class="fw-bold mb-3">Welcome to Elite Coaching</h4>
Hello! I'm your elite AI nutrition coach. I've analyzed your biometrics. <br> Your biometric profile has been synced. I am ready to generate <strong>Professional Meal Plans</strong> or <strong>Macro Breakdowns</strong> using high-precision calculations.
<strong>What should we focus on?</strong> I can generate weekly meal plans, shopping lists, or specialized macros.
</div> </div>
</div> </div>
<div class="input-bar d-flex align-items-center mt-4 shadow-lg"> <div class="input-bar d-flex align-items-center mt-4">
<input type="text" id="userInput" class="form-control bg-transparent border-0 text-white" <input type="text" id="userInput" class="form-control bg-transparent border-0"
placeholder="Message your AI coach (Press ENTER)..." style="font-size: 1.1rem;"> placeholder="Ask for a 7-day meal plan table...">
<button onclick="sendToGroq()" class="btn btn-primary rounded-circle p-2 ms-2" style="width: 50px; height: 50px;"> <button onclick="sendToGroq()" class="btn btn-primary rounded-pill px-4 ms-2 py-2">
<i class="fas fa-arrow-up"></i> SEND <i class="fas fa-paper-plane ms-2"></i>
</button> </button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script> <script>
const GROQ_API_KEY = "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"; const GROQ_API_KEY = "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";
let currentKcal = 0;
let isMetric = true; let isMetric = true;
function setTheme(theme) { document.body.className = theme + '-theme'; } function setTheme(t) { document.body.className = t + '-theme'; }
function toggleUnits() {
isMetric = document.getElementById('metric').checked;
const h = document.getElementById('height');
const w = document.getElementById('weight');
if (isMetric) {
h.value = Math.round(h.value * 2.54);
w.value = Math.round(w.value / 2.205);
document.getElementById('labelHeight').innerText = "Height (cm)";
document.getElementById('labelWeight').innerText = "Weight (kg)";
} else {
h.value = Math.round(h.value / 2.54);
w.value = Math.round(w.value * 2.205);
document.getElementById('labelHeight').innerText = "Height (in)";
document.getElementById('labelWeight').innerText = "Weight (lb)";
}
updateProfile();
}
document.getElementById('userInput').addEventListener('keypress', (e) => { if(e.key === 'Enter') sendToGroq(); }); document.getElementById('userInput').addEventListener('keypress', (e) => { if(e.key === 'Enter') sendToGroq(); });
function updateProfile() {
const age = parseFloat(document.getElementById('age').value);
const sex = document.getElementById('sex').value;
const act = parseFloat(document.getElementById('activity').value);
const adj = parseInt(document.getElementById('kcalAdjust').value);
let h = parseFloat(document.getElementById('height').value);
let w = parseFloat(document.getElementById('weight').value);
// Convert to metric if needed for BMR formula
if (!isMetric) {
h = h * 2.54;
w = w / 2.205;
}
let bmr = (sex === 'male') ? (10 * w) + (6.25 * h) - (5 * age) + 5 : (10 * w) + (6.25 * h) - (5 * age) - 161;
currentKcal = Math.round((bmr * act) + adj);
document.getElementById('totalKcal').innerText = currentKcal;
document.getElementById('objLabel').innerText = (adj > 0 ? '+' : '') + adj;
}
async function sendToGroq() { async function sendToGroq() {
const input = document.getElementById('userInput'); const input = document.getElementById('userInput');
const text = input.value.trim(); const text = input.value.trim();
@@ -186,24 +180,30 @@
addMsg('user', text); addMsg('user', text);
input.value = ""; input.value = "";
const loadId = 'load-' + Date.now(); const loadId = 'load-' + Date.now();
addMsg('ai', '<span class="spinner-grow spinner-grow-sm text-primary"></span> Computing...', loadId); addMsg('ai', '<div class="spinner-border spinner-border-sm text-primary"></div> AI is generating your professional report...', loadId);
try { try {
const unitSystem = isMetric ? "Metric (grams, cm, kg)" : "Imperial (ounces, lbs, inches)"; const systemPrompt = `You are a World-Class Nutritionist.
User Target: ${document.getElementById('totalKcal').innerText} kcal.
Units: ${isMetric ? 'Metric' : 'Imperial'}.
FORMATTING RULES:
1. Use Markdown TABLES for all meal plans.
2. Use BOLD headers for sections.
3. Include a "Macro Breakdown" section for every day.
4. Be concise but professional. Respond in English only.`;
const resp = await fetch("https://api.groq.com/openai/v1/chat/completions", { const resp = await fetch("https://api.groq.com/openai/v1/chat/completions", {
method: "POST", headers: { "Authorization": `Bearer ${GROQ_API_KEY}`, "Content-Type": "application/json" }, method: "POST", headers: { "Authorization": `Bearer ${GROQ_API_KEY}`, "Content-Type": "application/json" },
body: JSON.stringify({ body: JSON.stringify({
model: "llama-3.3-70b-versatile", model: "llama-3.3-70b-versatile",
messages: [{ messages: [{ role: "system", content: systemPrompt }, { role: "user", content: text }]
role: "system",
content: `Elite Nutritionist AI. Profile: ${currentKcal} kcal. Unit System: ${unitSystem}.
Respond strictly in ENGLISH. Use Markdown. Be precise with measurements.`
}, { role: "user", content: text }]
}) })
}); });
const data = await resp.json(); const data = await resp.json();
document.getElementById(loadId).innerHTML = `<div style="white-space: pre-wrap">${data.choices[0].message.content}</div>`; // Using marked.js or simple formatting
} catch(e) { document.getElementById(loadId).innerText = "Connection error!"; } let aiText = data.choices[0].message.content.replace(/\n/g, '<br>');
document.getElementById(loadId).innerHTML = `<div>${aiText}</div>`;
} catch(e) { document.getElementById(loadId).innerText = "Error!"; }
} }
function addMsg(type, content, id = null) { function addMsg(type, content, id = null) {
@@ -212,6 +212,21 @@
b.scrollTop = b.scrollHeight; b.scrollTop = b.scrollHeight;
} }
function toggleUnits() {
isMetric = document.getElementById('metric').checked;
updateProfile();
}
function updateProfile() {
const age = parseFloat(document.getElementById('age').value);
const h = parseFloat(document.getElementById('height').value);
const w = parseFloat(document.getElementById('weight').value);
const sex = document.getElementById('sex').value;
// Simple BMR
let bmr = (sex === 'male') ? (10 * w) + (6.25 * h) - (5 * age) + 5 : (10 * w) + (6.25 * h) - (5 * age) - 161;
document.getElementById('totalKcal').innerText = Math.round(bmr * 1.5);
}
updateProfile(); updateProfile();
</script> </script>
</body> </body>