examples/live-notes Public
// The editor page: a textarea bound to one note over a WebSocket. The note id comes from the// URL fragment, so /#groceries edits the note "groceries".import { Session, moveCaret } from "./session.ts";import type { ServerMessage } from "../src/protocol.ts";const NOTE_ID = /^[A-Za-z0-9_-]{1,64}$/;const area = document.querySelector<HTMLTextAreaElement>("#note")!;const status = document.querySelector<HTMLElement>("#status")!;const title = document.querySelector<HTMLElement>("#title")!;function noteId(): string { const id = decodeURIComponent(location.hash.slice(1)); return NOTE_ID.test(id) ? id : "scratch";}let socket: WebSocket | null = null;let retry = 0;function connect(): void { const id = noteId(); title.textContent = id; document.title = `${id} · live-notes`; area.disabled = true; status.textContent = "Connecting"; const scheme = location.protocol === "https:" ? "wss" : "ws"; const ws = new WebSocket(`${scheme}://${location.host}/notes/${encodeURIComponent(id)}/ws`); const session = new Session((msg) => ws.send(JSON.stringify(msg))); socket = ws; ws.addEventListener("message", (event) => { const msg = JSON.parse(String(event.data)) as ServerMessage; const { selectionStart, selectionEnd } = area; const applied = session.receive(msg); if (applied === "reset") { area.value = session.text; area.disabled = false; area.focus(); retry = 0; status.textContent = `Version ${session.version}`; } else if (applied) { area.value = session.text; area.setSelectionRange(moveCaret(selectionStart, applied), moveCaret(selectionEnd, applied)); } if (msg.type !== "snapshot") status.textContent = session.pending ? "Saving" : `Version ${session.version}`; }); area.oninput = () => { session.edit(area.value); status.textContent = "Saving"; }; ws.addEventListener("close", () => { if (socket !== ws) return; area.disabled = true; // Back off to at most 10 seconds between attempts. const wait = Math.min(10_000, 500 * 2 ** retry++); status.textContent = "Offline, reconnecting"; setTimeout(connect, wait); });}window.addEventListener("hashchange", () => { const old = socket; socket = null; old?.close(); connect();});connect();