- useMessages exposes upload(); Thread gains a hover reaction picker (react to
a message) and an attach button (upload → stage → send), plus inline image /
file rendering of message attachments — all capability-degrading (hidden when
the adapter lacks react/upload)
- themeable styles for the picker, attach button, staged chip, and attachments
- 57 tests still green
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a third membership beyond dm/group: a discoverable, joinable room.
- contract: Membership += 'channel'; Conversation.topic; ChannelSummary +
CreateChannelInput; optional adapter methods browseChannels/createChannel/
joinChannel/leaveChannel (capability-degrading — absent hides the UI)
- MockAdapter implements them (seeds a joined public, a joinable public, and a
private channel); listConversations now returns only threads I'm in
- useChannels hook (browse/create/join/leave + supported flag)
- UI: sidebar sections (Channels vs Direct messages), a + that opens a
ChannelBrowser (join + create), # / lock glyphs; themeable styles
- KernelClientAdapter passes channel membership + topic through
- 4 channel tests (mock browse/join/create + render browse→join); 52 total green
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>