
beyond-text
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of loading-spinner-words branch: v4View latest version
A minimal realtime chat app built on Val Town. Messages are stored in Val Town SQLite and streamed to every client over Server-Sent Events. A QR code of the app's own URL is shown in the sidebar so people can scan to join.
- 💬 Realtime chat via Server-Sent Events (no WebSockets needed)
- 🗄️ Messages stored in Val Town SQLite
- 📱 QR code of the site URL for easy joining
- 👤 Username saved to
localStorage(prompted on first visit) - 🧘 No auto-scroll, no auto-focus on the input
Rendering mermaid diagram...
index.ts ← Hono backend: static + /api routes + SSE stream
frontend/
index.html ← HTML shell (loads Twind + React)
index.tsx ← React entrypoint (mounts <App />)
favicon.svg ← App icon
components/
App.tsx ← Username gating (localStorage)
UsernamePrompt.tsx ← First-visit username form
Chat.tsx ← Messages list, input, sidebar
QRCode.tsx ← Renders a QR of the site URL
GET /— serves the React appPOST /api/messages—{ username, text }→ stores a message in SQLiteGET /api/messages?sinceId=N— fetch messages newer thanNGET /api/stream?sinceId=N— SSE stream of new messages (polls SQLite every 1.5s; emitshello,message, andpingevents)
- SSE instead of WebSockets: Val Town doesn't accept incoming WebSocket connections, so the server polls SQLite on a 1.5s loop and pushes new rows to each subscribed client as SSE events.
- No auto-scroll / auto-focus: The messages pane scrolls only when the user scrolls, and the input is never programmatically focused.
- Username in
localStorage: A first-visit prompt stores the chosen name under the keybeyond-text:username. A "change" button in the header clears it.