feat(messaging-ui): channels — browse, join, leave, create (contract + mock + UI)

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>
This commit is contained in:
2026-07-21 20:04:46 +05:30
parent 3f1f89dfbe
commit 00a2cc474a
14 changed files with 544 additions and 28 deletions
+126
View File
@@ -209,6 +209,132 @@
cursor: not-allowed;
}
/* sidebar sections + channel browser */
.miu-section {
border-bottom: 1px solid var(--miu-border);
}
.miu-section-head {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 14px 4px;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--miu-muted);
}
.miu-section-add {
border: none;
background: transparent;
color: var(--miu-muted);
cursor: pointer;
font-size: 16px;
line-height: 1;
padding: 0 4px;
}
.miu-section-add:hover {
color: var(--miu-text);
}
.miu-empty-sm {
padding: 8px 14px 12px;
text-align: left;
font-size: 12.5px;
}
.miu-browser {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
padding: 16px;
gap: 14px;
overflow-y: auto;
}
.miu-browser-head {
font-size: 16px;
font-weight: 700;
}
.miu-channel-create {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px;
border: 1px solid var(--miu-border);
border-radius: var(--miu-radius);
background: var(--miu-panel);
}
.miu-channel-vis {
display: flex;
gap: 16px;
font-size: 13px;
color: var(--miu-muted);
}
.miu-channel-vis label {
display: inline-flex;
align-items: center;
gap: 5px;
cursor: pointer;
}
.miu-browser-list {
display: flex;
flex-direction: column;
gap: 6px;
}
.miu-browser-row {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 12px;
border: 1px solid var(--miu-border);
border-radius: var(--miu-radius);
background: var(--miu-panel);
}
.miu-channel-glyph {
width: 30px;
height: 30px;
flex-shrink: 0;
border-radius: 8px;
display: grid;
place-items: center;
font-weight: 700;
color: var(--miu-muted);
background: var(--miu-panel-2);
}
.miu-browser-main {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 1px;
}
.miu-browser-name {
font-weight: 600;
}
.miu-browser-topic {
font-size: 12.5px;
color: var(--miu-muted);
}
.miu-browser-meta {
font-size: 11.5px;
color: var(--miu-muted);
}
.miu-join {
flex-shrink: 0;
padding: 5px 14px;
border-radius: 8px;
border: none;
font-weight: 700;
cursor: pointer;
color: var(--miu-accent-text);
background: var(--miu-accent);
}
.miu-browser-joined {
flex-shrink: 0;
font-size: 12px;
color: var(--miu-muted);
}
/* misc */
.miu-empty {
padding: 24px;