Chat

A realtime chat app. Messages are stored in Val Town SQLite and delivered to clients via Server-Sent Events over a single long-lived HTTP connection per client. Optimized for mobile + desktop.

Architecture

Rendering mermaid diagram...

How realtime works

  • Client opens one SSE connection to GET /api/stream?sinceId=N.
  • Server polls SQLite every ~1.5s for rows with id > lastSent and pushes each new row as an SSE message event (with the row id as the event id).
  • Server closes the stream after 5 minutes; the browser's EventSource automatically reconnects and resumes from Last-Event-ID. This keeps individual isolates from piling up while preserving delivery semantics.
  • Heartbeat comments are sent every 20s so intermediaries don't drop idle connections.
  • POSTing a message only inserts into SQLite — the SSE stream delivers it, so there's a single source of truth and no dedup logic needed on the client.

File structure

index.ts                          ← Hono server + /api routes + SSE stream
database/db.ts                    ← SQLite schema + queries
shared/types.ts                   ← Message type shared by client & server
frontend/
  index.html                      ← Shell (full-height, viewport-fit=cover for mobile)
  index.tsx                       ← React entrypoint
  components/
    App.tsx                       ← Layout + status dot
    MessageList.tsx               ← Renders messages (no auto-scroll)
    MessageInput.tsx              ← Input + Send (no auto-focus)
  hooks/
    useMessages.ts                ← Initial load + SSE subscription + send

Design choices

  • No auto-scroll, no auto-focus on the input — per spec.
  • Mobile friendly: 100dvh layout, font-size: 16px on input to prevent iOS zoom-on-focus, overscroll-behavior: none on body.
  • Rounded pill send button with disabled state when the input is empty.
  • Messages never delete; messages_v1 table is append-only.