AkurAI Build
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();
}