feat(messaging-ui): @mentions — autocomplete, resolve to ids, highlight

- contract: SendOpts.mentions (opaque userId notify-list) + optional
  listMembers(threadId) for autocomplete/highlight (capability-degrading)
- mock: listMembers; KernelClientAdapter.send forwards mentions to the socket
  (which already carries them → inbox MENTION items)
- composer: type @ → member/@channel/@here autocomplete; Enter picks the first;
  on send, mentions resolve to ids; message bodies highlight @mentions
- useMembers hook; mentions.tsx helpers (trailingMentionQuery/insertMention/
  resolveMentions/highlightMentions)
- 4 mention tests (helpers + render autocomplete→send carries id); 56 total green

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-21 20:10:38 +05:30
parent 00a2cc474a
commit cb9a0b9250
10 changed files with 260 additions and 15 deletions
@@ -6,6 +6,7 @@ import type {
Membership,
Message,
MessageEvent,
Person,
SendOpts,
Unsubscribe,
} from './types';
@@ -56,6 +57,10 @@ export interface MessagingAdapter {
/** Absent => treated as always connected (e.g. a pure-REST adapter). */
isConnected?(): boolean;
/** A thread's members (id + display name), for @mention autocomplete + highlighting.
* Absent => the composer offers no autocomplete (you can still type @text). */
listMembers?(threadId: string): Promise<Person[]>;
// ── Channels (optional capability) ──────────────────────────────
// A channel is just a third membership beyond dm/group: a discoverable, joinable room.
// Implement all four to enable the channels UI; absent => the UI hides channels entirely.