Irminsul / index.html
MukulRay's picture
feat: markdown rendering with gold-themed styling in UI
83c07da
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Irminsul β€” Genshin Impact AI Assistant</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600&family=EB+Garamond:ital,wght@0,400;0,500;1,400&family=JetBrains+Mono:wght@300;400&display=swap" rel="stylesheet">
<script src="https://cdnjs.cloudflare.com/ajax/libs/marked/9.1.6/marked.min.js"></script>
<style>
:root {
--bg: #060d08;
--bg2: #0a1410;
--surface: rgba(8,18,12,0.88);
--surface2: rgba(12,24,16,0.92);
--border: rgba(80,180,100,0.12);
--border2: rgba(80,180,100,0.22);
--dendro: #7ecb6a;
--dendro2: #a8e090;
--dendro3: #c8f0b0;
--parchment: #c8b888;
--parchment2:#e0d0a8;
--teal: #4ab890;
--text: #d8e8d0;
--text2: #7a9878;
--text3: #3a5038;
--green: #5dd68c;
--red: #e87878;
--amber: #d8a84a;
--gold: #d4af37;
--mono: 'JetBrains Mono', monospace;
--serif: 'EB Garamond', serif;
--display: 'Cinzel', serif;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--bg);
color: var(--text);
font-family: var(--serif);
min-height: 100vh;
display: flex;
flex-direction: column;
overflow-x: hidden;
}
canvas#bg { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
/* deep forest atmospheric glow */
.atmo {
position: fixed;
border-radius: 50%;
pointer-events: none;
z-index: 0;
filter: blur(80px);
}
.atmo-1 {
width: 700px; height: 500px;
top: -100px; left: -100px;
background: radial-gradient(ellipse, rgba(40,100,50,0.18) 0%, transparent 70%);
}
.atmo-2 {
width: 500px; height: 400px;
top: 30%; right: -80px;
background: radial-gradient(ellipse, rgba(60,160,80,0.1) 0%, transparent 70%);
}
.atmo-3 {
width: 600px; height: 300px;
bottom: 0; left: 20%;
background: radial-gradient(ellipse, rgba(30,80,40,0.14) 0%, transparent 70%);
}
header {
position: relative; z-index: 10;
padding: 20px 48px;
display: flex;
align-items: center;
justify-content: space-between;
border-bottom: 1px solid var(--border);
backdrop-filter: blur(20px);
background: rgba(6,13,8,0.7);
}
.logo { display: flex; align-items: center; gap: 14px; }
.logo-mark {
width: 38px; height: 38px;
border-radius: 50%;
border: 1px solid rgba(126,203,106,0.3);
background: radial-gradient(circle at 40% 35%, rgba(80,180,80,0.15), rgba(6,13,8,0.95));
display: flex; align-items: center; justify-content: center;
font-size: 18px;
box-shadow: 0 0 16px rgba(80,200,80,0.12), inset 0 0 12px rgba(80,200,80,0.06);
animation: runeGlow 3s ease-in-out infinite;
}
@keyframes runeGlow {
0%,100% { box-shadow: 0 0 16px rgba(80,200,80,0.12), inset 0 0 12px rgba(80,200,80,0.06); }
50% { box-shadow: 0 0 28px rgba(80,200,80,0.25), inset 0 0 16px rgba(80,200,80,0.12); }
}
.logo-text {
font-family: var(--display);
font-size: 17px; font-weight: 500;
color: var(--dendro2);
letter-spacing: 0.08em;
}
.logo-sub {
font-size: 10px; color: var(--text3);
font-family: var(--mono);
margin-top: 2px; letter-spacing: 0.04em;
}
.status-pill {
display: flex; align-items: center; gap: 7px;
padding: 5px 13px; border-radius: 20px;
border: 1px solid var(--border);
background: rgba(8,18,12,0.8);
font-family: var(--mono); font-size: 11px; color: var(--text2);
backdrop-filter: blur(8px);
}
.status-dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--text3); transition: background 0.3s;
}
.status-dot.online { background: var(--green); box-shadow: 0 0 7px var(--green); }
.status-dot.error { background: var(--red); }
.status-dot.loading { background: var(--amber); animation: pulse 1s ease-in-out infinite; }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:0.3} }
main {
position: relative; z-index: 10;
flex: 1; display: flex; flex-direction: column;
max-width: 800px; width: 100%;
margin: 0 auto; padding: 52px 40px 0;
}
/* parchment scroll hero */
.hero {
margin-bottom: 44px;
text-align: center;
animation: fadeUp 0.7s ease both;
}
@keyframes fadeUp {
from { opacity:0; transform: translateY(14px); }
to { opacity:1; transform: translateY(0); }
}
.rune-row {
display: flex; align-items: center; justify-content: center;
gap: 12px; margin-bottom: 18px;
font-size: 14px; color: var(--dendro);
opacity: 0.6; letter-spacing: 0.3em;
}
.rune-row span { animation: runeFlicker 4s ease-in-out infinite; }
.rune-row span:nth-child(2) { animation-delay: 0.8s; }
.rune-row span:nth-child(3) { animation-delay: 1.6s; }
@keyframes runeFlicker {
0%,100%{opacity:0.5} 50%{opacity:1;text-shadow:0 0 8px var(--dendro);}
}
.hero h1 {
font-family: var(--display);
font-size: 40px; font-weight: 500;
color: var(--parchment2);
letter-spacing: 0.06em; line-height: 1.2;
margin-bottom: 8px;
text-shadow: 0 0 60px rgba(100,200,80,0.15);
}
.hero h1 em {
font-style: normal;
color: var(--dendro2);
text-shadow: 0 0 30px rgba(126,203,106,0.5);
}
.hero-sub {
font-size: 16px; font-style: italic;
color: var(--text2); line-height: 1.75;
max-width: 480px; margin: 0 auto 20px;
}
.model-tag {
display: inline-flex; align-items: center; gap: 8px;
padding: 5px 16px; border-radius: 3px;
border: 1px solid rgba(126,203,106,0.2);
background: rgba(40,80,30,0.2);
font-family: var(--mono); font-size: 11px;
color: var(--dendro); letter-spacing: 0.04em;
}
/* ornamental divider */
.ornament {
display: flex; align-items: center;
gap: 12px; margin-bottom: 36px; opacity: 0.25;
}
.ornament-line {
flex: 1; height: 1px;
background: linear-gradient(90deg, transparent, var(--dendro), transparent);
}
.ornament-glyph {
font-size: 12px; color: var(--dendro);
letter-spacing: 4px;
}
/* query box β€” parchment card feel */
.query-box {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 4px;
overflow: hidden;
transition: border-color 0.3s, box-shadow 0.3s;
animation: fadeUp 0.7s 0.1s ease both;
backdrop-filter: blur(20px);
position: relative;
}
/* corner rune accents */
.query-box::before,
.query-box::after {
content: '✦';
position: absolute;
font-size: 10px;
color: var(--dendro);
opacity: 0.3;
top: 8px;
}
.query-box::before { left: 10px; }
.query-box::after { right: 10px; }
.query-box:focus-within {
border-color: rgba(126,203,106,0.35);
box-shadow: 0 0 32px rgba(80,180,60,0.08);
}
.query-label {
padding: 14px 18px 0;
font-family: var(--display);
font-size: 9px; color: var(--text3);
letter-spacing: 0.18em; text-transform: uppercase;
}
textarea {
width: 100%; background: transparent;
border: none; outline: none; resize: none;
padding: 10px 18px 16px;
font-family: var(--serif); font-size: 15px;
color: var(--text); line-height: 1.75;
min-height: 100px; caret-color: var(--dendro);
}
textarea::placeholder { color: var(--text3); font-style: italic; }
.query-footer {
display: flex; align-items: center;
justify-content: space-between;
padding: 10px 16px;
border-top: 1px solid var(--border);
background: var(--surface2);
}
.top-k-wrap {
display: flex; align-items: center;
gap: 8px; font-family: var(--mono);
font-size: 11px; color: var(--text2);
}
.top-k-wrap select {
background: rgba(6,13,8,0.8);
border: 1px solid var(--border2);
border-radius: 3px; color: var(--text);
font-family: var(--mono); font-size: 11px;
padding: 3px 8px; cursor: pointer; outline: none;
}
.send-btn {
display: flex; align-items: center; gap: 8px;
padding: 9px 22px;
background: linear-gradient(135deg, rgba(60,140,50,0.8), rgba(40,100,35,0.9));
border: 1px solid rgba(126,203,106,0.3);
border-radius: 3px;
color: var(--dendro3);
font-family: var(--display);
font-size: 11px; font-weight: 500;
cursor: pointer;
transition: all 0.2s;
letter-spacing: 0.1em;
box-shadow: 0 2px 20px rgba(60,180,50,0.15);
}
.send-btn:hover {
background: linear-gradient(135deg, rgba(80,160,60,0.9), rgba(55,120,45,0.95));
box-shadow: 0 4px 28px rgba(60,200,60,0.25);
border-color: rgba(126,203,106,0.5);
transform: translateY(-1px);
}
.send-btn:active { transform: scale(0.97); }
.send-btn:disabled {
background: rgba(20,35,20,0.7);
color: var(--text3); cursor: not-allowed;
transform: none; box-shadow: none;
border-color: var(--border);
}
#response-area {
margin-top: 28px;
animation: fadeUp 0.5s ease both;
display: none;
}
#response-area.visible { display: block; }
.response-card {
background: var(--surface);
border: 1px solid var(--border);
border-radius: 4px; overflow: hidden;
backdrop-filter: blur(20px);
}
.response-header {
display: flex; align-items: center;
justify-content: space-between;
padding: 11px 18px;
border-bottom: 1px solid var(--border);
background: var(--surface2);
}
.response-label {
font-family: var(--display); font-size: 9px;
color: var(--text3); text-transform: uppercase;
letter-spacing: 0.14em;
display: flex; align-items: center; gap: 8px;
}
.response-label .dot {
width: 6px; height: 6px; border-radius: 50%;
background: var(--dendro);
box-shadow: 0 0 8px var(--dendro);
animation: dendroFlicker 2s ease-in-out infinite;
}
@keyframes dendroFlicker {
0%,100%{opacity:1;box-shadow:0 0 8px var(--dendro);}
50%{opacity:0.6;box-shadow:0 0 4px var(--dendro);}
}
.latency-tag {
font-family: var(--mono); font-size: 11px;
color: var(--text3); padding: 2px 8px;
border-radius: 2px; border: 1px solid var(--border);
}
.response-body {
padding: 22px 24px;
font-size: 15.5px; line-height: 1.9;
color: var(--text); font-family: var(--serif);
white-space: normal; word-break: break-word;
}
.response-body > *:first-child { margin-top: 0; }
/* ── Markdown answer styling ─────────────────────────── */
.response-body h1,
.response-body h2 {
font-family: var(--display);
color: var(--gold);
letter-spacing: 0.04em;
margin: 1.4em 0 0.4em;
font-weight: 500;
}
.response-body h2 { font-size: 1.15em; border-bottom: 1px solid rgba(212,175,55,0.25); padding-bottom: 0.3em; }
.response-body h3 { font-family: var(--serif); color: var(--gold); opacity: 0.85; font-size: 1em; margin: 1.1em 0 0.25em; font-style: italic; }
.response-body strong { color: var(--gold); font-weight: 600; }
.response-body em { color: var(--text); opacity: 0.8; font-style: italic; }
.response-body ul, .response-body ol {
padding-left: 1.4em;
margin: 0.5em 0 0.8em;
}
.response-body li {
margin-bottom: 0.35em;
line-height: 1.75;
}
.response-body li::marker { color: var(--gold); opacity: 0.7; }
.response-body p { margin: 0 0 0.8em; }
.response-body p:last-child { margin-bottom: 0; }
.response-body hr {
border: none;
border-top: 1px solid rgba(212,175,55,0.2);
margin: 1.2em 0;
}
.response-body code {
font-family: var(--mono);
font-size: 0.88em;
background: rgba(212,175,55,0.08);
border: 1px solid rgba(212,175,55,0.2);
border-radius: 3px;
padding: 0.1em 0.4em;
color: var(--gold);
}
.thinking {
display: flex; align-items: center; gap: 12px;
padding: 22px 24px;
font-family: var(--serif); font-size: 14px;
color: var(--text3); font-style: italic;
}
.thinking-dots span {
display: inline-block;
width: 5px; height: 5px; border-radius: 50%;
background: var(--dendro); margin: 0 2px;
animation: dendroOrb 1.6s ease-in-out infinite;
opacity: 0.4;
}
.thinking-dots span:nth-child(2) { animation-delay: 0.25s; }
.thinking-dots span:nth-child(3) { animation-delay: 0.5s; }
@keyframes dendroOrb {
0%,100%{opacity:0.3;transform:scale(0.9);}
50%{opacity:1;transform:scale(1.3);box-shadow:0 0 6px var(--dendro);}
}
.sources-section {
border-top: 1px solid var(--border);
padding: 12px 22px;
background: var(--surface2);
}
.sources-label {
font-family: var(--display); font-size: 9px;
color: var(--text3); text-transform: uppercase;
letter-spacing: 0.14em; margin-bottom: 8px;
}
.source-chip {
display: inline-flex; align-items: center; gap: 5px;
padding: 3px 10px; border-radius: 2px;
border: 1px solid rgba(80,180,80,0.18);
background: rgba(40,80,30,0.15);
font-family: var(--mono); font-size: 11px;
color: var(--dendro); margin: 3px 4px 3px 0;
}
.no-sources {
font-family: var(--mono); font-size: 11px;
color: var(--text3); font-style: italic;
}
.error-card {
background: rgba(232,120,120,0.05);
border: 1px solid rgba(232,120,120,0.18);
border-radius: 4px; padding: 14px 18px;
font-family: var(--mono); font-size: 12px;
color: var(--red); margin-top: 16px; display: none;
}
.error-card.visible { display: block; }
.history-section {
margin-top: 32px; padding-bottom: 48px;
animation: fadeUp 0.5s ease both;
}
.history-label {
font-family: var(--display); font-size: 9px;
color: var(--text3); text-transform: uppercase;
letter-spacing: 0.14em; margin-bottom: 12px;
}
.history-item {
background: var(--surface); border: 1px solid var(--border);
border-radius: 3px; padding: 11px 16px;
margin-bottom: 6px; cursor: pointer;
transition: border-color 0.2s, background 0.2s;
backdrop-filter: blur(8px);
}
.history-item:hover {
border-color: rgba(80,180,80,0.28);
background: rgba(30,60,25,0.3);
}
.history-q {
font-size: 13.5px; color: var(--text2);
font-family: var(--serif);
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.history-meta {
font-size: 10px; color: var(--text3);
font-family: var(--mono); margin-top: 4px;
}
footer {
position: relative; z-index: 10;
text-align: center; padding: 18px;
font-family: var(--mono); font-size: 10px;
color: var(--text3); letter-spacing: 0.06em;
border-top: 1px solid var(--border);
backdrop-filter: blur(8px);
background: rgba(6,13,8,0.6);
margin-top: auto;
}
footer span { color: var(--dendro); opacity: 0.6; }
</style>
</head>
<body>
<canvas id="bg"></canvas>
<div class="atmo atmo-1"></div>
<div class="atmo atmo-2"></div>
<div class="atmo atmo-3"></div>
<header>
<div class="logo">
<div class="logo-mark">αͺ₯</div>
<div>
<div class="logo-text">Irminsul</div>
<div class="logo-sub">Llama 3.1 Β· QLoRA Β· Pinecone RAG</div>
</div>
</div>
<div class="status-pill">
<div class="status-dot" id="status-dot"></div>
<span id="status-text">checking...</span>
</div>
</header>
<main>
<div class="hero">
<div class="rune-row">
<span>αͺ₯</span><span>✦</span><span>αͺ₯</span>
</div>
<h1>The <em>Akasha</em> Speaks</h1>
<p class="hero-sub">All knowledge flows through the tree. Ask of Teyvat's lore, its people, their battles, and the elements that bind this world.</p>
<div class="model-tag">
β—ˆ &nbsp; Dendro-augmented retrieval Β· Genshin Impact corpus
</div>
</div>
<div class="ornament">
<div class="ornament-line"></div>
<div class="ornament-glyph">✦ αͺ₯ ✦</div>
<div class="ornament-line"></div>
</div>
<div class="query-box">
<div class="query-label">Inscribe your query</div>
<textarea
id="query-input"
placeholder="What does the Irminsul record of Nahida's imprisonment reveal..."
rows="3"
></textarea>
<div class="query-footer">
<div class="top-k-wrap">
<span>top_k</span>
<select id="top-k">
<option value="1">1</option>
<option value="2">2</option>
<option value="3" selected>3</option>
<option value="5">5</option>
</select>
<span style="color:var(--text3)">retrieved branches</span>
</div>
<button class="send-btn" id="send-btn" onclick="submitQuery()">
Query the Tree β†’
</button>
</div>
</div>
<div class="error-card" id="error-card"></div>
<div id="response-area">
<div class="response-card">
<div class="response-header">
<div class="response-label">
<div class="dot"></div>
irminsul responds
</div>
<div class="latency-tag" id="latency-tag">β€”</div>
</div>
<div id="response-body" class="response-body"></div>
<div class="sources-section">
<div class="sources-label">branches consulted</div>
<div id="sources-list"><span class="no-sources">no records ingested</span></div>
</div>
</div>
</div>
<div class="history-section" id="history-section" style="display:none">
<div class="history-label">Recent queries</div>
<div id="history-list"></div>
</div>
</main>
<footer>
Irminsul &nbsp;Β·&nbsp; <span>Genshin Impact AI Assistant</span> &nbsp;Β·&nbsp; exp2_lr2e-4_r16 + Groq
</footer>
<script>
// ── Floating spore particles + Dendro wisps ────────────────────────────────────
(function() {
const canvas = document.getElementById('bg');
const ctx = canvas.getContext('2d');
let W, H, particles = [], wisps = [];
function resize() {
W = canvas.width = window.innerWidth;
H = canvas.height = window.innerHeight;
}
function rand(a, b) { return a + Math.random() * (b - a); }
function initParticles() {
particles = [];
const n = Math.floor(W * H / 8000);
for (let i = 0; i < n; i++) {
particles.push({
x: rand(0, W), y: rand(0, H),
r: rand(0.8, 2.5),
vx: rand(-0.15, 0.15),
vy: rand(-0.4, -0.1),
alpha: rand(0.1, 0.5),
flicker: rand(0.002, 0.006),
phase: rand(0, Math.PI * 2),
hue: rand(100, 150),
});
}
}
function initWisps() {
wisps = [];
for (let i = 0; i < 6; i++) {
wisps.push({
x: rand(0, W), y: rand(H * 0.3, H * 0.9),
r: rand(60, 140),
alpha: rand(0.02, 0.06),
speed: rand(0.0003, 0.0008),
phase: rand(0, Math.PI * 2),
hue: rand(110, 145),
});
}
}
function draw(t) {
ctx.clearRect(0, 0, W, H);
// Dendro wisps β€” soft glowing orbs drifting in the background
wisps.forEach(w => {
const a = w.alpha * (0.6 + 0.4 * Math.sin(t * w.speed * 1000 + w.phase));
const grd = ctx.createRadialGradient(w.x, w.y, 0, w.x, w.y, w.r);
grd.addColorStop(0, `hsla(${w.hue},70%,55%,${a})`);
grd.addColorStop(1, `hsla(${w.hue},70%,40%,0)`);
ctx.beginPath();
ctx.arc(w.x, w.y, w.r, 0, Math.PI * 2);
ctx.fillStyle = grd;
ctx.fill();
w.x += Math.sin(t * 0.0003 + w.phase) * 0.3;
w.y += Math.cos(t * 0.0002 + w.phase) * 0.2;
});
// spore particles β€” tiny glowing motes floating upward
particles.forEach(p => {
const a = p.alpha * (0.5 + 0.5 * Math.sin(t * p.flicker * 1000 + p.phase));
ctx.beginPath();
ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${p.hue},80%,70%,${a})`;
ctx.fill();
// tiny glow ring on larger motes
if (p.r > 1.8) {
ctx.beginPath();
ctx.arc(p.x, p.y, p.r * 2.5, 0, Math.PI * 2);
ctx.fillStyle = `hsla(${p.hue},80%,70%,${a * 0.15})`;
ctx.fill();
}
p.x += p.vx + Math.sin(t * 0.0005 + p.phase) * 0.2;
p.y += p.vy;
if (p.y < -10) { p.y = H + 10; p.x = rand(0, W); }
if (p.x < -10) p.x = W + 10;
if (p.x > W + 10) p.x = -10;
});
requestAnimationFrame(draw);
}
window.addEventListener('resize', () => { resize(); initParticles(); initWisps(); });
resize(); initParticles(); initWisps();
requestAnimationFrame(draw);
})();
// ── API ────────────────────────────────────────────────────────────────────────
const API = window.location.origin;;
const history = [];
async function checkHealth() {
const dot = document.getElementById('status-dot');
const txt = document.getElementById('status-text');
dot.className = 'status-dot loading';
txt.textContent = 'connecting...';
try {
const r = await fetch(`${API}/health`);
const d = await r.json();
if (d.model_loaded) {
dot.className = 'status-dot online';
txt.textContent = 'akasha linked';
} else {
dot.className = 'status-dot loading';
txt.textContent = 'loading...';
setTimeout(checkHealth, 3000);
}
} catch {
dot.className = 'status-dot error';
txt.textContent = 'offline';
setTimeout(checkHealth, 5000);
}
}
async function submitQuery() {
const query = document.getElementById('query-input').value.trim();
if (!query) return;
const top_k = parseInt(document.getElementById('top-k').value);
const btn = document.getElementById('send-btn');
const respArea = document.getElementById('response-area');
const respBody = document.getElementById('response-body');
const errCard = document.getElementById('error-card');
const latTag = document.getElementById('latency-tag');
const srcList = document.getElementById('sources-list');
errCard.className = 'error-card';
respArea.style.display = 'block';
respArea.className = 'response-area visible';
latTag.textContent = 'β€”';
srcList.innerHTML = '';
btn.disabled = true;
respBody.innerHTML = `
<div class="thinking">
<div class="thinking-dots"><span></span><span></span><span></span></div>
searching the branches...
</div>`;
try {
const res = await fetch(`${API}/generate`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, top_k })
});
if (!res.ok) {
const err = await res.json();
throw new Error(JSON.stringify(err.detail || err));
}
const data = await res.json();
respBody.innerHTML = marked.parse(data.answer);
const ms = Math.round(data.latency_ms);
latTag.textContent = ms >= 1000 ? `${(ms/1000).toFixed(1)}s` : `${ms}ms`;
if (data.sources && data.sources.length > 0) {
srcList.innerHTML = data.sources.map(s => {
const name = s.split(/[\\/]/).pop();
return `<span class="source-chip">β—ˆ ${name}</span>`;
}).join('');
} else {
srcList.innerHTML = '<span class="no-sources">no records found β€” run ingest.py</span>';
}
addToHistory(query, ms);
} catch (err) {
respBody.innerHTML = '';
respArea.style.display = 'none';
errCard.className = 'error-card visible';
errCard.textContent = `Error: ${err.message}`;
}
btn.disabled = false;
}
function addToHistory(query, latency_ms) {
history.unshift({ query, latency_ms, time: new Date().toLocaleTimeString() });
if (history.length > 5) history.pop();
document.getElementById('history-section').style.display = 'block';
document.getElementById('history-list').innerHTML = history.map((h, i) => `
<div class="history-item" onclick="rerun(${i})">
<div class="history-q">${h.query}</div>
<div class="history-meta">${h.time} Β· ${(h.latency_ms / 1000).toFixed(1)}s</div>
</div>
`).join('');
}
function rerun(i) {
document.getElementById('query-input').value = history[i].query;
submitQuery();
}
document.getElementById('query-input').addEventListener('keydown', e => {
if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
submitQuery();
}
});
checkHealth();
</script>
</body>
</html>