Fix web session UX: restore on reload, stop, smart scroll #1

Merged
kubus merged 2 commits from t3code/hat-web-ux-fixes into main 2026-09-28 22:06:05 +00:00
Owner

Summary

  • Restore the previously open session on reload from localStorage (hat.session), showing a "Restoring conversation…" state; a 404 clears the stale pointer while 401/transient failures keep it for retry.
  • Add a Stop control (button and Esc) that aborts the in-flight turn via an AbortController, which the server turns into a real abort instead of only hiding the stream.
  • Keep the view pinned to the bottom only while already near it; scroll instantly while tokens stream, smoothly for new messages.
  • Extract SSE framing into a pure, tested SseFrameParser (keepalive comments, CRLF, split frames, mid-character UTF-8, mid-frame flush).
  • Replace the native confirm() delete with in-place confirmation, add missing aria-labels, and wrap the app in an ErrorBoundary.

Testing

  • pnpm typecheck
  • pnpm test — new apps/web/src/sse.test.ts covers frame splitting at every offset, CRLF terminators, keepalive comments, flush semantics, and a decoder flush completing a multibyte character.
  • Manual: reload mid-conversation lands back in the session; Stop/Esc aborts a running turn; scrolling up during streaming is not yanked down; delete confirms inline; a render error falls back to the boundary.
## Summary - Restore the previously open session on reload from `localStorage` (`hat.session`), showing a "Restoring conversation…" state; a 404 clears the stale pointer while 401/transient failures keep it for retry. - Add a Stop control (button and `Esc`) that aborts the in-flight turn via an `AbortController`, which the server turns into a real abort instead of only hiding the stream. - Keep the view pinned to the bottom only while already near it; scroll instantly while tokens stream, smoothly for new messages. - Extract SSE framing into a pure, tested `SseFrameParser` (keepalive comments, CRLF, split frames, mid-character UTF-8, mid-frame flush). - Replace the native `confirm()` delete with in-place confirmation, add missing `aria-label`s, and wrap the app in an `ErrorBoundary`. ## Testing - `pnpm typecheck` - `pnpm test` — new `apps/web/src/sse.test.ts` covers frame splitting at every offset, CRLF terminators, keepalive comments, flush semantics, and a decoder flush completing a multibyte character. - Manual: reload mid-conversation lands back in the session; Stop/`Esc` aborts a running turn; scrolling up during streaming is not yanked down; delete confirms inline; a render error falls back to the boundary.
- Persist the open session in localStorage and reopen it on boot, with in-place delete confirmation
- Turn the send button into Stop (or Esc) that aborts the turn server-side
- Autoscroll only while the user is at the bottom, instantly while streaming
- Extract the SSE frame parser into sse.ts and cover it with node:test
- Add an error boundary so a render throw is not a blank page
# Conflicts:
#	apps/web/src/App.tsx
#	apps/web/src/Sidebar.tsx
kubus merged commit b177fdff89 into main 2026-09-28 22:06:05 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kubus/hatchat!1
No description provided.