summaryrefslogtreecommitdiff
path: root/static/js/chat.js
diff options
context:
space:
mode:
authorElizabeth Alexander Hunt <me@liz.coffee>2026-06-20 09:40:07 -0700
committerElizabeth Alexander Hunt <me@liz.coffee>2026-06-20 09:40:07 -0700
commitfb653292612eddca5b088ed88466c546d1597107 (patch)
tree900ed32e066812688858ed3bb15128c41bb78054 /static/js/chat.js
downloadpenguins.lan-fb653292612eddca5b088ed88466c546d1597107.tar.gz
penguins.lan-fb653292612eddca5b088ed88466c546d1597107.zip
Initial commit
Diffstat (limited to 'static/js/chat.js')
-rw-r--r--static/js/chat.js101
1 files changed, 101 insertions, 0 deletions
diff --git a/static/js/chat.js b/static/js/chat.js
new file mode 100644
index 0000000..69c169c
--- /dev/null
+++ b/static/js/chat.js
@@ -0,0 +1,101 @@
+(function () {
+ const log = document.getElementById("log");
+ const form = document.getElementById("chat-form");
+ const input = document.getElementById("msg");
+ const status = document.getElementById("status");
+ const me = document.getElementById("me").textContent.trim();
+
+ let socket;
+ let reconnectDelay = 500;
+
+ function el(tag, className, text) {
+ const node = document.createElement(tag);
+ if (className) node.className = className;
+ if (text !== undefined) node.textContent = text;
+ return node;
+ }
+
+ function atBottom() {
+ return log.scrollHeight - log.scrollTop - log.clientHeight < 40;
+ }
+
+ function append(node) {
+ const stick = atBottom();
+ log.appendChild(node);
+ if (stick) log.scrollTop = log.scrollHeight;
+ }
+
+ function fmtTime(ms) {
+ if (!ms) return "";
+ const d = new Date(ms);
+ return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
+ }
+
+ function renderChat(msg) {
+ const line = el("div", "chat-line");
+ if (msg.nick === me) line.classList.add("mine");
+ line.appendChild(el("span", "chat-nick", msg.nick));
+ line.appendChild(el("span", "chat-body", " " + msg.body));
+ line.appendChild(el("span", "chat-time", " " + fmtTime(msg.at)));
+ append(line);
+ }
+
+ function renderSystem(msg) {
+ append(el("div", "chat-system", msg.body));
+ }
+
+ function handle(msg) {
+ switch (msg.type) {
+ case "history":
+ log.innerHTML = "";
+ (msg.history || []).forEach(renderChat);
+ break;
+ case "chat":
+ renderChat(msg);
+ break;
+ case "system":
+ renderSystem(msg);
+ break;
+ }
+ }
+
+ function connect() {
+ const proto = location.protocol === "https:" ? "wss" : "ws";
+ socket = new WebSocket(`${proto}://${location.host}/ws`);
+
+ socket.onopen = function () {
+ status.textContent = "connected ✓";
+ status.className = "status ok";
+ reconnectDelay = 500;
+ };
+
+ socket.onmessage = function (event) {
+ try {
+ handle(JSON.parse(event.data));
+ } catch (e) {
+ console.error("bad message", e);
+ }
+ };
+
+ socket.onclose = function () {
+ status.textContent = "reconnecting…";
+ status.className = "status";
+ setTimeout(connect, reconnectDelay);
+ reconnectDelay = Math.min(reconnectDelay * 2, 8000);
+ };
+
+ socket.onerror = function () {
+ socket.close();
+ };
+ }
+
+ form.addEventListener("submit", function (e) {
+ e.preventDefault();
+ const body = input.value.trim();
+ if (!body || !socket || socket.readyState !== WebSocket.OPEN) return;
+ socket.send(JSON.stringify({ type: "chat", body: body }));
+ input.value = "";
+ });
+
+ connect();
+})();