diff --git a/README.md b/README.md index 22b1ebd..66606e6 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,14 @@ ws/ Minimal WebSocket pub/sub layer: server (single- and multi-user), client, and a keyed message dispatcher (e.g. "Sensor", "Heater", "TempCtrl", "Stirrer", "Pot", "Sud"). + Abrupt client disconnects (no WebSocket close + frame — the normal case for browser tab closes + and Safari session teardowns) are handled + gracefully: `User.send()` catches + `ConnectionClosed` silently, and `handler_recv()` + exits cleanly on it, so a vanishing client never + produces an uncaught exception or "connection + handler failed" log noise. scripts/demos/ Standalone, eyeballed-plot demos (matplotlib) exercising components/* in isolation — pid/, @@ -610,6 +618,17 @@ for the desktop app's native file dialogs), Start/Pause/Stop, and Confirm (a plain `` pops with the step's message on `WAIT_USER` and sends Confirm when dismissed) are all wired up. +The page carries a `` tag so Safari and other mobile +browsers render at device CSS pixels rather than the 980 px virtual layout +they otherwise default to — essential for touch usability on iPad. The +header is a two-row layout: the top row holds the host address input, +Connect button, connection status, and the three large (76 × 76 px) +circular transport buttons (Start/Pause/Stop) with the countdown to their +right; the New/Load/Save file buttons occupy their own row directly below. +The LCD panel (Temperature/Heat rate/Power) uses a four-column grid, with +unit labels (°C, °C/min, W) in a dedicated column to the right of the Soll +value rather than embedded in the row labels. + The browser client applies the "state is server-only" principle strictly throughout `web/app.js`: every slider, readout, and checkbox is updated only when a push arrives from the server, never speculatively on user