Menu
Public repository / 01843dea338a2aaeff9b2715a27a3f46d50bc083
AkurAI-Framework / site/frontend/app.js
Latest change 01843dea338a2aaeff9b2715a27a3f46d50bc083 - feat(site): interactive /realtime demo page (live WS echo + SSE counter) by Ólafur Búi Ólafsson
// Native ES module — no bundler. Pings the framework's health endpoint.
const el = document.getElementById("health");
if (el) {
fetch("/api/health", { headers: { accept: "application/json" } })
.then((r) => (r.ok ? r.json() : Promise.reject(new Error(`status ${r.status}`))))
.then((d) => {
el.textContent = `server up · ${d.framework} ${d.version}`;
el.classList.add("ok");
})
.catch((e) => (el.textContent = `server unreachable: ${e.message}`));
}
// Realtime demo — only runs on /realtime, where the panels exist.
if (document.getElementById("sse-count")) initRealtime();
function initRealtime() {
startSse();
startWs();
}
// Server-Sent Events: native EventSource consuming /api/events.
function startSse() {
const count = document.getElementById("sse-count");
const log = document.getElementById("sse-log");
const status = document.getElementById("sse-status");
const es = new EventSource("/api/events");
es.addEventListener("open", () => setStatus(status, true, "connected"));
es.addEventListener("error", () => setStatus(status, false, "reconnecting…"));
es.addEventListener("tick", (e) => {
count.textContent = e.data;
logLine(log, `tick → ${e.data}`);
});
}
// WebSocket: native WebSocket against /api/ws, echoing what you send.
function startWs() {
const log = document.getElementById("ws-log");
const status = document.getElementById("ws-status");
const form = document.getElementById("ws-form");
const input = document.getElementById("ws-input");
const scheme = location.protocol === "https:" ? "wss" : "ws";
const ws = new WebSocket(`${scheme}://${location.host}/api/ws`);
ws.addEventListener("open", () => setStatus(status, true, "connected"));
ws.addEventListener("close", () => setStatus(status, false, "closed"));
ws.addEventListener("error", () => setStatus(status, false, "error"));
ws.addEventListener("message", (e) => logLine(log, `echo → ${e.data}`, "in"));
form.addEventListener("submit", (event) => {
event.preventDefault();
const msg = input.value.trim();
if (!msg || ws.readyState !== WebSocket.OPEN) return;
ws.send(msg);
logLine(log, `sent → ${msg}`, "out");
input.value = "";
});
}
function setStatus(el, ok, text) {
el.textContent = text;
el.classList.toggle("ok", ok);
}
// Prepend a line to a log list, capping its length so it never grows forever.
function logLine(el, text, kind) {
const li = document.createElement("li");
li.textContent = text;
if (kind) li.className = `rt-${kind}`;
el.prepend(li);
while (el.children.length > 8) el.lastChild.remove();
}