Throwaway, kept as a primary source rather than on main. Single self-contained
HTML file: free-play buttons plus six guided walkthroughs over a pure reducer.
It settled two things the spec had wrong or missing:
- The client must supply the message id. With a server-generated id there is
nothing to reconcile the optimistically rendered message against when it
comes back over the realtime channel, and it renders twice. Both cases are
reproducible via the toggle at the top.
- An unread *count* cannot be derived without fetching, because seq counts
events and not messages. Hence: fetch for DMs, a plain dot for channels.
prevLengthRef.current was never zeroed when conversationId changed,
so the first batch of messages in a new (shorter) conversation would
not trigger scrollIntoView. Reset it in the polling useEffect cleanup.
- Finding 1: add AppState::authed_get/authed_post helpers that attach
Bearer token when present; all 4 commands now use them
- Finding 2: MessageInput accepts onSent callback, calls it after
successful send; ChatPage passes refreshKey bump as onSent
- Finding 3: MessageList useEffect uses cancellation flag so stale
in-flight responses cannot overwrite a newly selected conversation
- Finding 4: scrollIntoView moved to separate effect keyed on
messages.length so scroll only fires when new messages arrive
Implement list_workspaces and list_conversations commands to enable
frontend access to workspace and conversation data via Tauri IPC.
- Add workspace.rs with list_workspaces command (GET /workspaces)
- Add conversation.rs with list_conversations command (GET /conversations?workspace_id=)
- Register both commands in lib.rs invoke_handler
- Update commands/mod.rs to expose new modules