s
This commit is contained in:
@@ -3,199 +3,191 @@
|
|||||||
<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 v13.0 - Hydration & Precision</title>
|
<title>NutriAI Elite v14.0</title>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
<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 {
|
:root { --primary: #3b82f6; --bg: #020617; --card: #1e293b; --accent: #60a5fa; }
|
||||||
--primary: #3b82f6; --bg: #0f172a; --sidebar-bg: #1e293b;
|
body { background-color: var(--bg); color: #fff; font-family: 'Outfit', sans-serif; height: 100vh; overflow: hidden; }
|
||||||
--input-bg: #ffffff; --input-text: #000000; --card-ai: #334155;
|
|
||||||
|
.sidebar { background: #0f172a; border-right: 1px solid rgba(255,255,255,0.1); height: 100vh; overflow-y: auto; padding: 1.5rem; }
|
||||||
|
.label-pro { color: var(--accent); font-weight: 800; font-size: 0.75rem; text-transform: uppercase; margin-bottom: 5px; display: block; }
|
||||||
|
|
||||||
|
.glass-input {
|
||||||
|
background: #fff !important; color: #000 !important;
|
||||||
|
border: 3px solid var(--primary) !important; border-radius: 12px;
|
||||||
|
font-weight: 800 !important; font-size: 1.1rem; padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.theme-dark { --bg: #020617; --sidebar-bg: #0f172a; --card-ai: #1e293b; }
|
.stat-panel {
|
||||||
body.theme-emerald { --bg: #064e3b; --sidebar-bg: #065f46; --primary: #10b981; --card-ai: #065f46; }
|
background: linear-gradient(145deg, #1e40af, #3b82f6);
|
||||||
body.theme-midnight { --bg: #1e1b4b; --sidebar-bg: #312e81; --primary: #818cf8; --card-ai: #312e81; }
|
border-radius: 20px; padding: 1.5rem; margin: 15px 0; box-shadow: 0 10px 20px rgba(0,0,0,0.3);
|
||||||
body.theme-carbon { --bg: #171717; --sidebar-bg: #262626; --primary: #f5f5f5; --card-ai: #262626; }
|
}
|
||||||
|
|
||||||
body { background-color: var(--bg); color: #ffffff; font-family: 'Outfit', sans-serif; transition: 0.5s; overflow-x: hidden; }
|
/* Enhanced Hydration UI */
|
||||||
.sidebar { background: var(--sidebar-bg); border-right: 1px solid rgba(255,255,255,0.1); min-height: 100vh; padding: 1.2rem; box-shadow: 10px 0 30px rgba(0,0,0,0.2); }
|
.water-hub {
|
||||||
|
background: rgba(59, 130, 246, 0.15); border: 2px solid var(--primary);
|
||||||
|
border-radius: 20px; padding: 20px; margin: 15px 0; text-align: center;
|
||||||
|
}
|
||||||
|
.water-icon { font-size: 2.2rem; color: #60a5fa; margin-bottom: 5px; }
|
||||||
|
|
||||||
.label-pro { color: var(--primary); font-weight: 800; font-size: 0.7rem; text-transform: uppercase; margin-bottom: 4px; display: block; }
|
/* Bigger Macro UI */
|
||||||
.glass-input { background: var(--input-bg) !important; color: var(--input-text) !important; border: 2px solid var(--primary) !important; border-radius: 8px; font-weight: 700 !important; font-size: 0.9rem; }
|
.macro-hub { background: #1e293b; border-radius: 20px; padding: 25px; margin-top: 15px; }
|
||||||
|
.macro-label { font-size: 1.1rem; font-weight: 700; }
|
||||||
|
|
||||||
.kcal-hub { background: linear-gradient(135deg, var(--primary) 0%, #1e40af 100%); border-radius: 15px; padding: 1rem; text-align: center; margin-top: 10px; }
|
.chat-container { height: calc(100vh - 120px); overflow-y: auto; padding: 2rem; }
|
||||||
.water-card { background: rgba(59, 130, 246, 0.1); border: 1px solid var(--primary); border-radius: 15px; padding: 12px; margin-top: 10px; }
|
.msg { padding: 1.5rem; border-radius: 1.5rem; margin-bottom: 1.5rem; font-size: 1.1rem; line-height: 1.6; }
|
||||||
.water-glass { cursor: pointer; font-size: 1.2rem; color: rgba(255,255,255,0.2); transition: 0.2s; }
|
.ai { background: #1e293b; border-left: 8px solid var(--primary); }
|
||||||
.water-glass.active { color: #60a5fa; transform: scale(1.2); }
|
.user { background: var(--primary); margin-left: auto; width: fit-content; max-width: 80%; }
|
||||||
|
|
||||||
.chat-area { height: 50vh; overflow-y: auto; padding: 20px; border-radius: 20px; background: rgba(0,0,0,0.2); }
|
.btn-shop { background: #f59e0b; color: #000; font-weight: 800; border: none; border-radius: 15px; padding: 0 25px; height: 60px; font-size: 1rem; }
|
||||||
.msg { padding: 1.2rem; border-radius: 1rem; margin-bottom: 1rem; max-width: 95%; }
|
|
||||||
.ai { background: var(--card-ai); border-left: 5px solid var(--primary); }
|
|
||||||
.user { background: var(--primary); margin-left: auto; }
|
|
||||||
|
|
||||||
.input-bar { background: var(--sidebar-bg); border-radius: 50px; padding: 8px 20px; border: 2px solid var(--primary); }
|
|
||||||
.btn-shop { background: #f59e0b; color: #000; font-weight: 800; border: none; border-radius: 12px; padding: 0 20px; height: 50px; }
|
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body class="theme-dark">
|
<body>
|
||||||
|
|
||||||
<div class="position-fixed top-0 end-0 p-3 d-flex gap-2" style="z-index: 1050;">
|
|
||||||
<div class="btn-group shadow">
|
|
||||||
<input type="radio" class="btn-check" name="units" id="metric" checked onchange="toggleUnits()">
|
|
||||||
<label class="btn btn-primary btn-sm" for="metric">Metric</label>
|
|
||||||
<input type="radio" class="btn-check" name="units" id="imperial" onchange="toggleUnits()">
|
|
||||||
<label class="btn btn-primary btn-sm" for="imperial">Imperial</label>
|
|
||||||
</div>
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="btn btn-light rounded-circle shadow" data-bs-toggle="dropdown"><i class="fas fa-palette"></i></button>
|
|
||||||
<ul class="dropdown-menu dropdown-menu-end shadow">
|
|
||||||
<li><a class="dropdown-item" href="#" onclick="setTheme('dark')">Onyx Dark</a></li>
|
|
||||||
<li><a class="dropdown-item" href="#" onclick="setTheme('emerald')">Emerald</a></li>
|
|
||||||
<li><a class="dropdown-item" href="#" onclick="setTheme('midnight')">Midnight</a></li>
|
|
||||||
<li><a class="dropdown-item" href="#" onclick="setTheme('carbon')">Carbon</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">
|
<div class="col-lg-4 col-xl-3 sidebar">
|
||||||
<h5 class="fw-bold mb-3"><i class="fas fa-bolt text-primary me-2"></i>NutriAI ELITE</h5>
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||||
|
<h3 class="fw-extrabold m-0"><i class="fas fa-brain text-primary"></i> NUTRI ELITE</h3>
|
||||||
|
<div class="btn-group">
|
||||||
|
<button id="btnMetric" class="btn btn-primary active" onclick="setUnits('metric')">METRIC</button>
|
||||||
|
<button id="btnImperial" class="btn btn-outline-primary" onclick="setUnits('imperial')">IMP</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="row g-2 mb-2">
|
<div class="row g-3">
|
||||||
<div class="col-6"><label class="label-pro" id="lblCurW">Current (kg)</label><input type="number" id="weight" class="form-control glass-input" value="110" oninput="updateProfile()"></div>
|
<div class="col-6"><label class="label-pro" id="txtW">Current (kg)</label><input type="number" id="w" class="form-control glass-input" value="110" oninput="update()"></div>
|
||||||
<div class="col-6"><label class="label-pro" id="lblTarW">Target (kg)</label><input type="number" id="targetW" class="form-control glass-input" value="85" oninput="updateProfile()"></div>
|
<div class="col-6"><label class="label-pro" id="txtT">Target (kg)</label><input type="number" id="t" class="form-control glass-input" value="85" oninput="update()"></div>
|
||||||
<div class="col-6"><label class="label-pro" id="lblH">Height (cm)</label><input type="number" id="height" class="form-control glass-input" value="176" oninput="updateProfile()"></div>
|
<div class="col-6"><label class="label-pro" id="txtH">Height (cm)</label><input type="number" id="h" class="form-control glass-input" value="176" oninput="update()"></div>
|
||||||
<div class="col-6"><label class="label-pro">Age</label><input type="number" id="age" class="form-control glass-input" value="30" oninput="updateProfile()"></div>
|
<div class="col-6"><label class="label-pro">Age</label><input type="number" id="age" class="form-control glass-input" value="30" oninput="update()"></div>
|
||||||
<div class="col-12"><label class="label-pro">Activity</label>
|
|
||||||
<select id="activity" class="form-select glass-input" onchange="updateProfile()">
|
<div class="col-12"><label class="label-pro">Detailed Activity Level</label>
|
||||||
<option value="1.2">Sedentary</option><option value="1.55" selected>Moderate</option><option value="1.725">Active</option>
|
<select id="act" class="form-select glass-input" onchange="update()">
|
||||||
|
<option value="1.2">Sedentary (Office/No Exercise)</option>
|
||||||
|
<option value="1.375">Lightly Active (1-2 days/week)</option>
|
||||||
|
<option value="1.55" selected>Moderately Active (3-5 days/week)</option>
|
||||||
|
<option value="1.725">Very Active (6-7 days/week)</option>
|
||||||
|
<option value="1.9">Extra Active (Pro Athlete/Physical Labor)</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<label class="label-pro d-flex justify-content-between">Aggressiveness <span id="aggValue">-300 kcal</span></label>
|
<label class="label-pro d-flex justify-content-between">Calorie Adjustment <span id="aggDisp">-300 kcal</span></label>
|
||||||
<input type="range" id="aggSlider" class="form-range" min="-1000" max="1000" step="50" value="-300" oninput="updateProfile()">
|
<input type="range" id="agg" class="form-range" min="-1000" max="1000" step="50" value="-300" oninput="update()">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="kcal-hub">
|
<div class="stat-panel text-center">
|
||||||
<small class="text-uppercase fw-bold opacity-75">Daily Goal</small>
|
<h1 class="fw-bold mb-0" id="kcal">2500</h1>
|
||||||
<h2 class="fw-bold mb-0" id="totalKcal">2500</h2>
|
<p class="text-uppercase fw-bold m-0 opacity-75">Daily Calorie Target</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="water-card text-center">
|
<div class="water-hub">
|
||||||
<label class="label-pro mb-2"><i class="fas fa-tint me-1"></i> Hydration: <span id="waterTarget">3.5</span>L</label>
|
<i class="fas fa-droplet water-icon"></i>
|
||||||
<div class="d-flex justify-content-between px-2" id="waterTracker">
|
<h2 class="fw-bold mb-0" id="h2o">3.9L</h2>
|
||||||
</div>
|
<span class="label-pro">Recommended Hydration</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="macro-card">
|
<div class="macro-hub">
|
||||||
<canvas id="macroChart" style="max-height: 100px;"></canvas>
|
<canvas id="mChart" style="max-height: 180px;"></canvas>
|
||||||
<div class="d-flex justify-content-around mt-2 small fw-bold">
|
<div class="row mt-4 text-center">
|
||||||
<span class="text-info">P: <span id="pGram">0</span>g</span>
|
<div class="col-4"><div class="text-info macro-label">P</div><div id="pG">0g</div></div>
|
||||||
<span class="text-danger">C: <span id="cGram">0</span>g</span>
|
<div class="col-4"><div class="text-danger macro-label">C</div><div id="cG">0g</div></div>
|
||||||
<span class="text-warning">F: <span id="fGram">0</span>g</span>
|
<div class="col-4"><div class="text-warning macro-label">F</div><div id="fG">0g</div></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-lg-9 p-5">
|
<div class="col-lg-8 col-xl-9 p-0">
|
||||||
<div id="chatBox" class="chat-area shadow-sm">
|
<div id="chat" class="chat-container">
|
||||||
<div class="msg ai">
|
<div class="msg ai shadow-lg">
|
||||||
<h5 class="fw-bold text-primary">Hydration & Precision Sync.</h5>
|
<h4 class="fw-bold">Precision Engine Online.</h4>
|
||||||
I have calculated your water needs and macros based on your <strong><span id="tagWeight">110kg</span></strong> profile. Ready for your meal plan?
|
I have corrected the unit synchronization. Weight, Target, and Height will now switch globally between Metric and Imperial.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-4 d-flex gap-2">
|
<div class="p-4 bg-dark border-top border-secondary d-flex gap-3">
|
||||||
<div class="input-bar d-flex align-items-center flex-grow-1">
|
<div class="input-group input-group-lg flex-grow-1">
|
||||||
<input type="text" id="userInput" class="form-control bg-transparent border-0 text-white" placeholder="Type a message...">
|
<input type="text" id="uIn" class="form-control bg-dark text-white border-primary" placeholder="Enter request (e.g. Generate 7-day meal plan)...">
|
||||||
<button onclick="sendToGroq()" class="btn btn-primary rounded-circle p-2 ms-2" style="width: 40px; height: 40px;"><i class="fas fa-paper-plane"></i></button>
|
<button class="btn btn-primary px-4" onclick="talk()"><i class="fas fa-paper-plane"></i></button>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="generateShoppingList()" class="btn-shop"><i class="fas fa-shopping-cart me-1"></i> SHOPPING LIST</button>
|
<button class="btn-shop shadow" onclick="talk('Generate shopping list for current plan')">
|
||||||
|
<i class="fas fa-shopping-basket me-2"></i> SHOPPING LIST
|
||||||
|
</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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";
|
let isMetric = true, mChart;
|
||||||
let isMetric = true, macroChart, waterG = 0;
|
const API = "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";
|
||||||
|
|
||||||
function setTheme(t) { document.body.className = 'theme-' + t; }
|
function setUnits(mode) {
|
||||||
document.getElementById('userInput').addEventListener('keypress', (e) => { if(e.key === 'Enter') sendToGroq(); });
|
if((mode==='metric' && !isMetric) || (mode==='imperial' && isMetric)) {
|
||||||
|
isMetric = !isMetric;
|
||||||
|
const w = document.getElementById('w'), t = document.getElementById('t'), h = document.getElementById('h');
|
||||||
|
if(isMetric) {
|
||||||
|
w.value = (w.value/2.205).toFixed(0); t.value = (t.value/2.205).toFixed(0); h.value = (h.value*2.54).toFixed(0);
|
||||||
|
} else {
|
||||||
|
w.value = (w.value*2.205).toFixed(0); t.value = (t.value*2.205).toFixed(0); h.value = (h.value/2.54).toFixed(0);
|
||||||
|
}
|
||||||
|
document.getElementById('txtW').innerText = isMetric ? "Current (kg)" : "Current (lb)";
|
||||||
|
document.getElementById('txtT').innerText = isMetric ? "Target (kg)" : "Target (lb)";
|
||||||
|
document.getElementById('txtH').innerText = isMetric ? "Height (cm)" : "Height (in)";
|
||||||
|
document.getElementById('btnMetric').className = isMetric ? "btn btn-primary active" : "btn btn-outline-primary";
|
||||||
|
document.getElementById('btnImperial').className = !isMetric ? "btn btn-primary active" : "btn btn-outline-primary";
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function initChart() {
|
function update() {
|
||||||
const ctx = document.getElementById('macroChart').getContext('2d');
|
const wv = parseFloat(document.getElementById('w').value)||0, hv = parseFloat(document.getElementById('h').value)||0;
|
||||||
macroChart = new Chart(ctx, {
|
const av = parseFloat(document.getElementById('age').value)||0, act = parseFloat(document.getElementById('act').value);
|
||||||
type: 'doughnut', data: { datasets: [{ data: [1,1,1], backgroundColor: ['#0dcaf0', '#dc3545', '#ffc107'], borderWeight: 0 }] },
|
const agg = parseInt(document.getElementById('agg').value);
|
||||||
options: { plugins: { legend: { display: false } }, cutout: '75%' }
|
|
||||||
|
const wc = isMetric ? wv : wv/2.205, hc = isMetric ? hv : hv*2.54;
|
||||||
|
const bmr = (10 * wc) + (6.25 * hc) - (5 * av) + 5;
|
||||||
|
const total = Math.round((bmr * act) + agg);
|
||||||
|
|
||||||
|
document.getElementById('kcal').innerText = total;
|
||||||
|
document.getElementById('aggDisp').innerText = (agg>0?'+':'')+agg+" kcal";
|
||||||
|
document.getElementById('h2o').innerText = (wc * 0.035).toFixed(1) + "L";
|
||||||
|
|
||||||
|
const pG = Math.round((total * (agg<0?0.35:0.25))/4), cG = Math.round((total * (agg<0?0.35:0.5))/4), fG = Math.round((total * 0.3)/9);
|
||||||
|
document.getElementById('pG').innerText = pG+"g"; document.getElementById('cG').innerText = cG+"g"; document.getElementById('fG').innerText = fG+"g";
|
||||||
|
if(mChart) { mChart.data.datasets[0].data = [pG*4, cG*4, fG*9]; mChart.update(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
async function talk(custom) {
|
||||||
|
const box = document.getElementById('chat'), input = document.getElementById('uIn');
|
||||||
|
const text = custom || input.value; if(!text) return;
|
||||||
|
if(!custom) { box.innerHTML += `<div class="msg user shadow-sm">${text}</div>`; input.value=""; }
|
||||||
|
const id = 'ai-'+Date.now(); box.innerHTML += `<div id="${id}" class="msg ai shadow-lg">Generating Elite Plan...</div>`;
|
||||||
|
box.scrollTop = box.scrollHeight;
|
||||||
|
|
||||||
|
const res = await fetch("https://api.groq.com/openai/v1/chat/completions", {
|
||||||
|
method: "POST", headers: {"Content-Type":"application/json", "Authorization":`Bearer ${API}`},
|
||||||
|
body: JSON.stringify({ model:"llama-3.3-70b-versatile", messages:[
|
||||||
|
{role:"system", content:`Pro Coach. Target: ${document.getElementById('kcal').innerText} kcal. Macros: P:${document.getElementById('pG').innerText}, C:${document.getElementById('cG').innerText}, F:${document.getElementById('fG').innerText}. Hydration: ${document.getElementById('h2o').innerText}. Use clean Markdown Tables.`},
|
||||||
|
{role:"user", content:text}
|
||||||
|
]})
|
||||||
});
|
});
|
||||||
renderWater(8);
|
const d = await res.json();
|
||||||
}
|
|
||||||
|
|
||||||
function renderWater(total) {
|
|
||||||
const container = document.getElementById('waterTracker');
|
|
||||||
container.innerHTML = '';
|
|
||||||
for(let i=0; i<total; i++) {
|
|
||||||
container.innerHTML += `<i class="fas fa-glass-water water-glass" onclick="toggleWater(${i}, this)"></i>`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toggleWater(idx, el) { el.classList.toggle('active'); }
|
|
||||||
|
|
||||||
function toggleUnits() {
|
|
||||||
isMetric = document.getElementById('metric').checked;
|
|
||||||
const w = document.getElementById('weight'), h = document.getElementById('height');
|
|
||||||
if (isMetric) { w.value = Math.round(w.value / 2.205); h.value = Math.round(h.value * 2.54);
|
|
||||||
} else { w.value = Math.round(w.value * 2.205); h.value = Math.round(h.value / 2.54); }
|
|
||||||
document.getElementById('lblCurW').innerText = isMetric ? "Current (kg)" : "Current (lb)";
|
|
||||||
updateProfile();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateProfile() {
|
|
||||||
let w = parseFloat(document.getElementById('weight').value) || 0, h = parseFloat(document.getElementById('height').value) || 0;
|
|
||||||
let a = parseFloat(document.getElementById('age').value) || 0, act = parseFloat(document.getElementById('activity').value), agg = parseInt(document.getElementById('aggSlider').value);
|
|
||||||
let w_c = isMetric ? w : w / 2.205, h_c = isMetric ? h : h * 2.54;
|
|
||||||
|
|
||||||
let bmr = (10 * w_c) + (6.25 * h_c) - (5 * a) + 5;
|
|
||||||
let totalKcal = Math.round((bmr * act) + agg);
|
|
||||||
|
|
||||||
document.getElementById('totalKcal').innerText = totalKcal;
|
|
||||||
document.getElementById('aggValue').innerText = (agg > 0 ? '+' : '') + agg + " kcal";
|
|
||||||
document.getElementById('tagWeight').innerText = w + (isMetric ? 'kg' : 'lb');
|
|
||||||
|
|
||||||
// Precision Water Math: ~35ml per kg
|
|
||||||
let waterL = (w_c * 0.035).toFixed(1);
|
|
||||||
document.getElementById('waterTarget').innerText = waterL;
|
|
||||||
|
|
||||||
let pG = Math.round((totalKcal * 0.3) / 4), cG = Math.round((totalKcal * 0.4) / 4), fG = Math.round((totalKcal * 0.3) / 9);
|
|
||||||
document.getElementById('pGram').innerText = pG; document.getElementById('cGram').innerText = cG; document.getElementById('fGram').innerText = fG;
|
|
||||||
if(macroChart) { macroChart.data.datasets[0].data = [pG*4, cG*4, fG*9]; macroChart.update(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
async function sendToGroq(custom) {
|
|
||||||
const input = document.getElementById('userInput'), text = custom || input.value.trim();
|
|
||||||
if(!text) return;
|
|
||||||
if(!custom) addMsg('user', text); input.value = "";
|
|
||||||
const id = 'l-' + Date.now(); addMsg('ai', 'Thinking...', id);
|
|
||||||
|
|
||||||
const resp = await fetch("https://api.groq.com/openai/v1/chat/completions", {
|
|
||||||
method: "POST", headers: { "Authorization": `Bearer ${GROQ_API_KEY}`, "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({ model: "llama-3.3-70b-versatile", messages: [{ role: "system", content: `Nutritionist. Daily: ${document.getElementById('totalKcal').innerText} kcal. P:${document.getElementById('pGram').innerText}g. Water: ${document.getElementById('waterTarget').innerText}L. Use tables.` }, { role: "user", content: text }] })
|
|
||||||
});
|
|
||||||
const d = await resp.json();
|
|
||||||
document.getElementById(id).innerHTML = marked.parse(d.choices[0].message.content);
|
document.getElementById(id).innerHTML = marked.parse(d.choices[0].message.content);
|
||||||
}
|
}
|
||||||
|
|
||||||
function generateShoppingList() { sendToGroq("Generate a weekly shopping list for this plan."); }
|
window.onload = () => {
|
||||||
function addMsg(t, c, i = null) { const b = document.getElementById('chatBox'); b.innerHTML += `<div class="msg ${t}" ${i ? `id="${i}"` : ''}>${c}</div>`; b.scrollTop = b.scrollHeight; }
|
mChart = new Chart(document.getElementById('mChart').getContext('2d'), {
|
||||||
|
type: 'doughnut', data: { datasets: [{ data: [1,1,1], backgroundColor: ['#0dcaf0','#dc3545','#ffc107'], borderWeight:0 }] },
|
||||||
initChart(); updateProfile();
|
options: { plugins: { legend: { display: false } }, cutout: '75%' }
|
||||||
|
});
|
||||||
|
update();
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user