Investment Plans workspace
Open raw ↗
/* Reference client — plain JS, no frameworks, no secrets ever present here.
   The API key lives only in the server's environment (GOV-B7.3). */
"use strict";

const $ = (id) => document.getElementById(id);

async function api(path, body) {
  const res = await fetch(path, {
    method: body === undefined ? "GET" : "POST",
    headers: body === undefined ? {} : {"Content-Type": "application/json"},
    body: body === undefined ? undefined : JSON.stringify(body),
    credentials: "same-origin",
  });
  const data = await res.json().catch(() => ({}));
  return {ok: res.ok, status: res.status, data};
}

function show(view) {
  $("login-view").hidden = view !== "login";
  $("chat-view").hidden = view !== "chat";
}

function bubble(kind, text) {
  const li = document.createElement("li");
  li.className = kind;
  li.textContent = text;               // textContent: engine output is data, never HTML (INV-6 spirit)
  $("thread").appendChild(li);
  $("thread").scrollTop = $("thread").scrollHeight;
}

async function boot() {
  const {data} = await api("/api/whoami");
  if (data && data.user) {
    if (data.engine) $("engine-badge").textContent = "engine: " + data.engine;
    show("chat");
  } else {
    show("login");
  }
}

$("login-form").addEventListener("submit", async (ev) => {
  ev.preventDefault();
  $("login-error").hidden = true;
  const {ok, data} = await api("/api/login", {
    username: $("username").value.trim(),
    password: $("password").value,
  });
  if (ok) { location.reload(); }
  else { $("login-error").textContent = "Sign-in failed."; $("login-error").hidden = false; }
});

$("logout").addEventListener("click", async () => { await api("/api/logout", {}); location.reload(); });

$("chat-form").addEventListener("submit", async (ev) => {
  ev.preventDefault();
  const text = $("message").value.trim();
  if (!text) return;
  $("chat-error").hidden = true;
  bubble("me", text);
  $("message").value = "";
  const {ok, data} = await api("/api/chat", {message: text});
  if (ok) bubble("engine", data.reply || "(empty reply)");
  else { $("chat-error").textContent = "Engine error — see the server console."; $("chat-error").hidden = false; }
});

boot();